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;
3348 player->mouse_action.button_hint = 0;
3350 player->effective_mouse_action.lx = 0;
3351 player->effective_mouse_action.ly = 0;
3352 player->effective_mouse_action.button = 0;
3353 player->effective_mouse_action.button_hint = 0;
3356 player->score_final = 0;
3358 player->health = MAX_HEALTH;
3359 player->health_final = MAX_HEALTH;
3361 player->gems_still_needed = level.gems_needed;
3362 player->sokobanfields_still_needed = 0;
3363 player->lights_still_needed = 0;
3364 player->friends_still_needed = 0;
3366 for (j = 0; j < MAX_NUM_KEYS; j++)
3367 player->key[j] = FALSE;
3369 player->num_white_keys = 0;
3371 player->dynabomb_count = 0;
3372 player->dynabomb_size = 1;
3373 player->dynabombs_left = 0;
3374 player->dynabomb_xl = FALSE;
3376 player->MovDir = initial_move_dir;
3379 player->GfxDir = initial_move_dir;
3380 player->GfxAction = ACTION_DEFAULT;
3382 player->StepFrame = 0;
3384 player->initial_element = player->element_nr;
3385 player->artwork_element =
3386 (level.use_artwork_element[i] ? level.artwork_element[i] :
3387 player->element_nr);
3388 player->use_murphy = FALSE;
3390 player->block_last_field = FALSE; /* initialized in InitPlayerField() */
3391 player->block_delay_adjustment = 0; /* initialized in InitPlayerField() */
3393 player->gravity = level.initial_player_gravity[i];
3395 player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
3397 player->actual_frame_counter = 0;
3399 player->step_counter = 0;
3401 player->last_move_dir = initial_move_dir;
3403 player->is_active = FALSE;
3405 player->is_waiting = FALSE;
3406 player->is_moving = FALSE;
3407 player->is_auto_moving = FALSE;
3408 player->is_digging = FALSE;
3409 player->is_snapping = FALSE;
3410 player->is_collecting = FALSE;
3411 player->is_pushing = FALSE;
3412 player->is_switching = FALSE;
3413 player->is_dropping = FALSE;
3414 player->is_dropping_pressed = FALSE;
3416 player->is_bored = FALSE;
3417 player->is_sleeping = FALSE;
3419 player->was_waiting = TRUE;
3420 player->was_moving = FALSE;
3421 player->was_snapping = FALSE;
3422 player->was_dropping = FALSE;
3424 player->force_dropping = FALSE;
3426 player->frame_counter_bored = -1;
3427 player->frame_counter_sleeping = -1;
3429 player->anim_delay_counter = 0;
3430 player->post_delay_counter = 0;
3432 player->dir_waiting = initial_move_dir;
3433 player->action_waiting = ACTION_DEFAULT;
3434 player->last_action_waiting = ACTION_DEFAULT;
3435 player->special_action_bored = ACTION_DEFAULT;
3436 player->special_action_sleeping = ACTION_DEFAULT;
3438 player->switch_x = -1;
3439 player->switch_y = -1;
3441 player->drop_x = -1;
3442 player->drop_y = -1;
3444 player->show_envelope = 0;
3446 SetPlayerMoveSpeed(player, level.initial_player_stepsize[i], TRUE);
3448 player->push_delay = -1; /* initialized when pushing starts */
3449 player->push_delay_value = game.initial_push_delay_value;
3451 player->drop_delay = 0;
3452 player->drop_pressed_delay = 0;
3454 player->last_jx = -1;
3455 player->last_jy = -1;
3459 player->shield_normal_time_left = 0;
3460 player->shield_deadly_time_left = 0;
3462 player->inventory_infinite_element = EL_UNDEFINED;
3463 player->inventory_size = 0;
3465 if (level.use_initial_inventory[i])
3467 for (j = 0; j < level.initial_inventory_size[i]; j++)
3469 int element = level.initial_inventory_content[i][j];
3470 int collect_count = element_info[element].collect_count_initial;
3473 if (!IS_CUSTOM_ELEMENT(element))
3476 if (collect_count == 0)
3477 player->inventory_infinite_element = element;
3479 for (k = 0; k < collect_count; k++)
3480 if (player->inventory_size < MAX_INVENTORY_SIZE)
3481 player->inventory_element[player->inventory_size++] = element;
3485 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
3486 SnapField(player, 0, 0);
3488 player->LevelSolved = FALSE;
3489 player->GameOver = FALSE;
3491 player->LevelSolved_GameWon = FALSE;
3492 player->LevelSolved_GameEnd = FALSE;
3493 player->LevelSolved_PanelOff = FALSE;
3494 player->LevelSolved_SaveTape = FALSE;
3495 player->LevelSolved_SaveScore = FALSE;
3497 player->LevelSolved_CountingTime = 0;
3498 player->LevelSolved_CountingScore = 0;
3499 player->LevelSolved_CountingHealth = 0;
3501 map_player_action[i] = i;
3504 network_player_action_received = FALSE;
3506 #if defined(NETWORK_AVALIABLE)
3507 /* initial null action */
3508 if (network_playing)
3509 SendToServer_MovePlayer(MV_NONE);
3518 TimeLeft = level.time;
3521 ScreenMovDir = MV_NONE;
3525 ScrollStepSize = 0; /* will be correctly initialized by ScrollScreen() */
3527 AllPlayersGone = FALSE;
3529 game.no_time_limit = (level.time == 0);
3531 game.yamyam_content_nr = 0;
3532 game.robot_wheel_active = FALSE;
3533 game.magic_wall_active = FALSE;
3534 game.magic_wall_time_left = 0;
3535 game.light_time_left = 0;
3536 game.timegate_time_left = 0;
3537 game.switchgate_pos = 0;
3538 game.wind_direction = level.wind_direction_initial;
3540 game.lenses_time_left = 0;
3541 game.magnify_time_left = 0;
3543 game.ball_state = level.ball_state_initial;
3544 game.ball_content_nr = 0;
3546 game.envelope_active = FALSE;
3548 /* set focus to local player for network games, else to all players */
3549 game.centered_player_nr = (network_playing ? local_player->index_nr : -1);
3550 game.centered_player_nr_next = game.centered_player_nr;
3551 game.set_centered_player = FALSE;
3553 if (network_playing && tape.recording)
3555 /* store client dependent player focus when recording network games */
3556 tape.centered_player_nr_next = game.centered_player_nr_next;
3557 tape.set_centered_player = TRUE;
3560 for (i = 0; i < NUM_BELTS; i++)
3562 game.belt_dir[i] = MV_NONE;
3563 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
3566 for (i = 0; i < MAX_NUM_AMOEBA; i++)
3567 AmoebaCnt[i] = AmoebaCnt2[i] = 0;
3569 #if DEBUG_INIT_PLAYER
3572 printf("Player status at level initialization:\n");
3576 SCAN_PLAYFIELD(x, y)
3578 Feld[x][y] = level.field[x][y];
3579 MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
3580 ChangeDelay[x][y] = 0;
3581 ChangePage[x][y] = -1;
3582 CustomValue[x][y] = 0; /* initialized in InitField() */
3583 Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
3585 WasJustMoving[x][y] = 0;
3586 WasJustFalling[x][y] = 0;
3587 CheckCollision[x][y] = 0;
3588 CheckImpact[x][y] = 0;
3590 Pushed[x][y] = FALSE;
3592 ChangeCount[x][y] = 0;
3593 ChangeEvent[x][y] = -1;
3595 ExplodePhase[x][y] = 0;
3596 ExplodeDelay[x][y] = 0;
3597 ExplodeField[x][y] = EX_TYPE_NONE;
3599 RunnerVisit[x][y] = 0;
3600 PlayerVisit[x][y] = 0;
3603 GfxRandom[x][y] = INIT_GFX_RANDOM();
3604 GfxElement[x][y] = EL_UNDEFINED;
3605 GfxAction[x][y] = ACTION_DEFAULT;
3606 GfxDir[x][y] = MV_NONE;
3607 GfxRedraw[x][y] = GFX_REDRAW_NONE;
3610 SCAN_PLAYFIELD(x, y)
3612 if (emulate_bd && !IS_BD_ELEMENT(Feld[x][y]))
3614 if (emulate_sb && !IS_SB_ELEMENT(Feld[x][y]))
3616 if (emulate_sp && !IS_SP_ELEMENT(Feld[x][y]))
3619 InitField(x, y, TRUE);
3621 ResetGfxAnimation(x, y);
3626 for (i = 0; i < MAX_PLAYERS; i++)
3628 struct PlayerInfo *player = &stored_player[i];
3630 /* set number of special actions for bored and sleeping animation */
3631 player->num_special_action_bored =
3632 get_num_special_action(player->artwork_element,
3633 ACTION_BORING_1, ACTION_BORING_LAST);
3634 player->num_special_action_sleeping =
3635 get_num_special_action(player->artwork_element,
3636 ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
3639 game.emulation = (emulate_bd ? EMU_BOULDERDASH :
3640 emulate_sb ? EMU_SOKOBAN :
3641 emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
3643 /* initialize type of slippery elements */
3644 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3646 if (!IS_CUSTOM_ELEMENT(i))
3648 /* default: elements slip down either to the left or right randomly */
3649 element_info[i].slippery_type = SLIPPERY_ANY_RANDOM;
3651 /* SP style elements prefer to slip down on the left side */
3652 if (game.engine_version >= VERSION_IDENT(3,1,1,0) && IS_SP_ELEMENT(i))
3653 element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3655 /* BD style elements prefer to slip down on the left side */
3656 if (game.emulation == EMU_BOULDERDASH)
3657 element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3661 /* initialize explosion and ignition delay */
3662 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3664 if (!IS_CUSTOM_ELEMENT(i))
3667 int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
3668 game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
3669 game.emulation == EMU_SUPAPLEX ? 3 : 2);
3670 int last_phase = (num_phase + 1) * delay;
3671 int half_phase = (num_phase / 2) * delay;
3673 element_info[i].explosion_delay = last_phase - 1;
3674 element_info[i].ignition_delay = half_phase;
3676 if (i == EL_BLACK_ORB)
3677 element_info[i].ignition_delay = 1;
3681 /* correct non-moving belts to start moving left */
3682 for (i = 0; i < NUM_BELTS; i++)
3683 if (game.belt_dir[i] == MV_NONE)
3684 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
3686 #if USE_NEW_PLAYER_ASSIGNMENTS
3687 /* !!! SAME AS init.c:InitPlayerInfo() -- FIX THIS !!! */
3688 /* choose default local player */
3689 local_player = &stored_player[0];
3691 for (i = 0; i < MAX_PLAYERS; i++)
3692 stored_player[i].connected = FALSE;
3694 local_player->connected = TRUE;
3695 /* !!! SAME AS init.c:InitPlayerInfo() -- FIX THIS !!! */
3699 for (i = 0; i < MAX_PLAYERS; i++)
3700 stored_player[i].connected = tape.player_participates[i];
3702 else if (game.team_mode && !options.network)
3704 /* try to guess locally connected team mode players (needed for correct
3705 assignment of player figures from level to locally playing players) */
3707 for (i = 0; i < MAX_PLAYERS; i++)
3708 if (setup.input[i].use_joystick ||
3709 setup.input[i].key.left != KSYM_UNDEFINED)
3710 stored_player[i].connected = TRUE;
3713 #if DEBUG_INIT_PLAYER
3716 printf("Player status after level initialization:\n");
3718 for (i = 0; i < MAX_PLAYERS; i++)
3720 struct PlayerInfo *player = &stored_player[i];
3722 printf("- player %d: present == %d, connected == %d, active == %d",
3728 if (local_player == player)
3729 printf(" (local player)");
3736 #if DEBUG_INIT_PLAYER
3738 printf("Reassigning players ...\n");
3741 /* check if any connected player was not found in playfield */
3742 for (i = 0; i < MAX_PLAYERS; i++)
3744 struct PlayerInfo *player = &stored_player[i];
3746 if (player->connected && !player->present)
3748 struct PlayerInfo *field_player = NULL;
3750 #if DEBUG_INIT_PLAYER
3752 printf("- looking for field player for player %d ...\n", i + 1);
3755 /* assign first free player found that is present in the playfield */
3757 /* first try: look for unmapped playfield player that is not connected */
3758 for (j = 0; j < MAX_PLAYERS; j++)
3759 if (field_player == NULL &&
3760 stored_player[j].present &&
3761 !stored_player[j].mapped &&
3762 !stored_player[j].connected)
3763 field_player = &stored_player[j];
3765 /* second try: look for *any* unmapped playfield player */
3766 for (j = 0; j < MAX_PLAYERS; j++)
3767 if (field_player == NULL &&
3768 stored_player[j].present &&
3769 !stored_player[j].mapped)
3770 field_player = &stored_player[j];
3772 if (field_player != NULL)
3774 int jx = field_player->jx, jy = field_player->jy;
3776 #if DEBUG_INIT_PLAYER
3778 printf("- found player %d\n", field_player->index_nr + 1);
3781 player->present = FALSE;
3782 player->active = FALSE;
3784 field_player->present = TRUE;
3785 field_player->active = TRUE;
3788 player->initial_element = field_player->initial_element;
3789 player->artwork_element = field_player->artwork_element;
3791 player->block_last_field = field_player->block_last_field;
3792 player->block_delay_adjustment = field_player->block_delay_adjustment;
3795 StorePlayer[jx][jy] = field_player->element_nr;
3797 field_player->jx = field_player->last_jx = jx;
3798 field_player->jy = field_player->last_jy = jy;
3800 if (local_player == player)
3801 local_player = field_player;
3803 map_player_action[field_player->index_nr] = i;
3805 field_player->mapped = TRUE;
3807 #if DEBUG_INIT_PLAYER
3809 printf("- map_player_action[%d] == %d\n",
3810 field_player->index_nr + 1, i + 1);
3815 if (player->connected && player->present)
3816 player->mapped = TRUE;
3819 #if DEBUG_INIT_PLAYER
3822 printf("Player status after player assignment (first stage):\n");
3824 for (i = 0; i < MAX_PLAYERS; i++)
3826 struct PlayerInfo *player = &stored_player[i];
3828 printf("- player %d: present == %d, connected == %d, active == %d",
3834 if (local_player == player)
3835 printf(" (local player)");
3844 /* check if any connected player was not found in playfield */
3845 for (i = 0; i < MAX_PLAYERS; i++)
3847 struct PlayerInfo *player = &stored_player[i];
3849 if (player->connected && !player->present)
3851 for (j = 0; j < MAX_PLAYERS; j++)
3853 struct PlayerInfo *field_player = &stored_player[j];
3854 int jx = field_player->jx, jy = field_player->jy;
3856 /* assign first free player found that is present in the playfield */
3857 if (field_player->present && !field_player->connected)
3859 player->present = TRUE;
3860 player->active = TRUE;
3862 field_player->present = FALSE;
3863 field_player->active = FALSE;
3865 player->initial_element = field_player->initial_element;
3866 player->artwork_element = field_player->artwork_element;
3868 player->block_last_field = field_player->block_last_field;
3869 player->block_delay_adjustment = field_player->block_delay_adjustment;
3871 StorePlayer[jx][jy] = player->element_nr;
3873 player->jx = player->last_jx = jx;
3874 player->jy = player->last_jy = jy;
3884 printf("::: local_player->present == %d\n", local_player->present);
3889 /* when playing a tape, eliminate all players who do not participate */
3891 #if USE_NEW_PLAYER_ASSIGNMENTS
3893 if (!game.team_mode)
3895 for (i = 0; i < MAX_PLAYERS; i++)
3897 if (stored_player[i].active &&
3898 !tape.player_participates[map_player_action[i]])
3900 struct PlayerInfo *player = &stored_player[i];
3901 int jx = player->jx, jy = player->jy;
3903 #if DEBUG_INIT_PLAYER
3905 printf("Removing player %d at (%d, %d)\n", i + 1, jx, jy);
3908 player->active = FALSE;
3909 StorePlayer[jx][jy] = 0;
3910 Feld[jx][jy] = EL_EMPTY;
3917 for (i = 0; i < MAX_PLAYERS; i++)
3919 if (stored_player[i].active &&
3920 !tape.player_participates[i])
3922 struct PlayerInfo *player = &stored_player[i];
3923 int jx = player->jx, jy = player->jy;
3925 player->active = FALSE;
3926 StorePlayer[jx][jy] = 0;
3927 Feld[jx][jy] = EL_EMPTY;
3932 else if (!options.network && !game.team_mode) /* && !tape.playing */
3934 /* when in single player mode, eliminate all but the first active player */
3936 for (i = 0; i < MAX_PLAYERS; i++)
3938 if (stored_player[i].active)
3940 for (j = i + 1; j < MAX_PLAYERS; j++)
3942 if (stored_player[j].active)
3944 struct PlayerInfo *player = &stored_player[j];
3945 int jx = player->jx, jy = player->jy;
3947 player->active = FALSE;
3948 player->present = FALSE;
3950 StorePlayer[jx][jy] = 0;
3951 Feld[jx][jy] = EL_EMPTY;
3958 /* when recording the game, store which players take part in the game */
3961 #if USE_NEW_PLAYER_ASSIGNMENTS
3962 for (i = 0; i < MAX_PLAYERS; i++)
3963 if (stored_player[i].connected)
3964 tape.player_participates[i] = TRUE;
3966 for (i = 0; i < MAX_PLAYERS; i++)
3967 if (stored_player[i].active)
3968 tape.player_participates[i] = TRUE;
3972 #if DEBUG_INIT_PLAYER
3975 printf("Player status after player assignment (final stage):\n");
3977 for (i = 0; i < MAX_PLAYERS; i++)
3979 struct PlayerInfo *player = &stored_player[i];
3981 printf("- player %d: present == %d, connected == %d, active == %d",
3987 if (local_player == player)
3988 printf(" (local player)");
3995 if (BorderElement == EL_EMPTY)
3998 SBX_Right = lev_fieldx - SCR_FIELDX;
4000 SBY_Lower = lev_fieldy - SCR_FIELDY;
4005 SBX_Right = lev_fieldx - SCR_FIELDX + 1;
4007 SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
4010 if (full_lev_fieldx <= SCR_FIELDX)
4011 SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
4012 if (full_lev_fieldy <= SCR_FIELDY)
4013 SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
4015 if (EVEN(SCR_FIELDX) && full_lev_fieldx > SCR_FIELDX)
4017 if (EVEN(SCR_FIELDY) && full_lev_fieldy > SCR_FIELDY)
4020 /* if local player not found, look for custom element that might create
4021 the player (make some assumptions about the right custom element) */
4022 if (!local_player->present)
4024 int start_x = 0, start_y = 0;
4025 int found_rating = 0;
4026 int found_element = EL_UNDEFINED;
4027 int player_nr = local_player->index_nr;
4029 SCAN_PLAYFIELD(x, y)
4031 int element = Feld[x][y];
4036 if (level.use_start_element[player_nr] &&
4037 level.start_element[player_nr] == element &&
4044 found_element = element;
4047 if (!IS_CUSTOM_ELEMENT(element))
4050 if (CAN_CHANGE(element))
4052 for (i = 0; i < element_info[element].num_change_pages; i++)
4054 /* check for player created from custom element as single target */
4055 content = element_info[element].change_page[i].target_element;
4056 is_player = ELEM_IS_PLAYER(content);
4058 if (is_player && (found_rating < 3 ||
4059 (found_rating == 3 && element < found_element)))
4065 found_element = element;
4070 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
4072 /* check for player created from custom element as explosion content */
4073 content = element_info[element].content.e[xx][yy];
4074 is_player = ELEM_IS_PLAYER(content);
4076 if (is_player && (found_rating < 2 ||
4077 (found_rating == 2 && element < found_element)))
4079 start_x = x + xx - 1;
4080 start_y = y + yy - 1;
4083 found_element = element;
4086 if (!CAN_CHANGE(element))
4089 for (i = 0; i < element_info[element].num_change_pages; i++)
4091 /* check for player created from custom element as extended target */
4093 element_info[element].change_page[i].target_content.e[xx][yy];
4095 is_player = ELEM_IS_PLAYER(content);
4097 if (is_player && (found_rating < 1 ||
4098 (found_rating == 1 && element < found_element)))
4100 start_x = x + xx - 1;
4101 start_y = y + yy - 1;
4104 found_element = element;
4110 scroll_x = SCROLL_POSITION_X(start_x);
4111 scroll_y = SCROLL_POSITION_Y(start_y);
4115 scroll_x = SCROLL_POSITION_X(local_player->jx);
4116 scroll_y = SCROLL_POSITION_Y(local_player->jy);
4119 /* !!! FIX THIS (START) !!! */
4120 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
4122 InitGameEngine_EM();
4124 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
4126 InitGameEngine_SP();
4128 else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4130 InitGameEngine_MM();
4134 DrawLevel(REDRAW_FIELD);
4137 /* after drawing the level, correct some elements */
4138 if (game.timegate_time_left == 0)
4139 CloseAllOpenTimegates();
4142 /* blit playfield from scroll buffer to normal back buffer for fading in */
4143 BlitScreenToBitmap(backbuffer);
4144 /* !!! FIX THIS (END) !!! */
4146 DrawMaskedBorder(fade_mask);
4151 // full screen redraw is required at this point in the following cases:
4152 // - special editor door undrawn when game was started from level editor
4153 // - drawing area (playfield) was changed and has to be removed completely
4154 redraw_mask = REDRAW_ALL;
4158 if (!game.restart_level)
4160 /* copy default game door content to main double buffer */
4162 /* !!! CHECK AGAIN !!! */
4163 SetPanelBackground();
4164 // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
4165 DrawBackground(DX, DY, DXSIZE, DYSIZE);
4168 SetPanelBackground();
4169 SetDrawBackgroundMask(REDRAW_DOOR_1);
4171 UpdateAndDisplayGameControlValues();
4173 if (!game.restart_level)
4179 CreateGameButtons();
4184 /* copy actual game door content to door double buffer for OpenDoor() */
4185 BlitBitmap(drawto, bitmap_db_door_1, DX, DY, DXSIZE, DYSIZE, 0, 0);
4187 OpenDoor(DOOR_OPEN_ALL);
4189 KeyboardAutoRepeatOffUnlessAutoplay();
4191 #if DEBUG_INIT_PLAYER
4194 printf("Player status (final):\n");
4196 for (i = 0; i < MAX_PLAYERS; i++)
4198 struct PlayerInfo *player = &stored_player[i];
4200 printf("- player %d: present == %d, connected == %d, active == %d",
4206 if (local_player == player)
4207 printf(" (local player)");
4220 if (!game.restart_level && !tape.playing)
4222 LevelStats_incPlayed(level_nr);
4224 SaveLevelSetup_SeriesInfo();
4227 game.restart_level = FALSE;
4228 game.restart_game_message = NULL;
4230 if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4231 InitGameActions_MM();
4233 SaveEngineSnapshotToListInitial();
4235 if (!game.restart_level)
4237 PlaySound(SND_GAME_STARTING);
4239 if (setup.sound_music)
4244 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y,
4245 int actual_player_x, int actual_player_y)
4247 /* this is used for non-R'n'D game engines to update certain engine values */
4249 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
4251 actual_player_x = correctLevelPosX_EM(actual_player_x);
4252 actual_player_y = correctLevelPosY_EM(actual_player_y);
4255 /* needed to determine if sounds are played within the visible screen area */
4256 scroll_x = actual_scroll_x;
4257 scroll_y = actual_scroll_y;
4259 /* needed to get player position for "follow finger" playing input method */
4260 local_player->jx = actual_player_x;
4261 local_player->jy = actual_player_y;
4264 void InitMovDir(int x, int y)
4266 int i, element = Feld[x][y];
4267 static int xy[4][2] =
4274 static int direction[3][4] =
4276 { MV_RIGHT, MV_UP, MV_LEFT, MV_DOWN },
4277 { MV_LEFT, MV_DOWN, MV_RIGHT, MV_UP },
4278 { MV_LEFT, MV_RIGHT, MV_UP, MV_DOWN }
4287 Feld[x][y] = EL_BUG;
4288 MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
4291 case EL_SPACESHIP_RIGHT:
4292 case EL_SPACESHIP_UP:
4293 case EL_SPACESHIP_LEFT:
4294 case EL_SPACESHIP_DOWN:
4295 Feld[x][y] = EL_SPACESHIP;
4296 MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
4299 case EL_BD_BUTTERFLY_RIGHT:
4300 case EL_BD_BUTTERFLY_UP:
4301 case EL_BD_BUTTERFLY_LEFT:
4302 case EL_BD_BUTTERFLY_DOWN:
4303 Feld[x][y] = EL_BD_BUTTERFLY;
4304 MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
4307 case EL_BD_FIREFLY_RIGHT:
4308 case EL_BD_FIREFLY_UP:
4309 case EL_BD_FIREFLY_LEFT:
4310 case EL_BD_FIREFLY_DOWN:
4311 Feld[x][y] = EL_BD_FIREFLY;
4312 MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
4315 case EL_PACMAN_RIGHT:
4317 case EL_PACMAN_LEFT:
4318 case EL_PACMAN_DOWN:
4319 Feld[x][y] = EL_PACMAN;
4320 MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
4323 case EL_YAMYAM_LEFT:
4324 case EL_YAMYAM_RIGHT:
4326 case EL_YAMYAM_DOWN:
4327 Feld[x][y] = EL_YAMYAM;
4328 MovDir[x][y] = direction[2][element - EL_YAMYAM_LEFT];
4331 case EL_SP_SNIKSNAK:
4332 MovDir[x][y] = MV_UP;
4335 case EL_SP_ELECTRON:
4336 MovDir[x][y] = MV_LEFT;
4343 Feld[x][y] = EL_MOLE;
4344 MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
4348 if (IS_CUSTOM_ELEMENT(element))
4350 struct ElementInfo *ei = &element_info[element];
4351 int move_direction_initial = ei->move_direction_initial;
4352 int move_pattern = ei->move_pattern;
4354 if (move_direction_initial == MV_START_PREVIOUS)
4356 if (MovDir[x][y] != MV_NONE)
4359 move_direction_initial = MV_START_AUTOMATIC;
4362 if (move_direction_initial == MV_START_RANDOM)
4363 MovDir[x][y] = 1 << RND(4);
4364 else if (move_direction_initial & MV_ANY_DIRECTION)
4365 MovDir[x][y] = move_direction_initial;
4366 else if (move_pattern == MV_ALL_DIRECTIONS ||
4367 move_pattern == MV_TURNING_LEFT ||
4368 move_pattern == MV_TURNING_RIGHT ||
4369 move_pattern == MV_TURNING_LEFT_RIGHT ||
4370 move_pattern == MV_TURNING_RIGHT_LEFT ||
4371 move_pattern == MV_TURNING_RANDOM)
4372 MovDir[x][y] = 1 << RND(4);
4373 else if (move_pattern == MV_HORIZONTAL)
4374 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
4375 else if (move_pattern == MV_VERTICAL)
4376 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
4377 else if (move_pattern & MV_ANY_DIRECTION)
4378 MovDir[x][y] = element_info[element].move_pattern;
4379 else if (move_pattern == MV_ALONG_LEFT_SIDE ||
4380 move_pattern == MV_ALONG_RIGHT_SIDE)
4382 /* use random direction as default start direction */
4383 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
4384 MovDir[x][y] = 1 << RND(4);
4386 for (i = 0; i < NUM_DIRECTIONS; i++)
4388 int x1 = x + xy[i][0];
4389 int y1 = y + xy[i][1];
4391 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4393 if (move_pattern == MV_ALONG_RIGHT_SIDE)
4394 MovDir[x][y] = direction[0][i];
4396 MovDir[x][y] = direction[1][i];
4405 MovDir[x][y] = 1 << RND(4);
4407 if (element != EL_BUG &&
4408 element != EL_SPACESHIP &&
4409 element != EL_BD_BUTTERFLY &&
4410 element != EL_BD_FIREFLY)
4413 for (i = 0; i < NUM_DIRECTIONS; i++)
4415 int x1 = x + xy[i][0];
4416 int y1 = y + xy[i][1];
4418 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4420 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
4422 MovDir[x][y] = direction[0][i];
4425 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
4426 element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4428 MovDir[x][y] = direction[1][i];
4437 GfxDir[x][y] = MovDir[x][y];
4440 void InitAmoebaNr(int x, int y)
4443 int group_nr = AmoebeNachbarNr(x, y);
4447 for (i = 1; i < MAX_NUM_AMOEBA; i++)
4449 if (AmoebaCnt[i] == 0)
4457 AmoebaNr[x][y] = group_nr;
4458 AmoebaCnt[group_nr]++;
4459 AmoebaCnt2[group_nr]++;
4462 static void PlayerWins(struct PlayerInfo *player)
4464 player->LevelSolved = TRUE;
4465 player->GameOver = TRUE;
4467 player->score_final = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
4468 level.native_em_level->lev->score :
4469 level.game_engine_type == GAME_ENGINE_TYPE_MM ?
4472 player->health_final = (level.game_engine_type == GAME_ENGINE_TYPE_MM ?
4473 MM_HEALTH(game_mm.laser_overload_value) :
4476 player->LevelSolved_CountingTime = (game.no_time_limit ? TimePlayed :
4478 player->LevelSolved_CountingScore = player->score_final;
4479 player->LevelSolved_CountingHealth = player->health_final;
4484 static int time_count_steps;
4485 static int time, time_final;
4486 static int score, score_final;
4487 static int health, health_final;
4488 static int game_over_delay_1 = 0;
4489 static int game_over_delay_2 = 0;
4490 static int game_over_delay_3 = 0;
4491 int game_over_delay_value_1 = 50;
4492 int game_over_delay_value_2 = 25;
4493 int game_over_delay_value_3 = 50;
4495 if (!local_player->LevelSolved_GameWon)
4499 /* do not start end game actions before the player stops moving (to exit) */
4500 if (local_player->MovPos)
4503 local_player->LevelSolved_GameWon = TRUE;
4504 local_player->LevelSolved_SaveTape = tape.recording;
4505 local_player->LevelSolved_SaveScore = !tape.playing;
4509 LevelStats_incSolved(level_nr);
4511 SaveLevelSetup_SeriesInfo();
4514 if (tape.auto_play) /* tape might already be stopped here */
4515 tape.auto_play_level_solved = TRUE;
4519 game_over_delay_1 = 0;
4520 game_over_delay_2 = 0;
4521 game_over_delay_3 = game_over_delay_value_3;
4523 time = time_final = (game.no_time_limit ? TimePlayed : TimeLeft);
4524 score = score_final = local_player->score_final;
4525 health = health_final = local_player->health_final;
4527 if (level.score[SC_TIME_BONUS] > 0)
4532 score_final += TimeLeft * level.score[SC_TIME_BONUS];
4534 else if (game.no_time_limit && TimePlayed < 999)
4537 score_final += (999 - TimePlayed) * level.score[SC_TIME_BONUS];
4540 time_count_steps = MAX(1, ABS(time_final - time) / 100);
4542 game_over_delay_1 = game_over_delay_value_1;
4544 if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4547 score_final += health * level.score[SC_TIME_BONUS];
4549 game_over_delay_2 = game_over_delay_value_2;
4552 local_player->score_final = score_final;
4553 local_player->health_final = health_final;
4556 if (level_editor_test_game)
4559 score = score_final;
4561 local_player->LevelSolved_CountingTime = time;
4562 local_player->LevelSolved_CountingScore = score;
4564 game_panel_controls[GAME_PANEL_TIME].value = time;
4565 game_panel_controls[GAME_PANEL_SCORE].value = score;
4567 DisplayGameControlValues();
4570 if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
4572 if (ExitX >= 0 && ExitY >= 0) /* local player has left the level */
4574 /* close exit door after last player */
4575 if ((AllPlayersGone &&
4576 (Feld[ExitX][ExitY] == EL_EXIT_OPEN ||
4577 Feld[ExitX][ExitY] == EL_SP_EXIT_OPEN ||
4578 Feld[ExitX][ExitY] == EL_STEEL_EXIT_OPEN)) ||
4579 Feld[ExitX][ExitY] == EL_EM_EXIT_OPEN ||
4580 Feld[ExitX][ExitY] == EL_EM_STEEL_EXIT_OPEN)
4582 int element = Feld[ExitX][ExitY];
4584 Feld[ExitX][ExitY] =
4585 (element == EL_EXIT_OPEN ? EL_EXIT_CLOSING :
4586 element == EL_EM_EXIT_OPEN ? EL_EM_EXIT_CLOSING :
4587 element == EL_SP_EXIT_OPEN ? EL_SP_EXIT_CLOSING:
4588 element == EL_STEEL_EXIT_OPEN ? EL_STEEL_EXIT_CLOSING:
4589 EL_EM_STEEL_EXIT_CLOSING);
4591 PlayLevelSoundElementAction(ExitX, ExitY, element, ACTION_CLOSING);
4594 /* player disappears */
4595 DrawLevelField(ExitX, ExitY);
4598 for (i = 0; i < MAX_PLAYERS; i++)
4600 struct PlayerInfo *player = &stored_player[i];
4602 if (player->present)
4604 RemovePlayer(player);
4606 /* player disappears */
4607 DrawLevelField(player->jx, player->jy);
4612 PlaySound(SND_GAME_WINNING);
4615 if (game_over_delay_1 > 0)
4617 game_over_delay_1--;
4622 if (time != time_final)
4624 int time_to_go = ABS(time_final - time);
4625 int time_count_dir = (time < time_final ? +1 : -1);
4627 if (time_to_go < time_count_steps)
4628 time_count_steps = 1;
4630 time += time_count_steps * time_count_dir;
4631 score += time_count_steps * level.score[SC_TIME_BONUS];
4633 local_player->LevelSolved_CountingTime = time;
4634 local_player->LevelSolved_CountingScore = score;
4636 game_panel_controls[GAME_PANEL_TIME].value = time;
4637 game_panel_controls[GAME_PANEL_SCORE].value = score;
4639 DisplayGameControlValues();
4641 if (time == time_final)
4642 StopSound(SND_GAME_LEVELTIME_BONUS);
4643 else if (setup.sound_loops)
4644 PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
4646 PlaySound(SND_GAME_LEVELTIME_BONUS);
4651 if (game_over_delay_2 > 0)
4653 game_over_delay_2--;
4658 if (health != health_final)
4660 int health_count_dir = (health < health_final ? +1 : -1);
4662 health += health_count_dir;
4663 score += level.score[SC_TIME_BONUS];
4665 local_player->LevelSolved_CountingHealth = health;
4666 local_player->LevelSolved_CountingScore = score;
4668 game_panel_controls[GAME_PANEL_HEALTH].value = health;
4669 game_panel_controls[GAME_PANEL_SCORE].value = score;
4671 DisplayGameControlValues();
4673 if (health == health_final)
4674 StopSound(SND_GAME_LEVELTIME_BONUS);
4675 else if (setup.sound_loops)
4676 PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
4678 PlaySound(SND_GAME_LEVELTIME_BONUS);
4683 local_player->LevelSolved_PanelOff = TRUE;
4685 if (game_over_delay_3 > 0)
4687 game_over_delay_3--;
4698 boolean raise_level = FALSE;
4700 local_player->LevelSolved_GameEnd = TRUE;
4702 if (local_player->LevelSolved_SaveTape)
4704 /* make sure that request dialog to save tape does not open door again */
4705 if (!global.use_envelope_request)
4706 CloseDoor(DOOR_CLOSE_1);
4708 SaveTapeChecked_LevelSolved(tape.level_nr); /* ask to save tape */
4711 /* if no tape is to be saved, close both doors simultaneously */
4712 CloseDoor(DOOR_CLOSE_ALL);
4714 if (level_editor_test_game)
4716 SetGameStatus(GAME_MODE_MAIN);
4723 if (!local_player->LevelSolved_SaveScore)
4725 SetGameStatus(GAME_MODE_MAIN);
4732 if (level_nr == leveldir_current->handicap_level)
4734 leveldir_current->handicap_level++;
4736 SaveLevelSetup_SeriesInfo();
4739 if (setup.increment_levels &&
4740 level_nr < leveldir_current->last_level)
4741 raise_level = TRUE; /* advance to next level */
4743 if ((hi_pos = NewHiScore()) >= 0)
4745 SetGameStatus(GAME_MODE_SCORES);
4747 DrawHallOfFame(hi_pos);
4757 SetGameStatus(GAME_MODE_MAIN);
4773 boolean one_score_entry_per_name = !program.many_scores_per_name;
4775 LoadScore(level_nr);
4777 if (strEqual(setup.player_name, EMPTY_PLAYER_NAME) ||
4778 local_player->score_final < highscore[MAX_SCORE_ENTRIES - 1].Score)
4781 for (k = 0; k < MAX_SCORE_ENTRIES; k++)
4783 if (local_player->score_final > highscore[k].Score)
4785 /* player has made it to the hall of fame */
4787 if (k < MAX_SCORE_ENTRIES - 1)
4789 int m = MAX_SCORE_ENTRIES - 1;
4791 if (one_score_entry_per_name)
4793 for (l = k; l < MAX_SCORE_ENTRIES; l++)
4794 if (strEqual(setup.player_name, highscore[l].Name))
4797 if (m == k) /* player's new highscore overwrites his old one */
4801 for (l = m; l > k; l--)
4803 strcpy(highscore[l].Name, highscore[l - 1].Name);
4804 highscore[l].Score = highscore[l - 1].Score;
4810 strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
4811 highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
4812 highscore[k].Score = local_player->score_final;
4817 else if (one_score_entry_per_name &&
4818 !strncmp(setup.player_name, highscore[k].Name,
4819 MAX_PLAYER_NAME_LEN))
4820 break; /* player already there with a higher score */
4824 SaveScore(level_nr);
4829 inline static int getElementMoveStepsizeExt(int x, int y, int direction)
4831 int element = Feld[x][y];
4832 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
4833 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
4834 int horiz_move = (dx != 0);
4835 int sign = (horiz_move ? dx : dy);
4836 int step = sign * element_info[element].move_stepsize;
4838 /* special values for move stepsize for spring and things on conveyor belt */
4841 if (CAN_FALL(element) &&
4842 y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
4843 step = sign * MOVE_STEPSIZE_NORMAL / 2;
4844 else if (element == EL_SPRING)
4845 step = sign * MOVE_STEPSIZE_NORMAL * 2;
4851 inline static int getElementMoveStepsize(int x, int y)
4853 return getElementMoveStepsizeExt(x, y, MovDir[x][y]);
4856 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
4858 if (player->GfxAction != action || player->GfxDir != dir)
4860 player->GfxAction = action;
4861 player->GfxDir = dir;
4863 player->StepFrame = 0;
4867 static void ResetGfxFrame(int x, int y)
4869 // profiling showed that "autotest" spends 10~20% of its time in this function
4870 if (DrawingDeactivatedField())
4873 int element = Feld[x][y];
4874 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
4876 if (graphic_info[graphic].anim_global_sync)
4877 GfxFrame[x][y] = FrameCounter;
4878 else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
4879 GfxFrame[x][y] = CustomValue[x][y];
4880 else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
4881 GfxFrame[x][y] = element_info[element].collect_score;
4882 else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
4883 GfxFrame[x][y] = ChangeDelay[x][y];
4886 static void ResetGfxAnimation(int x, int y)
4888 GfxAction[x][y] = ACTION_DEFAULT;
4889 GfxDir[x][y] = MovDir[x][y];
4892 ResetGfxFrame(x, y);
4895 static void ResetRandomAnimationValue(int x, int y)
4897 GfxRandom[x][y] = INIT_GFX_RANDOM();
4900 void InitMovingField(int x, int y, int direction)
4902 int element = Feld[x][y];
4903 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
4904 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
4907 boolean is_moving_before, is_moving_after;
4909 /* check if element was/is moving or being moved before/after mode change */
4910 is_moving_before = (WasJustMoving[x][y] != 0);
4911 is_moving_after = (getElementMoveStepsizeExt(x, y, direction) != 0);
4913 /* reset animation only for moving elements which change direction of moving
4914 or which just started or stopped moving
4915 (else CEs with property "can move" / "not moving" are reset each frame) */
4916 if (is_moving_before != is_moving_after ||
4917 direction != MovDir[x][y])
4918 ResetGfxAnimation(x, y);
4920 MovDir[x][y] = direction;
4921 GfxDir[x][y] = direction;
4923 GfxAction[x][y] = (!is_moving_after ? ACTION_WAITING :
4924 direction == MV_DOWN && CAN_FALL(element) ?
4925 ACTION_FALLING : ACTION_MOVING);
4927 /* this is needed for CEs with property "can move" / "not moving" */
4929 if (is_moving_after)
4931 if (Feld[newx][newy] == EL_EMPTY)
4932 Feld[newx][newy] = EL_BLOCKED;
4934 MovDir[newx][newy] = MovDir[x][y];
4936 CustomValue[newx][newy] = CustomValue[x][y];
4938 GfxFrame[newx][newy] = GfxFrame[x][y];
4939 GfxRandom[newx][newy] = GfxRandom[x][y];
4940 GfxAction[newx][newy] = GfxAction[x][y];
4941 GfxDir[newx][newy] = GfxDir[x][y];
4945 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
4947 int direction = MovDir[x][y];
4948 int newx = x + (direction & MV_LEFT ? -1 : direction & MV_RIGHT ? +1 : 0);
4949 int newy = y + (direction & MV_UP ? -1 : direction & MV_DOWN ? +1 : 0);
4955 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
4957 int oldx = x, oldy = y;
4958 int direction = MovDir[x][y];
4960 if (direction == MV_LEFT)
4962 else if (direction == MV_RIGHT)
4964 else if (direction == MV_UP)
4966 else if (direction == MV_DOWN)
4969 *comes_from_x = oldx;
4970 *comes_from_y = oldy;
4973 int MovingOrBlocked2Element(int x, int y)
4975 int element = Feld[x][y];
4977 if (element == EL_BLOCKED)
4981 Blocked2Moving(x, y, &oldx, &oldy);
4982 return Feld[oldx][oldy];
4988 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
4990 /* like MovingOrBlocked2Element(), but if element is moving
4991 and (x,y) is the field the moving element is just leaving,
4992 return EL_BLOCKED instead of the element value */
4993 int element = Feld[x][y];
4995 if (IS_MOVING(x, y))
4997 if (element == EL_BLOCKED)
5001 Blocked2Moving(x, y, &oldx, &oldy);
5002 return Feld[oldx][oldy];
5011 static void RemoveField(int x, int y)
5013 Feld[x][y] = EL_EMPTY;
5019 CustomValue[x][y] = 0;
5022 ChangeDelay[x][y] = 0;
5023 ChangePage[x][y] = -1;
5024 Pushed[x][y] = FALSE;
5026 GfxElement[x][y] = EL_UNDEFINED;
5027 GfxAction[x][y] = ACTION_DEFAULT;
5028 GfxDir[x][y] = MV_NONE;
5031 void RemoveMovingField(int x, int y)
5033 int oldx = x, oldy = y, newx = x, newy = y;
5034 int element = Feld[x][y];
5035 int next_element = EL_UNDEFINED;
5037 if (element != EL_BLOCKED && !IS_MOVING(x, y))
5040 if (IS_MOVING(x, y))
5042 Moving2Blocked(x, y, &newx, &newy);
5044 if (Feld[newx][newy] != EL_BLOCKED)
5046 /* element is moving, but target field is not free (blocked), but
5047 already occupied by something different (example: acid pool);
5048 in this case, only remove the moving field, but not the target */
5050 RemoveField(oldx, oldy);
5052 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5054 TEST_DrawLevelField(oldx, oldy);
5059 else if (element == EL_BLOCKED)
5061 Blocked2Moving(x, y, &oldx, &oldy);
5062 if (!IS_MOVING(oldx, oldy))
5066 if (element == EL_BLOCKED &&
5067 (Feld[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
5068 Feld[oldx][oldy] == EL_QUICKSAND_FAST_EMPTYING ||
5069 Feld[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
5070 Feld[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
5071 Feld[oldx][oldy] == EL_DC_MAGIC_WALL_EMPTYING ||
5072 Feld[oldx][oldy] == EL_AMOEBA_DROPPING))
5073 next_element = get_next_element(Feld[oldx][oldy]);
5075 RemoveField(oldx, oldy);
5076 RemoveField(newx, newy);
5078 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5080 if (next_element != EL_UNDEFINED)
5081 Feld[oldx][oldy] = next_element;
5083 TEST_DrawLevelField(oldx, oldy);
5084 TEST_DrawLevelField(newx, newy);
5087 void DrawDynamite(int x, int y)
5089 int sx = SCREENX(x), sy = SCREENY(y);
5090 int graphic = el2img(Feld[x][y]);
5093 if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
5096 if (IS_WALKABLE_INSIDE(Back[x][y]))
5100 DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
5101 else if (Store[x][y])
5102 DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
5104 frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5106 if (Back[x][y] || Store[x][y])
5107 DrawGraphicThruMask(sx, sy, graphic, frame);
5109 DrawGraphic(sx, sy, graphic, frame);
5112 void CheckDynamite(int x, int y)
5114 if (MovDelay[x][y] != 0) /* dynamite is still waiting to explode */
5118 if (MovDelay[x][y] != 0)
5121 PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5127 StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5132 static void setMinimalPlayerBoundaries(int *sx1, int *sy1, int *sx2, int *sy2)
5134 boolean num_checked_players = 0;
5137 for (i = 0; i < MAX_PLAYERS; i++)
5139 if (stored_player[i].active)
5141 int sx = stored_player[i].jx;
5142 int sy = stored_player[i].jy;
5144 if (num_checked_players == 0)
5151 *sx1 = MIN(*sx1, sx);
5152 *sy1 = MIN(*sy1, sy);
5153 *sx2 = MAX(*sx2, sx);
5154 *sy2 = MAX(*sy2, sy);
5157 num_checked_players++;
5162 static boolean checkIfAllPlayersFitToScreen_RND()
5164 int sx1 = 0, sy1 = 0, sx2 = 0, sy2 = 0;
5166 setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5168 return (sx2 - sx1 < SCR_FIELDX &&
5169 sy2 - sy1 < SCR_FIELDY);
5172 static void setScreenCenteredToAllPlayers(int *sx, int *sy)
5174 int sx1 = scroll_x, sy1 = scroll_y, sx2 = scroll_x, sy2 = scroll_y;
5176 setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5178 *sx = (sx1 + sx2) / 2;
5179 *sy = (sy1 + sy2) / 2;
5182 void DrawRelocateScreen(int old_x, int old_y, int x, int y, int move_dir,
5183 boolean center_screen, boolean quick_relocation)
5185 unsigned int frame_delay_value_old = GetVideoFrameDelay();
5186 boolean ffwd_delay = (tape.playing && tape.fast_forward);
5187 boolean no_delay = (tape.warp_forward);
5188 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5189 int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5190 int new_scroll_x, new_scroll_y;
5192 if (level.lazy_relocation && IN_VIS_FIELD(SCREENX(x), SCREENY(y)))
5194 /* case 1: quick relocation inside visible screen (without scrolling) */
5201 if (!level.shifted_relocation || center_screen)
5203 /* relocation _with_ centering of screen */
5205 new_scroll_x = SCROLL_POSITION_X(x);
5206 new_scroll_y = SCROLL_POSITION_Y(y);
5210 /* relocation _without_ centering of screen */
5212 int center_scroll_x = SCROLL_POSITION_X(old_x);
5213 int center_scroll_y = SCROLL_POSITION_Y(old_y);
5214 int offset_x = x + (scroll_x - center_scroll_x);
5215 int offset_y = y + (scroll_y - center_scroll_y);
5217 /* for new screen position, apply previous offset to center position */
5218 new_scroll_x = SCROLL_POSITION_X(offset_x);
5219 new_scroll_y = SCROLL_POSITION_Y(offset_y);
5222 if (quick_relocation)
5224 /* case 2: quick relocation (redraw without visible scrolling) */
5226 scroll_x = new_scroll_x;
5227 scroll_y = new_scroll_y;
5234 /* case 3: visible relocation (with scrolling to new position) */
5236 ScrollScreen(NULL, SCROLL_GO_ON); /* scroll last frame to full tile */
5238 SetVideoFrameDelay(wait_delay_value);
5240 while (scroll_x != new_scroll_x || scroll_y != new_scroll_y)
5243 int fx = FX, fy = FY;
5245 dx = (new_scroll_x < scroll_x ? +1 : new_scroll_x > scroll_x ? -1 : 0);
5246 dy = (new_scroll_y < scroll_y ? +1 : new_scroll_y > scroll_y ? -1 : 0);
5248 if (dx == 0 && dy == 0) /* no scrolling needed at all */
5254 fx += dx * TILEX / 2;
5255 fy += dy * TILEY / 2;
5257 ScrollLevel(dx, dy);
5260 /* scroll in two steps of half tile size to make things smoother */
5261 BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
5263 /* scroll second step to align at full tile size */
5264 BlitScreenToBitmap(window);
5270 SetVideoFrameDelay(frame_delay_value_old);
5273 void RelocatePlayer(int jx, int jy, int el_player_raw)
5275 int el_player = GET_PLAYER_ELEMENT(el_player_raw);
5276 int player_nr = GET_PLAYER_NR(el_player);
5277 struct PlayerInfo *player = &stored_player[player_nr];
5278 boolean ffwd_delay = (tape.playing && tape.fast_forward);
5279 boolean no_delay = (tape.warp_forward);
5280 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5281 int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5282 int old_jx = player->jx;
5283 int old_jy = player->jy;
5284 int old_element = Feld[old_jx][old_jy];
5285 int element = Feld[jx][jy];
5286 boolean player_relocated = (old_jx != jx || old_jy != jy);
5288 int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
5289 int move_dir_vert = (jy < old_jy ? MV_UP : jy > old_jy ? MV_DOWN : 0);
5290 int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
5291 int enter_side_vert = MV_DIR_OPPOSITE(move_dir_vert);
5292 int leave_side_horiz = move_dir_horiz;
5293 int leave_side_vert = move_dir_vert;
5294 int enter_side = enter_side_horiz | enter_side_vert;
5295 int leave_side = leave_side_horiz | leave_side_vert;
5297 if (player->GameOver) /* do not reanimate dead player */
5300 if (!player_relocated) /* no need to relocate the player */
5303 if (IS_PLAYER(jx, jy)) /* player already placed at new position */
5305 RemoveField(jx, jy); /* temporarily remove newly placed player */
5306 DrawLevelField(jx, jy);
5309 if (player->present)
5311 while (player->MovPos)
5313 ScrollPlayer(player, SCROLL_GO_ON);
5314 ScrollScreen(NULL, SCROLL_GO_ON);
5316 AdvanceFrameAndPlayerCounters(player->index_nr);
5320 BackToFront_WithFrameDelay(wait_delay_value);
5323 DrawPlayer(player); /* needed here only to cleanup last field */
5324 DrawLevelField(player->jx, player->jy); /* remove player graphic */
5326 player->is_moving = FALSE;
5329 if (IS_CUSTOM_ELEMENT(old_element))
5330 CheckElementChangeByPlayer(old_jx, old_jy, old_element,
5332 player->index_bit, leave_side);
5334 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
5336 player->index_bit, leave_side);
5338 Feld[jx][jy] = el_player;
5339 InitPlayerField(jx, jy, el_player, TRUE);
5341 /* "InitPlayerField()" above sets Feld[jx][jy] to EL_EMPTY, but it may be
5342 possible that the relocation target field did not contain a player element,
5343 but a walkable element, to which the new player was relocated -- in this
5344 case, restore that (already initialized!) element on the player field */
5345 if (!ELEM_IS_PLAYER(element)) /* player may be set on walkable element */
5347 Feld[jx][jy] = element; /* restore previously existing element */
5350 /* only visually relocate centered player */
5351 DrawRelocateScreen(old_jx, old_jy, player->jx, player->jy, player->MovDir,
5352 FALSE, level.instant_relocation);
5354 TestIfPlayerTouchesBadThing(jx, jy);
5355 TestIfPlayerTouchesCustomElement(jx, jy);
5357 if (IS_CUSTOM_ELEMENT(element))
5358 CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
5359 player->index_bit, enter_side);
5361 CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
5362 player->index_bit, enter_side);
5364 if (player->is_switching)
5366 /* ensure that relocation while still switching an element does not cause
5367 a new element to be treated as also switched directly after relocation
5368 (this is important for teleporter switches that teleport the player to
5369 a place where another teleporter switch is in the same direction, which
5370 would then incorrectly be treated as immediately switched before the
5371 direction key that caused the switch was released) */
5373 player->switch_x += jx - old_jx;
5374 player->switch_y += jy - old_jy;
5378 void Explode(int ex, int ey, int phase, int mode)
5384 /* !!! eliminate this variable !!! */
5385 int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
5387 if (game.explosions_delayed)
5389 ExplodeField[ex][ey] = mode;
5393 if (phase == EX_PHASE_START) /* initialize 'Store[][]' field */
5395 int center_element = Feld[ex][ey];
5396 int artwork_element, explosion_element; /* set these values later */
5398 /* remove things displayed in background while burning dynamite */
5399 if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
5402 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
5404 /* put moving element to center field (and let it explode there) */
5405 center_element = MovingOrBlocked2Element(ex, ey);
5406 RemoveMovingField(ex, ey);
5407 Feld[ex][ey] = center_element;
5410 /* now "center_element" is finally determined -- set related values now */
5411 artwork_element = center_element; /* for custom player artwork */
5412 explosion_element = center_element; /* for custom player artwork */
5414 if (IS_PLAYER(ex, ey))
5416 int player_nr = GET_PLAYER_NR(StorePlayer[ex][ey]);
5418 artwork_element = stored_player[player_nr].artwork_element;
5420 if (level.use_explosion_element[player_nr])
5422 explosion_element = level.explosion_element[player_nr];
5423 artwork_element = explosion_element;
5427 if (mode == EX_TYPE_NORMAL ||
5428 mode == EX_TYPE_CENTER ||
5429 mode == EX_TYPE_CROSS)
5430 PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
5432 last_phase = element_info[explosion_element].explosion_delay + 1;
5434 for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
5436 int xx = x - ex + 1;
5437 int yy = y - ey + 1;
5440 if (!IN_LEV_FIELD(x, y) ||
5441 (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
5442 (mode == EX_TYPE_CROSS && (x != ex && y != ey)))
5445 element = Feld[x][y];
5447 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
5449 element = MovingOrBlocked2Element(x, y);
5451 if (!IS_EXPLOSION_PROOF(element))
5452 RemoveMovingField(x, y);
5455 /* indestructible elements can only explode in center (but not flames) */
5456 if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
5457 mode == EX_TYPE_BORDER)) ||
5458 element == EL_FLAMES)
5461 /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
5462 behaviour, for example when touching a yamyam that explodes to rocks
5463 with active deadly shield, a rock is created under the player !!! */
5464 /* (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8) */
5466 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
5467 (game.engine_version < VERSION_IDENT(3,1,0,0) ||
5468 (x == ex && y == ey && mode != EX_TYPE_BORDER)))
5470 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
5473 if (IS_ACTIVE_BOMB(element))
5475 /* re-activate things under the bomb like gate or penguin */
5476 Feld[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
5483 /* save walkable background elements while explosion on same tile */
5484 if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
5485 (x != ex || y != ey || mode == EX_TYPE_BORDER))
5486 Back[x][y] = element;
5488 /* ignite explodable elements reached by other explosion */
5489 if (element == EL_EXPLOSION)
5490 element = Store2[x][y];
5492 if (AmoebaNr[x][y] &&
5493 (element == EL_AMOEBA_FULL ||
5494 element == EL_BD_AMOEBA ||
5495 element == EL_AMOEBA_GROWING))
5497 AmoebaCnt[AmoebaNr[x][y]]--;
5498 AmoebaCnt2[AmoebaNr[x][y]]--;
5503 if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
5505 int player_nr = StorePlayer[ex][ey] - EL_PLAYER_1;
5507 Store[x][y] = EL_PLAYER_IS_EXPLODING_1 + player_nr;
5509 if (PLAYERINFO(ex, ey)->use_murphy)
5510 Store[x][y] = EL_EMPTY;
5513 /* !!! check this case -- currently needed for rnd_rado_negundo_v,
5514 !!! levels 015 018 019 020 021 022 023 026 027 028 !!! */
5515 else if (ELEM_IS_PLAYER(center_element))
5516 Store[x][y] = EL_EMPTY;
5517 else if (center_element == EL_YAMYAM)
5518 Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
5519 else if (element_info[center_element].content.e[xx][yy] != EL_EMPTY)
5520 Store[x][y] = element_info[center_element].content.e[xx][yy];
5522 /* needed because EL_BD_BUTTERFLY is not defined as "CAN_EXPLODE"
5523 (killing EL_BD_BUTTERFLY with dynamite would result in BD diamond
5524 otherwise) -- FIX THIS !!! */
5525 else if (!CAN_EXPLODE(element) && element != EL_BD_BUTTERFLY)
5526 Store[x][y] = element_info[element].content.e[1][1];
5528 else if (!CAN_EXPLODE(element))
5529 Store[x][y] = element_info[element].content.e[1][1];
5532 Store[x][y] = EL_EMPTY;
5534 if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
5535 center_element == EL_AMOEBA_TO_DIAMOND)
5536 Store2[x][y] = element;
5538 Feld[x][y] = EL_EXPLOSION;
5539 GfxElement[x][y] = artwork_element;
5541 ExplodePhase[x][y] = 1;
5542 ExplodeDelay[x][y] = last_phase;
5547 if (center_element == EL_YAMYAM)
5548 game.yamyam_content_nr =
5549 (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
5561 GfxFrame[x][y] = 0; /* restart explosion animation */
5563 last_phase = ExplodeDelay[x][y];
5565 ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
5567 /* this can happen if the player leaves an explosion just in time */
5568 if (GfxElement[x][y] == EL_UNDEFINED)
5569 GfxElement[x][y] = EL_EMPTY;
5571 border_element = Store2[x][y];
5572 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5573 border_element = StorePlayer[x][y];
5575 if (phase == element_info[border_element].ignition_delay ||
5576 phase == last_phase)
5578 boolean border_explosion = FALSE;
5580 if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
5581 !PLAYER_EXPLOSION_PROTECTED(x, y))
5583 KillPlayerUnlessExplosionProtected(x, y);
5584 border_explosion = TRUE;
5586 else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
5588 Feld[x][y] = Store2[x][y];
5591 border_explosion = TRUE;
5593 else if (border_element == EL_AMOEBA_TO_DIAMOND)
5595 AmoebeUmwandeln(x, y);
5597 border_explosion = TRUE;
5600 /* if an element just explodes due to another explosion (chain-reaction),
5601 do not immediately end the new explosion when it was the last frame of
5602 the explosion (as it would be done in the following "if"-statement!) */
5603 if (border_explosion && phase == last_phase)
5607 if (phase == last_phase)
5611 element = Feld[x][y] = Store[x][y];
5612 Store[x][y] = Store2[x][y] = 0;
5613 GfxElement[x][y] = EL_UNDEFINED;
5615 /* player can escape from explosions and might therefore be still alive */
5616 if (element >= EL_PLAYER_IS_EXPLODING_1 &&
5617 element <= EL_PLAYER_IS_EXPLODING_4)
5619 int player_nr = element - EL_PLAYER_IS_EXPLODING_1;
5620 int explosion_element = EL_PLAYER_1 + player_nr;
5621 int xx = MIN(MAX(0, x - stored_player[player_nr].jx + 1), 2);
5622 int yy = MIN(MAX(0, y - stored_player[player_nr].jy + 1), 2);
5624 if (level.use_explosion_element[player_nr])
5625 explosion_element = level.explosion_element[player_nr];
5627 Feld[x][y] = (stored_player[player_nr].active ? EL_EMPTY :
5628 element_info[explosion_element].content.e[xx][yy]);
5631 /* restore probably existing indestructible background element */
5632 if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
5633 element = Feld[x][y] = Back[x][y];
5636 MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
5637 GfxDir[x][y] = MV_NONE;
5638 ChangeDelay[x][y] = 0;
5639 ChangePage[x][y] = -1;
5641 CustomValue[x][y] = 0;
5643 InitField_WithBug2(x, y, FALSE);
5645 TEST_DrawLevelField(x, y);
5647 TestIfElementTouchesCustomElement(x, y);
5649 if (GFX_CRUMBLED(element))
5650 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
5652 if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
5653 StorePlayer[x][y] = 0;
5655 if (ELEM_IS_PLAYER(element))
5656 RelocatePlayer(x, y, element);
5658 else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
5660 int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
5661 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5664 TEST_DrawLevelFieldCrumbled(x, y);
5666 if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
5668 DrawLevelElement(x, y, Back[x][y]);
5669 DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
5671 else if (IS_WALKABLE_UNDER(Back[x][y]))
5673 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5674 DrawLevelElementThruMask(x, y, Back[x][y]);
5676 else if (!IS_WALKABLE_INSIDE(Back[x][y]))
5677 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5681 void DynaExplode(int ex, int ey)
5684 int dynabomb_element = Feld[ex][ey];
5685 int dynabomb_size = 1;
5686 boolean dynabomb_xl = FALSE;
5687 struct PlayerInfo *player;
5688 static int xy[4][2] =
5696 if (IS_ACTIVE_BOMB(dynabomb_element))
5698 player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
5699 dynabomb_size = player->dynabomb_size;
5700 dynabomb_xl = player->dynabomb_xl;
5701 player->dynabombs_left++;
5704 Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
5706 for (i = 0; i < NUM_DIRECTIONS; i++)
5708 for (j = 1; j <= dynabomb_size; j++)
5710 int x = ex + j * xy[i][0];
5711 int y = ey + j * xy[i][1];
5714 if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
5717 element = Feld[x][y];
5719 /* do not restart explosions of fields with active bombs */
5720 if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
5723 Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
5725 if (element != EL_EMPTY && element != EL_EXPLOSION &&
5726 !IS_DIGGABLE(element) && !dynabomb_xl)
5732 void Bang(int x, int y)
5734 int element = MovingOrBlocked2Element(x, y);
5735 int explosion_type = EX_TYPE_NORMAL;
5737 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5739 struct PlayerInfo *player = PLAYERINFO(x, y);
5741 element = Feld[x][y] = player->initial_element;
5743 if (level.use_explosion_element[player->index_nr])
5745 int explosion_element = level.explosion_element[player->index_nr];
5747 if (element_info[explosion_element].explosion_type == EXPLODES_CROSS)
5748 explosion_type = EX_TYPE_CROSS;
5749 else if (element_info[explosion_element].explosion_type == EXPLODES_1X1)
5750 explosion_type = EX_TYPE_CENTER;
5758 case EL_BD_BUTTERFLY:
5761 case EL_DARK_YAMYAM:
5765 RaiseScoreElement(element);
5768 case EL_DYNABOMB_PLAYER_1_ACTIVE:
5769 case EL_DYNABOMB_PLAYER_2_ACTIVE:
5770 case EL_DYNABOMB_PLAYER_3_ACTIVE:
5771 case EL_DYNABOMB_PLAYER_4_ACTIVE:
5772 case EL_DYNABOMB_INCREASE_NUMBER:
5773 case EL_DYNABOMB_INCREASE_SIZE:
5774 case EL_DYNABOMB_INCREASE_POWER:
5775 explosion_type = EX_TYPE_DYNA;
5778 case EL_DC_LANDMINE:
5779 explosion_type = EX_TYPE_CENTER;
5784 case EL_LAMP_ACTIVE:
5785 case EL_AMOEBA_TO_DIAMOND:
5786 if (!IS_PLAYER(x, y)) /* penguin and player may be at same field */
5787 explosion_type = EX_TYPE_CENTER;
5791 if (element_info[element].explosion_type == EXPLODES_CROSS)
5792 explosion_type = EX_TYPE_CROSS;
5793 else if (element_info[element].explosion_type == EXPLODES_1X1)
5794 explosion_type = EX_TYPE_CENTER;
5798 if (explosion_type == EX_TYPE_DYNA)
5801 Explode(x, y, EX_PHASE_START, explosion_type);
5803 CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
5806 void SplashAcid(int x, int y)
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_LEFT;
5813 if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
5814 (!IN_LEV_FIELD(x + 1, y - 2) ||
5815 !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
5816 Feld[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
5818 PlayLevelSound(x, y, SND_ACID_SPLASHING);
5821 static void InitBeltMovement()
5823 static int belt_base_element[4] =
5825 EL_CONVEYOR_BELT_1_LEFT,
5826 EL_CONVEYOR_BELT_2_LEFT,
5827 EL_CONVEYOR_BELT_3_LEFT,
5828 EL_CONVEYOR_BELT_4_LEFT
5830 static int belt_base_active_element[4] =
5832 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
5833 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
5834 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
5835 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
5840 /* set frame order for belt animation graphic according to belt direction */
5841 for (i = 0; i < NUM_BELTS; i++)
5845 for (j = 0; j < NUM_BELT_PARTS; j++)
5847 int element = belt_base_active_element[belt_nr] + j;
5848 int graphic_1 = el2img(element);
5849 int graphic_2 = el2panelimg(element);
5851 if (game.belt_dir[i] == MV_LEFT)
5853 graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
5854 graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
5858 graphic_info[graphic_1].anim_mode |= ANIM_REVERSE;
5859 graphic_info[graphic_2].anim_mode |= ANIM_REVERSE;
5864 SCAN_PLAYFIELD(x, y)
5866 int element = Feld[x][y];
5868 for (i = 0; i < NUM_BELTS; i++)
5870 if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
5872 int e_belt_nr = getBeltNrFromBeltElement(element);
5875 if (e_belt_nr == belt_nr)
5877 int belt_part = Feld[x][y] - belt_base_element[belt_nr];
5879 Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
5886 static void ToggleBeltSwitch(int x, int y)
5888 static int belt_base_element[4] =
5890 EL_CONVEYOR_BELT_1_LEFT,
5891 EL_CONVEYOR_BELT_2_LEFT,
5892 EL_CONVEYOR_BELT_3_LEFT,
5893 EL_CONVEYOR_BELT_4_LEFT
5895 static int belt_base_active_element[4] =
5897 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
5898 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
5899 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
5900 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
5902 static int belt_base_switch_element[4] =
5904 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
5905 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
5906 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
5907 EL_CONVEYOR_BELT_4_SWITCH_LEFT
5909 static int belt_move_dir[4] =
5917 int element = Feld[x][y];
5918 int belt_nr = getBeltNrFromBeltSwitchElement(element);
5919 int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
5920 int belt_dir = belt_move_dir[belt_dir_nr];
5923 if (!IS_BELT_SWITCH(element))
5926 game.belt_dir_nr[belt_nr] = belt_dir_nr;
5927 game.belt_dir[belt_nr] = belt_dir;
5929 if (belt_dir_nr == 3)
5932 /* set frame order for belt animation graphic according to belt direction */
5933 for (i = 0; i < NUM_BELT_PARTS; i++)
5935 int element = belt_base_active_element[belt_nr] + i;
5936 int graphic_1 = el2img(element);
5937 int graphic_2 = el2panelimg(element);
5939 if (belt_dir == MV_LEFT)
5941 graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
5942 graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
5946 graphic_info[graphic_1].anim_mode |= ANIM_REVERSE;
5947 graphic_info[graphic_2].anim_mode |= ANIM_REVERSE;
5951 SCAN_PLAYFIELD(xx, yy)
5953 int element = Feld[xx][yy];
5955 if (IS_BELT_SWITCH(element))
5957 int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
5959 if (e_belt_nr == belt_nr)
5961 Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
5962 TEST_DrawLevelField(xx, yy);
5965 else if (IS_BELT(element) && belt_dir != MV_NONE)
5967 int e_belt_nr = getBeltNrFromBeltElement(element);
5969 if (e_belt_nr == belt_nr)
5971 int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
5973 Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
5974 TEST_DrawLevelField(xx, yy);
5977 else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
5979 int e_belt_nr = getBeltNrFromBeltActiveElement(element);
5981 if (e_belt_nr == belt_nr)
5983 int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
5985 Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
5986 TEST_DrawLevelField(xx, yy);
5992 static void ToggleSwitchgateSwitch(int x, int y)
5996 game.switchgate_pos = !game.switchgate_pos;
5998 SCAN_PLAYFIELD(xx, yy)
6000 int element = Feld[xx][yy];
6002 if (element == EL_SWITCHGATE_SWITCH_UP)
6004 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_DOWN;
6005 TEST_DrawLevelField(xx, yy);
6007 else if (element == EL_SWITCHGATE_SWITCH_DOWN)
6009 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP;
6010 TEST_DrawLevelField(xx, yy);
6012 else if (element == EL_DC_SWITCHGATE_SWITCH_UP)
6014 Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_DOWN;
6015 TEST_DrawLevelField(xx, yy);
6017 else if (element == EL_DC_SWITCHGATE_SWITCH_DOWN)
6019 Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP;
6020 TEST_DrawLevelField(xx, yy);
6022 else if (element == EL_SWITCHGATE_OPEN ||
6023 element == EL_SWITCHGATE_OPENING)
6025 Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
6027 PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
6029 else if (element == EL_SWITCHGATE_CLOSED ||
6030 element == EL_SWITCHGATE_CLOSING)
6032 Feld[xx][yy] = EL_SWITCHGATE_OPENING;
6034 PlayLevelSoundAction(xx, yy, ACTION_OPENING);
6039 static int getInvisibleActiveFromInvisibleElement(int element)
6041 return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
6042 element == EL_INVISIBLE_WALL ? EL_INVISIBLE_WALL_ACTIVE :
6043 element == EL_INVISIBLE_SAND ? EL_INVISIBLE_SAND_ACTIVE :
6047 static int getInvisibleFromInvisibleActiveElement(int element)
6049 return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
6050 element == EL_INVISIBLE_WALL_ACTIVE ? EL_INVISIBLE_WALL :
6051 element == EL_INVISIBLE_SAND_ACTIVE ? EL_INVISIBLE_SAND :
6055 static void RedrawAllLightSwitchesAndInvisibleElements()
6059 SCAN_PLAYFIELD(x, y)
6061 int element = Feld[x][y];
6063 if (element == EL_LIGHT_SWITCH &&
6064 game.light_time_left > 0)
6066 Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
6067 TEST_DrawLevelField(x, y);
6069 else if (element == EL_LIGHT_SWITCH_ACTIVE &&
6070 game.light_time_left == 0)
6072 Feld[x][y] = EL_LIGHT_SWITCH;
6073 TEST_DrawLevelField(x, y);
6075 else if (element == EL_EMC_DRIPPER &&
6076 game.light_time_left > 0)
6078 Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
6079 TEST_DrawLevelField(x, y);
6081 else if (element == EL_EMC_DRIPPER_ACTIVE &&
6082 game.light_time_left == 0)
6084 Feld[x][y] = EL_EMC_DRIPPER;
6085 TEST_DrawLevelField(x, y);
6087 else if (element == EL_INVISIBLE_STEELWALL ||
6088 element == EL_INVISIBLE_WALL ||
6089 element == EL_INVISIBLE_SAND)
6091 if (game.light_time_left > 0)
6092 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
6094 TEST_DrawLevelField(x, y);
6096 /* uncrumble neighbour fields, if needed */
6097 if (element == EL_INVISIBLE_SAND)
6098 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6100 else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6101 element == EL_INVISIBLE_WALL_ACTIVE ||
6102 element == EL_INVISIBLE_SAND_ACTIVE)
6104 if (game.light_time_left == 0)
6105 Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
6107 TEST_DrawLevelField(x, y);
6109 /* re-crumble neighbour fields, if needed */
6110 if (element == EL_INVISIBLE_SAND)
6111 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6116 static void RedrawAllInvisibleElementsForLenses()
6120 SCAN_PLAYFIELD(x, y)
6122 int element = Feld[x][y];
6124 if (element == EL_EMC_DRIPPER &&
6125 game.lenses_time_left > 0)
6127 Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
6128 TEST_DrawLevelField(x, y);
6130 else if (element == EL_EMC_DRIPPER_ACTIVE &&
6131 game.lenses_time_left == 0)
6133 Feld[x][y] = EL_EMC_DRIPPER;
6134 TEST_DrawLevelField(x, y);
6136 else if (element == EL_INVISIBLE_STEELWALL ||
6137 element == EL_INVISIBLE_WALL ||
6138 element == EL_INVISIBLE_SAND)
6140 if (game.lenses_time_left > 0)
6141 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
6143 TEST_DrawLevelField(x, y);
6145 /* uncrumble neighbour fields, if needed */
6146 if (element == EL_INVISIBLE_SAND)
6147 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6149 else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6150 element == EL_INVISIBLE_WALL_ACTIVE ||
6151 element == EL_INVISIBLE_SAND_ACTIVE)
6153 if (game.lenses_time_left == 0)
6154 Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
6156 TEST_DrawLevelField(x, y);
6158 /* re-crumble neighbour fields, if needed */
6159 if (element == EL_INVISIBLE_SAND)
6160 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6165 static void RedrawAllInvisibleElementsForMagnifier()
6169 SCAN_PLAYFIELD(x, y)
6171 int element = Feld[x][y];
6173 if (element == EL_EMC_FAKE_GRASS &&
6174 game.magnify_time_left > 0)
6176 Feld[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
6177 TEST_DrawLevelField(x, y);
6179 else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
6180 game.magnify_time_left == 0)
6182 Feld[x][y] = EL_EMC_FAKE_GRASS;
6183 TEST_DrawLevelField(x, y);
6185 else if (IS_GATE_GRAY(element) &&
6186 game.magnify_time_left > 0)
6188 Feld[x][y] = (IS_RND_GATE_GRAY(element) ?
6189 element - EL_GATE_1_GRAY + EL_GATE_1_GRAY_ACTIVE :
6190 IS_EM_GATE_GRAY(element) ?
6191 element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
6192 IS_EMC_GATE_GRAY(element) ?
6193 element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
6194 IS_DC_GATE_GRAY(element) ?
6195 EL_DC_GATE_WHITE_GRAY_ACTIVE :
6197 TEST_DrawLevelField(x, y);
6199 else if (IS_GATE_GRAY_ACTIVE(element) &&
6200 game.magnify_time_left == 0)
6202 Feld[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ?
6203 element - EL_GATE_1_GRAY_ACTIVE + EL_GATE_1_GRAY :
6204 IS_EM_GATE_GRAY_ACTIVE(element) ?
6205 element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
6206 IS_EMC_GATE_GRAY_ACTIVE(element) ?
6207 element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
6208 IS_DC_GATE_GRAY_ACTIVE(element) ?
6209 EL_DC_GATE_WHITE_GRAY :
6211 TEST_DrawLevelField(x, y);
6216 static void ToggleLightSwitch(int x, int y)
6218 int element = Feld[x][y];
6220 game.light_time_left =
6221 (element == EL_LIGHT_SWITCH ?
6222 level.time_light * FRAMES_PER_SECOND : 0);
6224 RedrawAllLightSwitchesAndInvisibleElements();
6227 static void ActivateTimegateSwitch(int x, int y)
6231 game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
6233 SCAN_PLAYFIELD(xx, yy)
6235 int element = Feld[xx][yy];
6237 if (element == EL_TIMEGATE_CLOSED ||
6238 element == EL_TIMEGATE_CLOSING)
6240 Feld[xx][yy] = EL_TIMEGATE_OPENING;
6241 PlayLevelSound(xx, yy, SND_CLASS_TIMEGATE_OPENING);
6245 else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
6247 Feld[xx][yy] = EL_TIMEGATE_SWITCH;
6248 TEST_DrawLevelField(xx, yy);
6254 Feld[x][y] = (Feld[x][y] == EL_TIMEGATE_SWITCH ? EL_TIMEGATE_SWITCH_ACTIVE :
6255 EL_DC_TIMEGATE_SWITCH_ACTIVE);
6258 void Impact(int x, int y)
6260 boolean last_line = (y == lev_fieldy - 1);
6261 boolean object_hit = FALSE;
6262 boolean impact = (last_line || object_hit);
6263 int element = Feld[x][y];
6264 int smashed = EL_STEELWALL;
6266 if (!last_line) /* check if element below was hit */
6268 if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
6271 object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
6272 MovDir[x][y + 1] != MV_DOWN ||
6273 MovPos[x][y + 1] <= TILEY / 2));
6275 /* do not smash moving elements that left the smashed field in time */
6276 if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
6277 ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
6280 #if USE_QUICKSAND_IMPACT_BUGFIX
6281 if (Feld[x][y + 1] == EL_QUICKSAND_EMPTYING && object_hit == FALSE)
6283 RemoveMovingField(x, y + 1);
6284 Feld[x][y + 1] = EL_QUICKSAND_EMPTY;
6285 Feld[x][y + 2] = EL_ROCK;
6286 TEST_DrawLevelField(x, y + 2);
6291 if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTYING && object_hit == FALSE)
6293 RemoveMovingField(x, y + 1);
6294 Feld[x][y + 1] = EL_QUICKSAND_FAST_EMPTY;
6295 Feld[x][y + 2] = EL_ROCK;
6296 TEST_DrawLevelField(x, y + 2);
6303 smashed = MovingOrBlocked2Element(x, y + 1);
6305 impact = (last_line || object_hit);
6308 if (!last_line && smashed == EL_ACID) /* element falls into acid */
6310 SplashAcid(x, y + 1);
6314 /* !!! not sufficient for all cases -- see EL_PEARL below !!! */
6315 /* only reset graphic animation if graphic really changes after impact */
6317 el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
6319 ResetGfxAnimation(x, y);
6320 TEST_DrawLevelField(x, y);
6323 if (impact && CAN_EXPLODE_IMPACT(element))
6328 else if (impact && element == EL_PEARL &&
6329 smashed != EL_DC_MAGIC_WALL && smashed != EL_DC_MAGIC_WALL_ACTIVE)
6331 ResetGfxAnimation(x, y);
6333 Feld[x][y] = EL_PEARL_BREAKING;
6334 PlayLevelSound(x, y, SND_PEARL_BREAKING);
6337 else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
6339 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6344 if (impact && element == EL_AMOEBA_DROP)
6346 if (object_hit && IS_PLAYER(x, y + 1))
6347 KillPlayerUnlessEnemyProtected(x, y + 1);
6348 else if (object_hit && smashed == EL_PENGUIN)
6352 Feld[x][y] = EL_AMOEBA_GROWING;
6353 Store[x][y] = EL_AMOEBA_WET;
6355 ResetRandomAnimationValue(x, y);
6360 if (object_hit) /* check which object was hit */
6362 if ((CAN_PASS_MAGIC_WALL(element) &&
6363 (smashed == EL_MAGIC_WALL ||
6364 smashed == EL_BD_MAGIC_WALL)) ||
6365 (CAN_PASS_DC_MAGIC_WALL(element) &&
6366 smashed == EL_DC_MAGIC_WALL))
6369 int activated_magic_wall =
6370 (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
6371 smashed == EL_BD_MAGIC_WALL ? EL_BD_MAGIC_WALL_ACTIVE :
6372 EL_DC_MAGIC_WALL_ACTIVE);
6374 /* activate magic wall / mill */
6375 SCAN_PLAYFIELD(xx, yy)
6377 if (Feld[xx][yy] == smashed)
6378 Feld[xx][yy] = activated_magic_wall;
6381 game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
6382 game.magic_wall_active = TRUE;
6384 PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
6385 SND_MAGIC_WALL_ACTIVATING :
6386 smashed == EL_BD_MAGIC_WALL ?
6387 SND_BD_MAGIC_WALL_ACTIVATING :
6388 SND_DC_MAGIC_WALL_ACTIVATING));
6391 if (IS_PLAYER(x, y + 1))
6393 if (CAN_SMASH_PLAYER(element))
6395 KillPlayerUnlessEnemyProtected(x, y + 1);
6399 else if (smashed == EL_PENGUIN)
6401 if (CAN_SMASH_PLAYER(element))
6407 else if (element == EL_BD_DIAMOND)
6409 if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
6415 else if (((element == EL_SP_INFOTRON ||
6416 element == EL_SP_ZONK) &&
6417 (smashed == EL_SP_SNIKSNAK ||
6418 smashed == EL_SP_ELECTRON ||
6419 smashed == EL_SP_DISK_ORANGE)) ||
6420 (element == EL_SP_INFOTRON &&
6421 smashed == EL_SP_DISK_YELLOW))
6426 else if (CAN_SMASH_EVERYTHING(element))
6428 if (IS_CLASSIC_ENEMY(smashed) ||
6429 CAN_EXPLODE_SMASHED(smashed))
6434 else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
6436 if (smashed == EL_LAMP ||
6437 smashed == EL_LAMP_ACTIVE)
6442 else if (smashed == EL_NUT)
6444 Feld[x][y + 1] = EL_NUT_BREAKING;
6445 PlayLevelSound(x, y, SND_NUT_BREAKING);
6446 RaiseScoreElement(EL_NUT);
6449 else if (smashed == EL_PEARL)
6451 ResetGfxAnimation(x, y);
6453 Feld[x][y + 1] = EL_PEARL_BREAKING;
6454 PlayLevelSound(x, y, SND_PEARL_BREAKING);
6457 else if (smashed == EL_DIAMOND)
6459 Feld[x][y + 1] = EL_DIAMOND_BREAKING;
6460 PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
6463 else if (IS_BELT_SWITCH(smashed))
6465 ToggleBeltSwitch(x, y + 1);
6467 else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
6468 smashed == EL_SWITCHGATE_SWITCH_DOWN ||
6469 smashed == EL_DC_SWITCHGATE_SWITCH_UP ||
6470 smashed == EL_DC_SWITCHGATE_SWITCH_DOWN)
6472 ToggleSwitchgateSwitch(x, y + 1);
6474 else if (smashed == EL_LIGHT_SWITCH ||
6475 smashed == EL_LIGHT_SWITCH_ACTIVE)
6477 ToggleLightSwitch(x, y + 1);
6481 CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6483 CheckElementChangeBySide(x, y + 1, smashed, element,
6484 CE_SWITCHED, CH_SIDE_TOP);
6485 CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
6491 CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6496 /* play sound of magic wall / mill */
6498 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
6499 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ||
6500 Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE))
6502 if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
6503 PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
6504 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
6505 PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
6506 else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
6507 PlayLevelSound(x, y, SND_DC_MAGIC_WALL_FILLING);
6512 /* play sound of object that hits the ground */
6513 if (last_line || object_hit)
6514 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6517 inline static void TurnRoundExt(int x, int y)
6529 { 0, 0 }, { 0, 0 }, { 0, 0 },
6534 int left, right, back;
6538 { MV_DOWN, MV_UP, MV_RIGHT },
6539 { MV_UP, MV_DOWN, MV_LEFT },
6541 { MV_LEFT, MV_RIGHT, MV_DOWN },
6545 { MV_RIGHT, MV_LEFT, MV_UP }
6548 int element = Feld[x][y];
6549 int move_pattern = element_info[element].move_pattern;
6551 int old_move_dir = MovDir[x][y];
6552 int left_dir = turn[old_move_dir].left;
6553 int right_dir = turn[old_move_dir].right;
6554 int back_dir = turn[old_move_dir].back;
6556 int left_dx = move_xy[left_dir].dx, left_dy = move_xy[left_dir].dy;
6557 int right_dx = move_xy[right_dir].dx, right_dy = move_xy[right_dir].dy;
6558 int move_dx = move_xy[old_move_dir].dx, move_dy = move_xy[old_move_dir].dy;
6559 int back_dx = move_xy[back_dir].dx, back_dy = move_xy[back_dir].dy;
6561 int left_x = x + left_dx, left_y = y + left_dy;
6562 int right_x = x + right_dx, right_y = y + right_dy;
6563 int move_x = x + move_dx, move_y = y + move_dy;
6567 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
6569 TestIfBadThingTouchesOtherBadThing(x, y);
6571 if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
6572 MovDir[x][y] = right_dir;
6573 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6574 MovDir[x][y] = left_dir;
6576 if (element == EL_BUG && MovDir[x][y] != old_move_dir)
6578 else if (element == EL_BD_BUTTERFLY) /* && MovDir[x][y] == left_dir) */
6581 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
6583 TestIfBadThingTouchesOtherBadThing(x, y);
6585 if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
6586 MovDir[x][y] = left_dir;
6587 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6588 MovDir[x][y] = right_dir;
6590 if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
6592 else if (element == EL_BD_FIREFLY) /* && MovDir[x][y] == right_dir) */
6595 else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
6597 TestIfBadThingTouchesOtherBadThing(x, y);
6599 if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
6600 MovDir[x][y] = left_dir;
6601 else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
6602 MovDir[x][y] = right_dir;
6604 if (MovDir[x][y] != old_move_dir)
6607 else if (element == EL_YAMYAM)
6609 boolean can_turn_left = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
6610 boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
6612 if (can_turn_left && can_turn_right)
6613 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6614 else if (can_turn_left)
6615 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6616 else if (can_turn_right)
6617 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6619 MovDir[x][y] = back_dir;
6621 MovDelay[x][y] = 16 + 16 * RND(3);
6623 else if (element == EL_DARK_YAMYAM)
6625 boolean can_turn_left = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6627 boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6630 if (can_turn_left && can_turn_right)
6631 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6632 else if (can_turn_left)
6633 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6634 else if (can_turn_right)
6635 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6637 MovDir[x][y] = back_dir;
6639 MovDelay[x][y] = 16 + 16 * RND(3);
6641 else if (element == EL_PACMAN)
6643 boolean can_turn_left = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
6644 boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
6646 if (can_turn_left && can_turn_right)
6647 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6648 else if (can_turn_left)
6649 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6650 else if (can_turn_right)
6651 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6653 MovDir[x][y] = back_dir;
6655 MovDelay[x][y] = 6 + RND(40);
6657 else if (element == EL_PIG)
6659 boolean can_turn_left = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
6660 boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
6661 boolean can_move_on = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
6662 boolean should_turn_left, should_turn_right, should_move_on;
6664 int rnd = RND(rnd_value);
6666 should_turn_left = (can_turn_left &&
6668 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
6669 y + back_dy + left_dy)));
6670 should_turn_right = (can_turn_right &&
6672 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
6673 y + back_dy + right_dy)));
6674 should_move_on = (can_move_on &&
6677 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
6678 y + move_dy + left_dy) ||
6679 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
6680 y + move_dy + right_dy)));
6682 if (should_turn_left || should_turn_right || should_move_on)
6684 if (should_turn_left && should_turn_right && should_move_on)
6685 MovDir[x][y] = (rnd < rnd_value / 3 ? left_dir :
6686 rnd < 2 * rnd_value / 3 ? right_dir :
6688 else if (should_turn_left && should_turn_right)
6689 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6690 else if (should_turn_left && should_move_on)
6691 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
6692 else if (should_turn_right && should_move_on)
6693 MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
6694 else if (should_turn_left)
6695 MovDir[x][y] = left_dir;
6696 else if (should_turn_right)
6697 MovDir[x][y] = right_dir;
6698 else if (should_move_on)
6699 MovDir[x][y] = old_move_dir;
6701 else if (can_move_on && rnd > rnd_value / 8)
6702 MovDir[x][y] = old_move_dir;
6703 else if (can_turn_left && can_turn_right)
6704 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6705 else if (can_turn_left && rnd > rnd_value / 8)
6706 MovDir[x][y] = left_dir;
6707 else if (can_turn_right && rnd > rnd_value/8)
6708 MovDir[x][y] = right_dir;
6710 MovDir[x][y] = back_dir;
6712 xx = x + move_xy[MovDir[x][y]].dx;
6713 yy = y + move_xy[MovDir[x][y]].dy;
6715 if (!IN_LEV_FIELD(xx, yy) ||
6716 (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy])))
6717 MovDir[x][y] = old_move_dir;
6721 else if (element == EL_DRAGON)
6723 boolean can_turn_left = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
6724 boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
6725 boolean can_move_on = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
6727 int rnd = RND(rnd_value);
6729 if (can_move_on && rnd > rnd_value / 8)
6730 MovDir[x][y] = old_move_dir;
6731 else if (can_turn_left && can_turn_right)
6732 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6733 else if (can_turn_left && rnd > rnd_value / 8)
6734 MovDir[x][y] = left_dir;
6735 else if (can_turn_right && rnd > rnd_value / 8)
6736 MovDir[x][y] = right_dir;
6738 MovDir[x][y] = back_dir;
6740 xx = x + move_xy[MovDir[x][y]].dx;
6741 yy = y + move_xy[MovDir[x][y]].dy;
6743 if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
6744 MovDir[x][y] = old_move_dir;
6748 else if (element == EL_MOLE)
6750 boolean can_move_on =
6751 (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
6752 IS_AMOEBOID(Feld[move_x][move_y]) ||
6753 Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
6756 boolean can_turn_left =
6757 (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
6758 IS_AMOEBOID(Feld[left_x][left_y])));
6760 boolean can_turn_right =
6761 (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
6762 IS_AMOEBOID(Feld[right_x][right_y])));
6764 if (can_turn_left && can_turn_right)
6765 MovDir[x][y] = (RND(2) ? left_dir : right_dir);
6766 else if (can_turn_left)
6767 MovDir[x][y] = left_dir;
6769 MovDir[x][y] = right_dir;
6772 if (MovDir[x][y] != old_move_dir)
6775 else if (element == EL_BALLOON)
6777 MovDir[x][y] = game.wind_direction;
6780 else if (element == EL_SPRING)
6782 if (MovDir[x][y] & MV_HORIZONTAL)
6784 if (SPRING_CAN_BUMP_FROM_FIELD(move_x, move_y) &&
6785 !SPRING_CAN_ENTER_FIELD(element, x, y + 1))
6787 Feld[move_x][move_y] = EL_EMC_SPRING_BUMPER_ACTIVE;
6788 ResetGfxAnimation(move_x, move_y);
6789 TEST_DrawLevelField(move_x, move_y);
6791 MovDir[x][y] = back_dir;
6793 else if (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
6794 SPRING_CAN_ENTER_FIELD(element, x, y + 1))
6795 MovDir[x][y] = MV_NONE;
6800 else if (element == EL_ROBOT ||
6801 element == EL_SATELLITE ||
6802 element == EL_PENGUIN ||
6803 element == EL_EMC_ANDROID)
6805 int attr_x = -1, attr_y = -1;
6816 for (i = 0; i < MAX_PLAYERS; i++)
6818 struct PlayerInfo *player = &stored_player[i];
6819 int jx = player->jx, jy = player->jy;
6821 if (!player->active)
6825 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
6833 if (element == EL_ROBOT && ZX >= 0 && ZY >= 0 &&
6834 (Feld[ZX][ZY] == EL_ROBOT_WHEEL_ACTIVE ||
6835 game.engine_version < VERSION_IDENT(3,1,0,0)))
6841 if (element == EL_PENGUIN)
6844 static int xy[4][2] =
6852 for (i = 0; i < NUM_DIRECTIONS; i++)
6854 int ex = x + xy[i][0];
6855 int ey = y + xy[i][1];
6857 if (IN_LEV_FIELD(ex, ey) && (Feld[ex][ey] == EL_EXIT_OPEN ||
6858 Feld[ex][ey] == EL_EM_EXIT_OPEN ||
6859 Feld[ex][ey] == EL_STEEL_EXIT_OPEN ||
6860 Feld[ex][ey] == EL_EM_STEEL_EXIT_OPEN))
6869 MovDir[x][y] = MV_NONE;
6871 MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
6872 else if (attr_x > x)
6873 MovDir[x][y] |= (AllPlayersGone ? MV_LEFT : MV_RIGHT);
6875 MovDir[x][y] |= (AllPlayersGone ? MV_DOWN : MV_UP);
6876 else if (attr_y > y)
6877 MovDir[x][y] |= (AllPlayersGone ? MV_UP : MV_DOWN);
6879 if (element == EL_ROBOT)
6883 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6884 MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
6885 Moving2Blocked(x, y, &newx, &newy);
6887 if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
6888 MovDelay[x][y] = 8 + 8 * !RND(3);
6890 MovDelay[x][y] = 16;
6892 else if (element == EL_PENGUIN)
6898 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6900 boolean first_horiz = RND(2);
6901 int new_move_dir = MovDir[x][y];
6904 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6905 Moving2Blocked(x, y, &newx, &newy);
6907 if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
6911 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6912 Moving2Blocked(x, y, &newx, &newy);
6914 if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
6917 MovDir[x][y] = old_move_dir;
6921 else if (element == EL_SATELLITE)
6927 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6929 boolean first_horiz = RND(2);
6930 int new_move_dir = MovDir[x][y];
6933 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6934 Moving2Blocked(x, y, &newx, &newy);
6936 if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
6940 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6941 Moving2Blocked(x, y, &newx, &newy);
6943 if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
6946 MovDir[x][y] = old_move_dir;
6950 else if (element == EL_EMC_ANDROID)
6952 static int check_pos[16] =
6954 -1, /* 0 => (invalid) */
6955 7, /* 1 => MV_LEFT */
6956 3, /* 2 => MV_RIGHT */
6957 -1, /* 3 => (invalid) */
6959 0, /* 5 => MV_LEFT | MV_UP */
6960 2, /* 6 => MV_RIGHT | MV_UP */
6961 -1, /* 7 => (invalid) */
6962 5, /* 8 => MV_DOWN */
6963 6, /* 9 => MV_LEFT | MV_DOWN */
6964 4, /* 10 => MV_RIGHT | MV_DOWN */
6965 -1, /* 11 => (invalid) */
6966 -1, /* 12 => (invalid) */
6967 -1, /* 13 => (invalid) */
6968 -1, /* 14 => (invalid) */
6969 -1, /* 15 => (invalid) */
6977 { -1, -1, MV_LEFT | MV_UP },
6979 { +1, -1, MV_RIGHT | MV_UP },
6980 { +1, 0, MV_RIGHT },
6981 { +1, +1, MV_RIGHT | MV_DOWN },
6983 { -1, +1, MV_LEFT | MV_DOWN },
6986 int start_pos, check_order;
6987 boolean can_clone = FALSE;
6990 /* check if there is any free field around current position */
6991 for (i = 0; i < 8; i++)
6993 int newx = x + check_xy[i].dx;
6994 int newy = y + check_xy[i].dy;
6996 if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7004 if (can_clone) /* randomly find an element to clone */
7008 start_pos = check_pos[RND(8)];
7009 check_order = (RND(2) ? -1 : +1);
7011 for (i = 0; i < 8; i++)
7013 int pos_raw = start_pos + i * check_order;
7014 int pos = (pos_raw + 8) % 8;
7015 int newx = x + check_xy[pos].dx;
7016 int newy = y + check_xy[pos].dy;
7018 if (ANDROID_CAN_CLONE_FIELD(newx, newy))
7020 element_info[element].move_leave_type = LEAVE_TYPE_LIMITED;
7021 element_info[element].move_leave_element = EL_TRIGGER_ELEMENT;
7023 Store[x][y] = Feld[newx][newy];
7032 if (can_clone) /* randomly find a direction to move */
7036 start_pos = check_pos[RND(8)];
7037 check_order = (RND(2) ? -1 : +1);
7039 for (i = 0; i < 8; i++)
7041 int pos_raw = start_pos + i * check_order;
7042 int pos = (pos_raw + 8) % 8;
7043 int newx = x + check_xy[pos].dx;
7044 int newy = y + check_xy[pos].dy;
7045 int new_move_dir = check_xy[pos].dir;
7047 if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7049 MovDir[x][y] = new_move_dir;
7050 MovDelay[x][y] = level.android_clone_time * 8 + 1;
7059 if (can_clone) /* cloning and moving successful */
7062 /* cannot clone -- try to move towards player */
7064 start_pos = check_pos[MovDir[x][y] & 0x0f];
7065 check_order = (RND(2) ? -1 : +1);
7067 for (i = 0; i < 3; i++)
7069 /* first check start_pos, then previous/next or (next/previous) pos */
7070 int pos_raw = start_pos + (i < 2 ? i : -1) * check_order;
7071 int pos = (pos_raw + 8) % 8;
7072 int newx = x + check_xy[pos].dx;
7073 int newy = y + check_xy[pos].dy;
7074 int new_move_dir = check_xy[pos].dir;
7076 if (IS_PLAYER(newx, newy))
7079 if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7081 MovDir[x][y] = new_move_dir;
7082 MovDelay[x][y] = level.android_move_time * 8 + 1;
7089 else if (move_pattern == MV_TURNING_LEFT ||
7090 move_pattern == MV_TURNING_RIGHT ||
7091 move_pattern == MV_TURNING_LEFT_RIGHT ||
7092 move_pattern == MV_TURNING_RIGHT_LEFT ||
7093 move_pattern == MV_TURNING_RANDOM ||
7094 move_pattern == MV_ALL_DIRECTIONS)
7096 boolean can_turn_left =
7097 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
7098 boolean can_turn_right =
7099 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
7101 if (element_info[element].move_stepsize == 0) /* "not moving" */
7104 if (move_pattern == MV_TURNING_LEFT)
7105 MovDir[x][y] = left_dir;
7106 else if (move_pattern == MV_TURNING_RIGHT)
7107 MovDir[x][y] = right_dir;
7108 else if (move_pattern == MV_TURNING_LEFT_RIGHT)
7109 MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
7110 else if (move_pattern == MV_TURNING_RIGHT_LEFT)
7111 MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
7112 else if (move_pattern == MV_TURNING_RANDOM)
7113 MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
7114 can_turn_right && !can_turn_left ? right_dir :
7115 RND(2) ? left_dir : right_dir);
7116 else if (can_turn_left && can_turn_right)
7117 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7118 else if (can_turn_left)
7119 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7120 else if (can_turn_right)
7121 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7123 MovDir[x][y] = back_dir;
7125 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7127 else if (move_pattern == MV_HORIZONTAL ||
7128 move_pattern == MV_VERTICAL)
7130 if (move_pattern & old_move_dir)
7131 MovDir[x][y] = back_dir;
7132 else if (move_pattern == MV_HORIZONTAL)
7133 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
7134 else if (move_pattern == MV_VERTICAL)
7135 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
7137 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7139 else if (move_pattern & MV_ANY_DIRECTION)
7141 MovDir[x][y] = move_pattern;
7142 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7144 else if (move_pattern & MV_WIND_DIRECTION)
7146 MovDir[x][y] = game.wind_direction;
7147 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7149 else if (move_pattern == MV_ALONG_LEFT_SIDE)
7151 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
7152 MovDir[x][y] = left_dir;
7153 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7154 MovDir[x][y] = right_dir;
7156 if (MovDir[x][y] != old_move_dir)
7157 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7159 else if (move_pattern == MV_ALONG_RIGHT_SIDE)
7161 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
7162 MovDir[x][y] = right_dir;
7163 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7164 MovDir[x][y] = left_dir;
7166 if (MovDir[x][y] != old_move_dir)
7167 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7169 else if (move_pattern == MV_TOWARDS_PLAYER ||
7170 move_pattern == MV_AWAY_FROM_PLAYER)
7172 int attr_x = -1, attr_y = -1;
7174 boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
7185 for (i = 0; i < MAX_PLAYERS; i++)
7187 struct PlayerInfo *player = &stored_player[i];
7188 int jx = player->jx, jy = player->jy;
7190 if (!player->active)
7194 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7202 MovDir[x][y] = MV_NONE;
7204 MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
7205 else if (attr_x > x)
7206 MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
7208 MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
7209 else if (attr_y > y)
7210 MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
7212 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7214 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7216 boolean first_horiz = RND(2);
7217 int new_move_dir = MovDir[x][y];
7219 if (element_info[element].move_stepsize == 0) /* "not moving" */
7221 first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
7222 MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7228 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7229 Moving2Blocked(x, y, &newx, &newy);
7231 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7235 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7236 Moving2Blocked(x, y, &newx, &newy);
7238 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7241 MovDir[x][y] = old_move_dir;
7244 else if (move_pattern == MV_WHEN_PUSHED ||
7245 move_pattern == MV_WHEN_DROPPED)
7247 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7248 MovDir[x][y] = MV_NONE;
7252 else if (move_pattern & MV_MAZE_RUNNER_STYLE)
7254 static int test_xy[7][2] =
7264 static int test_dir[7] =
7274 boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
7275 int move_preference = -1000000; /* start with very low preference */
7276 int new_move_dir = MV_NONE;
7277 int start_test = RND(4);
7280 for (i = 0; i < NUM_DIRECTIONS; i++)
7282 int move_dir = test_dir[start_test + i];
7283 int move_dir_preference;
7285 xx = x + test_xy[start_test + i][0];
7286 yy = y + test_xy[start_test + i][1];
7288 if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
7289 (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
7291 new_move_dir = move_dir;
7296 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
7299 move_dir_preference = -1 * RunnerVisit[xx][yy];
7300 if (hunter_mode && PlayerVisit[xx][yy] > 0)
7301 move_dir_preference = PlayerVisit[xx][yy];
7303 if (move_dir_preference > move_preference)
7305 /* prefer field that has not been visited for the longest time */
7306 move_preference = move_dir_preference;
7307 new_move_dir = move_dir;
7309 else if (move_dir_preference == move_preference &&
7310 move_dir == old_move_dir)
7312 /* prefer last direction when all directions are preferred equally */
7313 move_preference = move_dir_preference;
7314 new_move_dir = move_dir;
7318 MovDir[x][y] = new_move_dir;
7319 if (old_move_dir != new_move_dir)
7320 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7324 static void TurnRound(int x, int y)
7326 int direction = MovDir[x][y];
7330 GfxDir[x][y] = MovDir[x][y];
7332 if (direction != MovDir[x][y])
7336 GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction);
7338 ResetGfxFrame(x, y);
7341 static boolean JustBeingPushed(int x, int y)
7345 for (i = 0; i < MAX_PLAYERS; i++)
7347 struct PlayerInfo *player = &stored_player[i];
7349 if (player->active && player->is_pushing && player->MovPos)
7351 int next_jx = player->jx + (player->jx - player->last_jx);
7352 int next_jy = player->jy + (player->jy - player->last_jy);
7354 if (x == next_jx && y == next_jy)
7362 void StartMoving(int x, int y)
7364 boolean started_moving = FALSE; /* some elements can fall _and_ move */
7365 int element = Feld[x][y];
7370 if (MovDelay[x][y] == 0)
7371 GfxAction[x][y] = ACTION_DEFAULT;
7373 if (CAN_FALL(element) && y < lev_fieldy - 1)
7375 if ((x > 0 && IS_PLAYER(x - 1, y)) ||
7376 (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
7377 if (JustBeingPushed(x, y))
7380 if (element == EL_QUICKSAND_FULL)
7382 if (IS_FREE(x, y + 1))
7384 InitMovingField(x, y, MV_DOWN);
7385 started_moving = TRUE;
7387 Feld[x][y] = EL_QUICKSAND_EMPTYING;
7388 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7389 if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7390 Store[x][y] = EL_ROCK;
7392 Store[x][y] = EL_ROCK;
7395 PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7397 else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7399 if (!MovDelay[x][y])
7401 MovDelay[x][y] = TILEY + 1;
7403 ResetGfxAnimation(x, y);
7404 ResetGfxAnimation(x, y + 1);
7409 DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7410 DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7417 Feld[x][y] = EL_QUICKSAND_EMPTY;
7418 Feld[x][y + 1] = EL_QUICKSAND_FULL;
7419 Store[x][y + 1] = Store[x][y];
7422 PlayLevelSoundAction(x, y, ACTION_FILLING);
7424 else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7426 if (!MovDelay[x][y])
7428 MovDelay[x][y] = TILEY + 1;
7430 ResetGfxAnimation(x, y);
7431 ResetGfxAnimation(x, y + 1);
7436 DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7437 DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7444 Feld[x][y] = EL_QUICKSAND_EMPTY;
7445 Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7446 Store[x][y + 1] = Store[x][y];
7449 PlayLevelSoundAction(x, y, ACTION_FILLING);
7452 else if (element == EL_QUICKSAND_FAST_FULL)
7454 if (IS_FREE(x, y + 1))
7456 InitMovingField(x, y, MV_DOWN);
7457 started_moving = TRUE;
7459 Feld[x][y] = EL_QUICKSAND_FAST_EMPTYING;
7460 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7461 if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7462 Store[x][y] = EL_ROCK;
7464 Store[x][y] = EL_ROCK;
7467 PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7469 else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7471 if (!MovDelay[x][y])
7473 MovDelay[x][y] = TILEY + 1;
7475 ResetGfxAnimation(x, y);
7476 ResetGfxAnimation(x, y + 1);
7481 DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7482 DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7489 Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
7490 Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7491 Store[x][y + 1] = Store[x][y];
7494 PlayLevelSoundAction(x, y, ACTION_FILLING);
7496 else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7498 if (!MovDelay[x][y])
7500 MovDelay[x][y] = TILEY + 1;
7502 ResetGfxAnimation(x, y);
7503 ResetGfxAnimation(x, y + 1);
7508 DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7509 DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7516 Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
7517 Feld[x][y + 1] = EL_QUICKSAND_FULL;
7518 Store[x][y + 1] = Store[x][y];
7521 PlayLevelSoundAction(x, y, ACTION_FILLING);
7524 else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7525 Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7527 InitMovingField(x, y, MV_DOWN);
7528 started_moving = TRUE;
7530 Feld[x][y] = EL_QUICKSAND_FILLING;
7531 Store[x][y] = element;
7533 PlayLevelSoundAction(x, y, ACTION_FILLING);
7535 else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7536 Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7538 InitMovingField(x, y, MV_DOWN);
7539 started_moving = TRUE;
7541 Feld[x][y] = EL_QUICKSAND_FAST_FILLING;
7542 Store[x][y] = element;
7544 PlayLevelSoundAction(x, y, ACTION_FILLING);
7546 else if (element == EL_MAGIC_WALL_FULL)
7548 if (IS_FREE(x, y + 1))
7550 InitMovingField(x, y, MV_DOWN);
7551 started_moving = TRUE;
7553 Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
7554 Store[x][y] = EL_CHANGED(Store[x][y]);
7556 else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
7558 if (!MovDelay[x][y])
7559 MovDelay[x][y] = TILEY / 4 + 1;
7568 Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
7569 Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
7570 Store[x][y + 1] = EL_CHANGED(Store[x][y]);
7574 else if (element == EL_BD_MAGIC_WALL_FULL)
7576 if (IS_FREE(x, y + 1))
7578 InitMovingField(x, y, MV_DOWN);
7579 started_moving = TRUE;
7581 Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
7582 Store[x][y] = EL_CHANGED_BD(Store[x][y]);
7584 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
7586 if (!MovDelay[x][y])
7587 MovDelay[x][y] = TILEY / 4 + 1;
7596 Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
7597 Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
7598 Store[x][y + 1] = EL_CHANGED_BD(Store[x][y]);
7602 else if (element == EL_DC_MAGIC_WALL_FULL)
7604 if (IS_FREE(x, y + 1))
7606 InitMovingField(x, y, MV_DOWN);
7607 started_moving = TRUE;
7609 Feld[x][y] = EL_DC_MAGIC_WALL_EMPTYING;
7610 Store[x][y] = EL_CHANGED_DC(Store[x][y]);
7612 else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
7614 if (!MovDelay[x][y])
7615 MovDelay[x][y] = TILEY / 4 + 1;
7624 Feld[x][y] = EL_DC_MAGIC_WALL_ACTIVE;
7625 Feld[x][y + 1] = EL_DC_MAGIC_WALL_FULL;
7626 Store[x][y + 1] = EL_CHANGED_DC(Store[x][y]);
7630 else if ((CAN_PASS_MAGIC_WALL(element) &&
7631 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
7632 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)) ||
7633 (CAN_PASS_DC_MAGIC_WALL(element) &&
7634 (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)))
7637 InitMovingField(x, y, MV_DOWN);
7638 started_moving = TRUE;
7641 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
7642 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ? EL_BD_MAGIC_WALL_FILLING :
7643 EL_DC_MAGIC_WALL_FILLING);
7644 Store[x][y] = element;
7646 else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
7648 SplashAcid(x, y + 1);
7650 InitMovingField(x, y, MV_DOWN);
7651 started_moving = TRUE;
7653 Store[x][y] = EL_ACID;
7656 (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7657 CheckImpact[x][y] && !IS_FREE(x, y + 1)) ||
7658 (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
7659 CAN_FALL(element) && WasJustFalling[x][y] &&
7660 (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
7662 (game.engine_version < VERSION_IDENT(2,2,0,7) &&
7663 CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
7664 (Feld[x][y + 1] == EL_BLOCKED)))
7666 /* this is needed for a special case not covered by calling "Impact()"
7667 from "ContinueMoving()": if an element moves to a tile directly below
7668 another element which was just falling on that tile (which was empty
7669 in the previous frame), the falling element above would just stop
7670 instead of smashing the element below (in previous version, the above
7671 element was just checked for "moving" instead of "falling", resulting
7672 in incorrect smashes caused by horizontal movement of the above
7673 element; also, the case of the player being the element to smash was
7674 simply not covered here... :-/ ) */
7676 CheckCollision[x][y] = 0;
7677 CheckImpact[x][y] = 0;
7681 else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
7683 if (MovDir[x][y] == MV_NONE)
7685 InitMovingField(x, y, MV_DOWN);
7686 started_moving = TRUE;
7689 else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
7691 if (WasJustFalling[x][y]) /* prevent animation from being restarted */
7692 MovDir[x][y] = MV_DOWN;
7694 InitMovingField(x, y, MV_DOWN);
7695 started_moving = TRUE;
7697 else if (element == EL_AMOEBA_DROP)
7699 Feld[x][y] = EL_AMOEBA_GROWING;
7700 Store[x][y] = EL_AMOEBA_WET;
7702 else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
7703 (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
7704 !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
7705 element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
7707 boolean can_fall_left = (x > 0 && IS_FREE(x - 1, y) &&
7708 (IS_FREE(x - 1, y + 1) ||
7709 Feld[x - 1][y + 1] == EL_ACID));
7710 boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
7711 (IS_FREE(x + 1, y + 1) ||
7712 Feld[x + 1][y + 1] == EL_ACID));
7713 boolean can_fall_any = (can_fall_left || can_fall_right);
7714 boolean can_fall_both = (can_fall_left && can_fall_right);
7715 int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
7717 if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
7719 if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
7720 can_fall_right = FALSE;
7721 else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
7722 can_fall_left = FALSE;
7723 else if (slippery_type == SLIPPERY_ONLY_LEFT)
7724 can_fall_right = FALSE;
7725 else if (slippery_type == SLIPPERY_ONLY_RIGHT)
7726 can_fall_left = FALSE;
7728 can_fall_any = (can_fall_left || can_fall_right);
7729 can_fall_both = FALSE;
7734 if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
7735 can_fall_right = FALSE; /* slip down on left side */
7737 can_fall_left = !(can_fall_right = RND(2));
7739 can_fall_both = FALSE;
7744 /* if not determined otherwise, prefer left side for slipping down */
7745 InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
7746 started_moving = TRUE;
7749 else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
7751 boolean left_is_free = (x > 0 && IS_FREE(x - 1, y));
7752 boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
7753 int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
7754 int belt_dir = game.belt_dir[belt_nr];
7756 if ((belt_dir == MV_LEFT && left_is_free) ||
7757 (belt_dir == MV_RIGHT && right_is_free))
7759 int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
7761 InitMovingField(x, y, belt_dir);
7762 started_moving = TRUE;
7764 Pushed[x][y] = TRUE;
7765 Pushed[nextx][y] = TRUE;
7767 GfxAction[x][y] = ACTION_DEFAULT;
7771 MovDir[x][y] = 0; /* if element was moving, stop it */
7776 /* not "else if" because of elements that can fall and move (EL_SPRING) */
7777 if (CAN_MOVE(element) && !started_moving)
7779 int move_pattern = element_info[element].move_pattern;
7782 Moving2Blocked(x, y, &newx, &newy);
7784 if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
7787 if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7788 CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7790 WasJustMoving[x][y] = 0;
7791 CheckCollision[x][y] = 0;
7793 TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
7795 if (Feld[x][y] != element) /* element has changed */
7799 if (!MovDelay[x][y]) /* start new movement phase */
7801 /* all objects that can change their move direction after each step
7802 (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall */
7804 if (element != EL_YAMYAM &&
7805 element != EL_DARK_YAMYAM &&
7806 element != EL_PACMAN &&
7807 !(move_pattern & MV_ANY_DIRECTION) &&
7808 move_pattern != MV_TURNING_LEFT &&
7809 move_pattern != MV_TURNING_RIGHT &&
7810 move_pattern != MV_TURNING_LEFT_RIGHT &&
7811 move_pattern != MV_TURNING_RIGHT_LEFT &&
7812 move_pattern != MV_TURNING_RANDOM)
7816 if (MovDelay[x][y] && (element == EL_BUG ||
7817 element == EL_SPACESHIP ||
7818 element == EL_SP_SNIKSNAK ||
7819 element == EL_SP_ELECTRON ||
7820 element == EL_MOLE))
7821 TEST_DrawLevelField(x, y);
7825 if (MovDelay[x][y]) /* wait some time before next movement */
7829 if (element == EL_ROBOT ||
7830 element == EL_YAMYAM ||
7831 element == EL_DARK_YAMYAM)
7833 DrawLevelElementAnimationIfNeeded(x, y, element);
7834 PlayLevelSoundAction(x, y, ACTION_WAITING);
7836 else if (element == EL_SP_ELECTRON)
7837 DrawLevelElementAnimationIfNeeded(x, y, element);
7838 else if (element == EL_DRAGON)
7841 int dir = MovDir[x][y];
7842 int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
7843 int dy = (dir == MV_UP ? -1 : dir == MV_DOWN ? +1 : 0);
7844 int graphic = (dir == MV_LEFT ? IMG_FLAMES_1_LEFT :
7845 dir == MV_RIGHT ? IMG_FLAMES_1_RIGHT :
7846 dir == MV_UP ? IMG_FLAMES_1_UP :
7847 dir == MV_DOWN ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
7848 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
7850 GfxAction[x][y] = ACTION_ATTACKING;
7852 if (IS_PLAYER(x, y))
7853 DrawPlayerField(x, y);
7855 TEST_DrawLevelField(x, y);
7857 PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
7859 for (i = 1; i <= 3; i++)
7861 int xx = x + i * dx;
7862 int yy = y + i * dy;
7863 int sx = SCREENX(xx);
7864 int sy = SCREENY(yy);
7865 int flame_graphic = graphic + (i - 1);
7867 if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
7872 int flamed = MovingOrBlocked2Element(xx, yy);
7874 if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
7877 RemoveMovingField(xx, yy);
7879 ChangeDelay[xx][yy] = 0;
7881 Feld[xx][yy] = EL_FLAMES;
7883 if (IN_SCR_FIELD(sx, sy))
7885 TEST_DrawLevelFieldCrumbled(xx, yy);
7886 DrawGraphic(sx, sy, flame_graphic, frame);
7891 if (Feld[xx][yy] == EL_FLAMES)
7892 Feld[xx][yy] = EL_EMPTY;
7893 TEST_DrawLevelField(xx, yy);
7898 if (MovDelay[x][y]) /* element still has to wait some time */
7900 PlayLevelSoundAction(x, y, ACTION_WAITING);
7906 /* now make next step */
7908 Moving2Blocked(x, y, &newx, &newy); /* get next screen position */
7910 if (DONT_COLLIDE_WITH(element) &&
7911 IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
7912 !PLAYER_ENEMY_PROTECTED(newx, newy))
7914 TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
7919 else if (CAN_MOVE_INTO_ACID(element) &&
7920 IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID &&
7921 !IS_MV_DIAGONAL(MovDir[x][y]) &&
7922 (MovDir[x][y] == MV_DOWN ||
7923 game.engine_version >= VERSION_IDENT(3,1,0,0)))
7925 SplashAcid(newx, newy);
7926 Store[x][y] = EL_ACID;
7928 else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
7930 if (Feld[newx][newy] == EL_EXIT_OPEN ||
7931 Feld[newx][newy] == EL_EM_EXIT_OPEN ||
7932 Feld[newx][newy] == EL_STEEL_EXIT_OPEN ||
7933 Feld[newx][newy] == EL_EM_STEEL_EXIT_OPEN)
7936 TEST_DrawLevelField(x, y);
7938 PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
7939 if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
7940 DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
7942 local_player->friends_still_needed--;
7943 if (!local_player->friends_still_needed &&
7944 !local_player->GameOver && AllPlayersGone)
7945 PlayerWins(local_player);
7949 else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
7951 if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MP_MOVING)
7952 TEST_DrawLevelField(newx, newy);
7954 GfxDir[x][y] = MovDir[x][y] = MV_NONE;
7956 else if (!IS_FREE(newx, newy))
7958 GfxAction[x][y] = ACTION_WAITING;
7960 if (IS_PLAYER(x, y))
7961 DrawPlayerField(x, y);
7963 TEST_DrawLevelField(x, y);
7968 else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
7970 if (IS_FOOD_PIG(Feld[newx][newy]))
7972 if (IS_MOVING(newx, newy))
7973 RemoveMovingField(newx, newy);
7976 Feld[newx][newy] = EL_EMPTY;
7977 TEST_DrawLevelField(newx, newy);
7980 PlayLevelSound(x, y, SND_PIG_DIGGING);
7982 else if (!IS_FREE(newx, newy))
7984 if (IS_PLAYER(x, y))
7985 DrawPlayerField(x, y);
7987 TEST_DrawLevelField(x, y);
7992 else if (element == EL_EMC_ANDROID && IN_LEV_FIELD(newx, newy))
7994 if (Store[x][y] != EL_EMPTY)
7996 boolean can_clone = FALSE;
7999 /* check if element to clone is still there */
8000 for (yy = y - 1; yy <= y + 1; yy++) for (xx = x - 1; xx <= x + 1; xx++)
8002 if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == Store[x][y])
8010 /* cannot clone or target field not free anymore -- do not clone */
8011 if (!can_clone || !ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8012 Store[x][y] = EL_EMPTY;
8015 if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8017 if (IS_MV_DIAGONAL(MovDir[x][y]))
8019 int diagonal_move_dir = MovDir[x][y];
8020 int stored = Store[x][y];
8021 int change_delay = 8;
8024 /* android is moving diagonally */
8026 CreateField(x, y, EL_DIAGONAL_SHRINKING);
8028 Store[x][y] = (stored == EL_ACID ? EL_EMPTY : stored);
8029 GfxElement[x][y] = EL_EMC_ANDROID;
8030 GfxAction[x][y] = ACTION_SHRINKING;
8031 GfxDir[x][y] = diagonal_move_dir;
8032 ChangeDelay[x][y] = change_delay;
8034 graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],
8037 DrawLevelGraphicAnimation(x, y, graphic);
8038 PlayLevelSoundAction(x, y, ACTION_SHRINKING);
8040 if (Feld[newx][newy] == EL_ACID)
8042 SplashAcid(newx, newy);
8047 CreateField(newx, newy, EL_DIAGONAL_GROWING);
8049 Store[newx][newy] = EL_EMC_ANDROID;
8050 GfxElement[newx][newy] = EL_EMC_ANDROID;
8051 GfxAction[newx][newy] = ACTION_GROWING;
8052 GfxDir[newx][newy] = diagonal_move_dir;
8053 ChangeDelay[newx][newy] = change_delay;
8055 graphic = el_act_dir2img(GfxElement[newx][newy],
8056 GfxAction[newx][newy], GfxDir[newx][newy]);
8058 DrawLevelGraphicAnimation(newx, newy, graphic);
8059 PlayLevelSoundAction(newx, newy, ACTION_GROWING);
8065 Feld[newx][newy] = EL_EMPTY;
8066 TEST_DrawLevelField(newx, newy);
8068 PlayLevelSoundAction(x, y, ACTION_DIGGING);
8071 else if (!IS_FREE(newx, newy))
8076 else if (IS_CUSTOM_ELEMENT(element) &&
8077 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
8079 if (!DigFieldByCE(newx, newy, element))
8082 if (move_pattern & MV_MAZE_RUNNER_STYLE)
8084 RunnerVisit[x][y] = FrameCounter;
8085 PlayerVisit[x][y] /= 8; /* expire player visit path */
8088 else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
8090 if (!IS_FREE(newx, newy))
8092 if (IS_PLAYER(x, y))
8093 DrawPlayerField(x, y);
8095 TEST_DrawLevelField(x, y);
8101 boolean wanna_flame = !RND(10);
8102 int dx = newx - x, dy = newy - y;
8103 int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
8104 int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
8105 int element1 = (IN_LEV_FIELD(newx1, newy1) ?
8106 MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
8107 int element2 = (IN_LEV_FIELD(newx2, newy2) ?
8108 MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
8111 IS_CLASSIC_ENEMY(element1) ||
8112 IS_CLASSIC_ENEMY(element2)) &&
8113 element1 != EL_DRAGON && element2 != EL_DRAGON &&
8114 element1 != EL_FLAMES && element2 != EL_FLAMES)
8116 ResetGfxAnimation(x, y);
8117 GfxAction[x][y] = ACTION_ATTACKING;
8119 if (IS_PLAYER(x, y))
8120 DrawPlayerField(x, y);
8122 TEST_DrawLevelField(x, y);
8124 PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
8126 MovDelay[x][y] = 50;
8128 Feld[newx][newy] = EL_FLAMES;
8129 if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
8130 Feld[newx1][newy1] = EL_FLAMES;
8131 if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
8132 Feld[newx2][newy2] = EL_FLAMES;
8138 else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8139 Feld[newx][newy] == EL_DIAMOND)
8141 if (IS_MOVING(newx, newy))
8142 RemoveMovingField(newx, newy);
8145 Feld[newx][newy] = EL_EMPTY;
8146 TEST_DrawLevelField(newx, newy);
8149 PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
8151 else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8152 IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
8154 if (AmoebaNr[newx][newy])
8156 AmoebaCnt2[AmoebaNr[newx][newy]]--;
8157 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
8158 Feld[newx][newy] == EL_BD_AMOEBA)
8159 AmoebaCnt[AmoebaNr[newx][newy]]--;
8162 if (IS_MOVING(newx, newy))
8164 RemoveMovingField(newx, newy);
8168 Feld[newx][newy] = EL_EMPTY;
8169 TEST_DrawLevelField(newx, newy);
8172 PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
8174 else if ((element == EL_PACMAN || element == EL_MOLE)
8175 && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
8177 if (AmoebaNr[newx][newy])
8179 AmoebaCnt2[AmoebaNr[newx][newy]]--;
8180 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
8181 Feld[newx][newy] == EL_BD_AMOEBA)
8182 AmoebaCnt[AmoebaNr[newx][newy]]--;
8185 if (element == EL_MOLE)
8187 Feld[newx][newy] = EL_AMOEBA_SHRINKING;
8188 PlayLevelSound(x, y, SND_MOLE_DIGGING);
8190 ResetGfxAnimation(x, y);
8191 GfxAction[x][y] = ACTION_DIGGING;
8192 TEST_DrawLevelField(x, y);
8194 MovDelay[newx][newy] = 0; /* start amoeba shrinking delay */
8196 return; /* wait for shrinking amoeba */
8198 else /* element == EL_PACMAN */
8200 Feld[newx][newy] = EL_EMPTY;
8201 TEST_DrawLevelField(newx, newy);
8202 PlayLevelSound(x, y, SND_PACMAN_DIGGING);
8205 else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
8206 (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
8207 (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
8209 /* wait for shrinking amoeba to completely disappear */
8212 else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
8214 /* object was running against a wall */
8218 if (GFX_ELEMENT(element) != EL_SAND) /* !!! FIX THIS (crumble) !!! */
8219 DrawLevelElementAnimation(x, y, element);
8221 if (DONT_TOUCH(element))
8222 TestIfBadThingTouchesPlayer(x, y);
8227 InitMovingField(x, y, MovDir[x][y]);
8229 PlayLevelSoundAction(x, y, ACTION_MOVING);
8233 ContinueMoving(x, y);
8236 void ContinueMoving(int x, int y)
8238 int element = Feld[x][y];
8239 struct ElementInfo *ei = &element_info[element];
8240 int direction = MovDir[x][y];
8241 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
8242 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
8243 int newx = x + dx, newy = y + dy;
8244 int stored = Store[x][y];
8245 int stored_new = Store[newx][newy];
8246 boolean pushed_by_player = (Pushed[x][y] && IS_PLAYER(x, y));
8247 boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
8248 boolean last_line = (newy == lev_fieldy - 1);
8250 MovPos[x][y] += getElementMoveStepsize(x, y);
8252 if (pushed_by_player) /* special case: moving object pushed by player */
8253 MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
8255 if (ABS(MovPos[x][y]) < TILEX)
8257 TEST_DrawLevelField(x, y);
8259 return; /* element is still moving */
8262 /* element reached destination field */
8264 Feld[x][y] = EL_EMPTY;
8265 Feld[newx][newy] = element;
8266 MovPos[x][y] = 0; /* force "not moving" for "crumbled sand" */
8268 if (Store[x][y] == EL_ACID) /* element is moving into acid pool */
8270 element = Feld[newx][newy] = EL_ACID;
8272 else if (element == EL_MOLE)
8274 Feld[x][y] = EL_SAND;
8276 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8278 else if (element == EL_QUICKSAND_FILLING)
8280 element = Feld[newx][newy] = get_next_element(element);
8281 Store[newx][newy] = Store[x][y];
8283 else if (element == EL_QUICKSAND_EMPTYING)
8285 Feld[x][y] = get_next_element(element);
8286 element = Feld[newx][newy] = Store[x][y];
8288 else if (element == EL_QUICKSAND_FAST_FILLING)
8290 element = Feld[newx][newy] = get_next_element(element);
8291 Store[newx][newy] = Store[x][y];
8293 else if (element == EL_QUICKSAND_FAST_EMPTYING)
8295 Feld[x][y] = get_next_element(element);
8296 element = Feld[newx][newy] = Store[x][y];
8298 else if (element == EL_MAGIC_WALL_FILLING)
8300 element = Feld[newx][newy] = get_next_element(element);
8301 if (!game.magic_wall_active)
8302 element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
8303 Store[newx][newy] = Store[x][y];
8305 else if (element == EL_MAGIC_WALL_EMPTYING)
8307 Feld[x][y] = get_next_element(element);
8308 if (!game.magic_wall_active)
8309 Feld[x][y] = EL_MAGIC_WALL_DEAD;
8310 element = Feld[newx][newy] = Store[x][y];
8312 InitField(newx, newy, FALSE);
8314 else if (element == EL_BD_MAGIC_WALL_FILLING)
8316 element = Feld[newx][newy] = get_next_element(element);
8317 if (!game.magic_wall_active)
8318 element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
8319 Store[newx][newy] = Store[x][y];
8321 else if (element == EL_BD_MAGIC_WALL_EMPTYING)
8323 Feld[x][y] = get_next_element(element);
8324 if (!game.magic_wall_active)
8325 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
8326 element = Feld[newx][newy] = Store[x][y];
8328 InitField(newx, newy, FALSE);
8330 else if (element == EL_DC_MAGIC_WALL_FILLING)
8332 element = Feld[newx][newy] = get_next_element(element);
8333 if (!game.magic_wall_active)
8334 element = Feld[newx][newy] = EL_DC_MAGIC_WALL_DEAD;
8335 Store[newx][newy] = Store[x][y];
8337 else if (element == EL_DC_MAGIC_WALL_EMPTYING)
8339 Feld[x][y] = get_next_element(element);
8340 if (!game.magic_wall_active)
8341 Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
8342 element = Feld[newx][newy] = Store[x][y];
8344 InitField(newx, newy, FALSE);
8346 else if (element == EL_AMOEBA_DROPPING)
8348 Feld[x][y] = get_next_element(element);
8349 element = Feld[newx][newy] = Store[x][y];
8351 else if (element == EL_SOKOBAN_OBJECT)
8354 Feld[x][y] = Back[x][y];
8356 if (Back[newx][newy])
8357 Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
8359 Back[x][y] = Back[newx][newy] = 0;
8362 Store[x][y] = EL_EMPTY;
8367 MovDelay[newx][newy] = 0;
8369 if (CAN_CHANGE_OR_HAS_ACTION(element))
8371 /* copy element change control values to new field */
8372 ChangeDelay[newx][newy] = ChangeDelay[x][y];
8373 ChangePage[newx][newy] = ChangePage[x][y];
8374 ChangeCount[newx][newy] = ChangeCount[x][y];
8375 ChangeEvent[newx][newy] = ChangeEvent[x][y];
8378 CustomValue[newx][newy] = CustomValue[x][y];
8380 ChangeDelay[x][y] = 0;
8381 ChangePage[x][y] = -1;
8382 ChangeCount[x][y] = 0;
8383 ChangeEvent[x][y] = -1;
8385 CustomValue[x][y] = 0;
8387 /* copy animation control values to new field */
8388 GfxFrame[newx][newy] = GfxFrame[x][y];
8389 GfxRandom[newx][newy] = GfxRandom[x][y]; /* keep same random value */
8390 GfxAction[newx][newy] = GfxAction[x][y]; /* keep action one frame */
8391 GfxDir[newx][newy] = GfxDir[x][y]; /* keep element direction */
8393 Pushed[x][y] = Pushed[newx][newy] = FALSE;
8395 /* some elements can leave other elements behind after moving */
8396 if (ei->move_leave_element != EL_EMPTY &&
8397 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
8398 (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
8400 int move_leave_element = ei->move_leave_element;
8402 /* this makes it possible to leave the removed element again */
8403 if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
8404 move_leave_element = (stored == EL_ACID ? EL_EMPTY : stored);
8406 Feld[x][y] = move_leave_element;
8408 if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
8409 MovDir[x][y] = direction;
8411 InitField(x, y, FALSE);
8413 if (GFX_CRUMBLED(Feld[x][y]))
8414 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8416 if (ELEM_IS_PLAYER(move_leave_element))
8417 RelocatePlayer(x, y, move_leave_element);
8420 /* do this after checking for left-behind element */
8421 ResetGfxAnimation(x, y); /* reset animation values for old field */
8423 if (!CAN_MOVE(element) ||
8424 (CAN_FALL(element) && direction == MV_DOWN &&
8425 (element == EL_SPRING ||
8426 element_info[element].move_pattern == MV_WHEN_PUSHED ||
8427 element_info[element].move_pattern == MV_WHEN_DROPPED)))
8428 GfxDir[x][y] = MovDir[newx][newy] = 0;
8430 TEST_DrawLevelField(x, y);
8431 TEST_DrawLevelField(newx, newy);
8433 Stop[newx][newy] = TRUE; /* ignore this element until the next frame */
8435 /* prevent pushed element from moving on in pushed direction */
8436 if (pushed_by_player && CAN_MOVE(element) &&
8437 element_info[element].move_pattern & MV_ANY_DIRECTION &&
8438 !(element_info[element].move_pattern & direction))
8439 TurnRound(newx, newy);
8441 /* prevent elements on conveyor belt from moving on in last direction */
8442 if (pushed_by_conveyor && CAN_FALL(element) &&
8443 direction & MV_HORIZONTAL)
8444 MovDir[newx][newy] = 0;
8446 if (!pushed_by_player)
8448 int nextx = newx + dx, nexty = newy + dy;
8449 boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
8451 WasJustMoving[newx][newy] = CHECK_DELAY_MOVING;
8453 if (CAN_FALL(element) && direction == MV_DOWN)
8454 WasJustFalling[newx][newy] = CHECK_DELAY_FALLING;
8456 if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
8457 CheckCollision[newx][newy] = CHECK_DELAY_COLLISION;
8459 if (CAN_FALL(element) && direction == MV_DOWN && check_collision_again)
8460 CheckImpact[newx][newy] = CHECK_DELAY_IMPACT;
8463 if (DONT_TOUCH(element)) /* object may be nasty to player or others */
8465 TestIfBadThingTouchesPlayer(newx, newy);
8466 TestIfBadThingTouchesFriend(newx, newy);
8468 if (!IS_CUSTOM_ELEMENT(element))
8469 TestIfBadThingTouchesOtherBadThing(newx, newy);
8471 else if (element == EL_PENGUIN)
8472 TestIfFriendTouchesBadThing(newx, newy);
8474 if (DONT_GET_HIT_BY(element))
8476 TestIfGoodThingGetsHitByBadThing(newx, newy, direction);
8479 /* give the player one last chance (one more frame) to move away */
8480 if (CAN_FALL(element) && direction == MV_DOWN &&
8481 (last_line || (!IS_FREE(x, newy + 1) &&
8482 (!IS_PLAYER(x, newy + 1) ||
8483 game.engine_version < VERSION_IDENT(3,1,1,0)))))
8486 if (pushed_by_player && !game.use_change_when_pushing_bug)
8488 int push_side = MV_DIR_OPPOSITE(direction);
8489 struct PlayerInfo *player = PLAYERINFO(x, y);
8491 CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
8492 player->index_bit, push_side);
8493 CheckTriggeredElementChangeByPlayer(newx,newy, element, CE_PLAYER_PUSHES_X,
8494 player->index_bit, push_side);
8497 if (element == EL_EMC_ANDROID && pushed_by_player) /* make another move */
8498 MovDelay[newx][newy] = 1;
8500 CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
8502 TestIfElementTouchesCustomElement(x, y); /* empty or new element */
8503 TestIfElementHitsCustomElement(newx, newy, direction);
8504 TestIfPlayerTouchesCustomElement(newx, newy);
8505 TestIfElementTouchesCustomElement(newx, newy);
8507 if (IS_CUSTOM_ELEMENT(element) && ei->move_enter_element != EL_EMPTY &&
8508 IS_EQUAL_OR_IN_GROUP(stored_new, ei->move_enter_element))
8509 CheckElementChangeBySide(newx, newy, element, stored_new, CE_DIGGING_X,
8510 MV_DIR_OPPOSITE(direction));
8513 int AmoebeNachbarNr(int ax, int ay)
8516 int element = Feld[ax][ay];
8518 static int xy[4][2] =
8526 for (i = 0; i < NUM_DIRECTIONS; i++)
8528 int x = ax + xy[i][0];
8529 int y = ay + xy[i][1];
8531 if (!IN_LEV_FIELD(x, y))
8534 if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
8535 group_nr = AmoebaNr[x][y];
8541 void AmoebenVereinigen(int ax, int ay)
8543 int i, x, y, xx, yy;
8544 int new_group_nr = AmoebaNr[ax][ay];
8545 static int xy[4][2] =
8553 if (new_group_nr == 0)
8556 for (i = 0; i < NUM_DIRECTIONS; i++)
8561 if (!IN_LEV_FIELD(x, y))
8564 if ((Feld[x][y] == EL_AMOEBA_FULL ||
8565 Feld[x][y] == EL_BD_AMOEBA ||
8566 Feld[x][y] == EL_AMOEBA_DEAD) &&
8567 AmoebaNr[x][y] != new_group_nr)
8569 int old_group_nr = AmoebaNr[x][y];
8571 if (old_group_nr == 0)
8574 AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
8575 AmoebaCnt[old_group_nr] = 0;
8576 AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
8577 AmoebaCnt2[old_group_nr] = 0;
8579 SCAN_PLAYFIELD(xx, yy)
8581 if (AmoebaNr[xx][yy] == old_group_nr)
8582 AmoebaNr[xx][yy] = new_group_nr;
8588 void AmoebeUmwandeln(int ax, int ay)
8592 if (Feld[ax][ay] == EL_AMOEBA_DEAD)
8594 int group_nr = AmoebaNr[ax][ay];
8599 printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
8600 printf("AmoebeUmwandeln(): This should never happen!\n");
8605 SCAN_PLAYFIELD(x, y)
8607 if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
8610 Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
8614 PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
8615 SND_AMOEBA_TURNING_TO_GEM :
8616 SND_AMOEBA_TURNING_TO_ROCK));
8621 static int xy[4][2] =
8629 for (i = 0; i < NUM_DIRECTIONS; i++)
8634 if (!IN_LEV_FIELD(x, y))
8637 if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
8639 PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
8640 SND_AMOEBA_TURNING_TO_GEM :
8641 SND_AMOEBA_TURNING_TO_ROCK));
8648 void AmoebeUmwandelnBD(int ax, int ay, int new_element)
8651 int group_nr = AmoebaNr[ax][ay];
8652 boolean done = FALSE;
8657 printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
8658 printf("AmoebeUmwandelnBD(): This should never happen!\n");
8663 SCAN_PLAYFIELD(x, y)
8665 if (AmoebaNr[x][y] == group_nr &&
8666 (Feld[x][y] == EL_AMOEBA_DEAD ||
8667 Feld[x][y] == EL_BD_AMOEBA ||
8668 Feld[x][y] == EL_AMOEBA_GROWING))
8671 Feld[x][y] = new_element;
8672 InitField(x, y, FALSE);
8673 TEST_DrawLevelField(x, y);
8679 PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
8680 SND_BD_AMOEBA_TURNING_TO_ROCK :
8681 SND_BD_AMOEBA_TURNING_TO_GEM));
8684 void AmoebeWaechst(int x, int y)
8686 static unsigned int sound_delay = 0;
8687 static unsigned int sound_delay_value = 0;
8689 if (!MovDelay[x][y]) /* start new growing cycle */
8693 if (DelayReached(&sound_delay, sound_delay_value))
8695 PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
8696 sound_delay_value = 30;
8700 if (MovDelay[x][y]) /* wait some time before growing bigger */
8703 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
8705 int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
8706 6 - MovDelay[x][y]);
8708 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
8711 if (!MovDelay[x][y])
8713 Feld[x][y] = Store[x][y];
8715 TEST_DrawLevelField(x, y);
8720 void AmoebaDisappearing(int x, int y)
8722 static unsigned int sound_delay = 0;
8723 static unsigned int sound_delay_value = 0;
8725 if (!MovDelay[x][y]) /* start new shrinking cycle */
8729 if (DelayReached(&sound_delay, sound_delay_value))
8730 sound_delay_value = 30;
8733 if (MovDelay[x][y]) /* wait some time before shrinking */
8736 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
8738 int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
8739 6 - MovDelay[x][y]);
8741 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
8744 if (!MovDelay[x][y])
8746 Feld[x][y] = EL_EMPTY;
8747 TEST_DrawLevelField(x, y);
8749 /* don't let mole enter this field in this cycle;
8750 (give priority to objects falling to this field from above) */
8756 void AmoebeAbleger(int ax, int ay)
8759 int element = Feld[ax][ay];
8760 int graphic = el2img(element);
8761 int newax = ax, neway = ay;
8762 boolean can_drop = (element == EL_AMOEBA_WET || element == EL_EMC_DRIPPER);
8763 static int xy[4][2] =
8771 if (!level.amoeba_speed && element != EL_EMC_DRIPPER)
8773 Feld[ax][ay] = EL_AMOEBA_DEAD;
8774 TEST_DrawLevelField(ax, ay);
8778 if (IS_ANIMATED(graphic))
8779 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
8781 if (!MovDelay[ax][ay]) /* start making new amoeba field */
8782 MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
8784 if (MovDelay[ax][ay]) /* wait some time before making new amoeba */
8787 if (MovDelay[ax][ay])
8791 if (can_drop) /* EL_AMOEBA_WET or EL_EMC_DRIPPER */
8794 int x = ax + xy[start][0];
8795 int y = ay + xy[start][1];
8797 if (!IN_LEV_FIELD(x, y))
8800 if (IS_FREE(x, y) ||
8801 CAN_GROW_INTO(Feld[x][y]) ||
8802 Feld[x][y] == EL_QUICKSAND_EMPTY ||
8803 Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
8809 if (newax == ax && neway == ay)
8812 else /* normal or "filled" (BD style) amoeba */
8815 boolean waiting_for_player = FALSE;
8817 for (i = 0; i < NUM_DIRECTIONS; i++)
8819 int j = (start + i) % 4;
8820 int x = ax + xy[j][0];
8821 int y = ay + xy[j][1];
8823 if (!IN_LEV_FIELD(x, y))
8826 if (IS_FREE(x, y) ||
8827 CAN_GROW_INTO(Feld[x][y]) ||
8828 Feld[x][y] == EL_QUICKSAND_EMPTY ||
8829 Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
8835 else if (IS_PLAYER(x, y))
8836 waiting_for_player = TRUE;
8839 if (newax == ax && neway == ay) /* amoeba cannot grow */
8841 if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
8843 Feld[ax][ay] = EL_AMOEBA_DEAD;
8844 TEST_DrawLevelField(ax, ay);
8845 AmoebaCnt[AmoebaNr[ax][ay]]--;
8847 if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0) /* amoeba is completely dead */
8849 if (element == EL_AMOEBA_FULL)
8850 AmoebeUmwandeln(ax, ay);
8851 else if (element == EL_BD_AMOEBA)
8852 AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
8857 else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
8859 /* amoeba gets larger by growing in some direction */
8861 int new_group_nr = AmoebaNr[ax][ay];
8864 if (new_group_nr == 0)
8866 printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
8867 printf("AmoebeAbleger(): This should never happen!\n");
8872 AmoebaNr[newax][neway] = new_group_nr;
8873 AmoebaCnt[new_group_nr]++;
8874 AmoebaCnt2[new_group_nr]++;
8876 /* if amoeba touches other amoeba(s) after growing, unify them */
8877 AmoebenVereinigen(newax, neway);
8879 if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
8881 AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
8887 if (!can_drop || neway < ay || !IS_FREE(newax, neway) ||
8888 (neway == lev_fieldy - 1 && newax != ax))
8890 Feld[newax][neway] = EL_AMOEBA_GROWING; /* creation of new amoeba */
8891 Store[newax][neway] = element;
8893 else if (neway == ay || element == EL_EMC_DRIPPER)
8895 Feld[newax][neway] = EL_AMOEBA_DROP; /* drop left/right of amoeba */
8897 PlayLevelSoundAction(newax, neway, ACTION_GROWING);
8901 InitMovingField(ax, ay, MV_DOWN); /* drop dripping from amoeba */
8902 Feld[ax][ay] = EL_AMOEBA_DROPPING;
8903 Store[ax][ay] = EL_AMOEBA_DROP;
8904 ContinueMoving(ax, ay);
8908 TEST_DrawLevelField(newax, neway);
8911 void Life(int ax, int ay)
8915 int element = Feld[ax][ay];
8916 int graphic = el2img(element);
8917 int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
8919 boolean changed = FALSE;
8921 if (IS_ANIMATED(graphic))
8922 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
8927 if (!MovDelay[ax][ay]) /* start new "game of life" cycle */
8928 MovDelay[ax][ay] = life_time;
8930 if (MovDelay[ax][ay]) /* wait some time before next cycle */
8933 if (MovDelay[ax][ay])
8937 for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
8939 int xx = ax+x1, yy = ay+y1;
8942 if (!IN_LEV_FIELD(xx, yy))
8945 for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
8947 int x = xx+x2, y = yy+y2;
8949 if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
8952 if (((Feld[x][y] == element ||
8953 (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y))) &&
8955 (IS_FREE(x, y) && Stop[x][y]))
8959 if (xx == ax && yy == ay) /* field in the middle */
8961 if (nachbarn < life_parameter[0] ||
8962 nachbarn > life_parameter[1])
8964 Feld[xx][yy] = EL_EMPTY;
8966 TEST_DrawLevelField(xx, yy);
8967 Stop[xx][yy] = TRUE;
8971 else if (IS_FREE(xx, yy) || CAN_GROW_INTO(Feld[xx][yy]))
8972 { /* free border field */
8973 if (nachbarn >= life_parameter[2] &&
8974 nachbarn <= life_parameter[3])
8976 Feld[xx][yy] = element;
8977 MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
8979 TEST_DrawLevelField(xx, yy);
8980 Stop[xx][yy] = TRUE;
8987 PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
8988 SND_GAME_OF_LIFE_GROWING);
8991 static void InitRobotWheel(int x, int y)
8993 ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
8996 static void RunRobotWheel(int x, int y)
8998 PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
9001 static void StopRobotWheel(int x, int y)
9003 if (ZX == x && ZY == y)
9007 game.robot_wheel_active = FALSE;
9011 static void InitTimegateWheel(int x, int y)
9013 ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
9016 static void RunTimegateWheel(int x, int y)
9018 PlayLevelSound(x, y, SND_CLASS_TIMEGATE_SWITCH_ACTIVE);
9021 static void InitMagicBallDelay(int x, int y)
9023 ChangeDelay[x][y] = (level.ball_time + 1) * 8 + 1;
9026 static void ActivateMagicBall(int bx, int by)
9030 if (level.ball_random)
9032 int pos_border = RND(8); /* select one of the eight border elements */
9033 int pos_content = (pos_border > 3 ? pos_border + 1 : pos_border);
9034 int xx = pos_content % 3;
9035 int yy = pos_content / 3;
9040 if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
9041 CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9045 for (y = by - 1; y <= by + 1; y++) for (x = bx - 1; x <= bx + 1; x++)
9047 int xx = x - bx + 1;
9048 int yy = y - by + 1;
9050 if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
9051 CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9055 game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
9058 void CheckExit(int x, int y)
9060 if (local_player->gems_still_needed > 0 ||
9061 local_player->sokobanfields_still_needed > 0 ||
9062 local_player->lights_still_needed > 0)
9064 int element = Feld[x][y];
9065 int graphic = el2img(element);
9067 if (IS_ANIMATED(graphic))
9068 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9073 if (AllPlayersGone) /* do not re-open exit door closed after last player */
9076 Feld[x][y] = EL_EXIT_OPENING;
9078 PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
9081 void CheckExitEM(int x, int y)
9083 if (local_player->gems_still_needed > 0 ||
9084 local_player->sokobanfields_still_needed > 0 ||
9085 local_player->lights_still_needed > 0)
9087 int element = Feld[x][y];
9088 int graphic = el2img(element);
9090 if (IS_ANIMATED(graphic))
9091 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9096 if (AllPlayersGone) /* do not re-open exit door closed after last player */
9099 Feld[x][y] = EL_EM_EXIT_OPENING;
9101 PlayLevelSoundNearest(x, y, SND_CLASS_EM_EXIT_OPENING);
9104 void CheckExitSteel(int x, int y)
9106 if (local_player->gems_still_needed > 0 ||
9107 local_player->sokobanfields_still_needed > 0 ||
9108 local_player->lights_still_needed > 0)
9110 int element = Feld[x][y];
9111 int graphic = el2img(element);
9113 if (IS_ANIMATED(graphic))
9114 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9119 if (AllPlayersGone) /* do not re-open exit door closed after last player */
9122 Feld[x][y] = EL_STEEL_EXIT_OPENING;
9124 PlayLevelSoundNearest(x, y, SND_CLASS_STEEL_EXIT_OPENING);
9127 void CheckExitSteelEM(int x, int y)
9129 if (local_player->gems_still_needed > 0 ||
9130 local_player->sokobanfields_still_needed > 0 ||
9131 local_player->lights_still_needed > 0)
9133 int element = Feld[x][y];
9134 int graphic = el2img(element);
9136 if (IS_ANIMATED(graphic))
9137 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9142 if (AllPlayersGone) /* do not re-open exit door closed after last player */
9145 Feld[x][y] = EL_EM_STEEL_EXIT_OPENING;
9147 PlayLevelSoundNearest(x, y, SND_CLASS_EM_STEEL_EXIT_OPENING);
9150 void CheckExitSP(int x, int y)
9152 if (local_player->gems_still_needed > 0)
9154 int element = Feld[x][y];
9155 int graphic = el2img(element);
9157 if (IS_ANIMATED(graphic))
9158 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9163 if (AllPlayersGone) /* do not re-open exit door closed after last player */
9166 Feld[x][y] = EL_SP_EXIT_OPENING;
9168 PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
9171 static void CloseAllOpenTimegates()
9175 SCAN_PLAYFIELD(x, y)
9177 int element = Feld[x][y];
9179 if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
9181 Feld[x][y] = EL_TIMEGATE_CLOSING;
9183 PlayLevelSoundAction(x, y, ACTION_CLOSING);
9188 void DrawTwinkleOnField(int x, int y)
9190 if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
9193 if (Feld[x][y] == EL_BD_DIAMOND)
9196 if (MovDelay[x][y] == 0) /* next animation frame */
9197 MovDelay[x][y] = 11 * !GetSimpleRandom(500);
9199 if (MovDelay[x][y] != 0) /* wait some time before next frame */
9203 DrawLevelElementAnimation(x, y, Feld[x][y]);
9205 if (MovDelay[x][y] != 0)
9207 int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
9208 10 - MovDelay[x][y]);
9210 DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
9215 void MauerWaechst(int x, int y)
9219 if (!MovDelay[x][y]) /* next animation frame */
9220 MovDelay[x][y] = 3 * delay;
9222 if (MovDelay[x][y]) /* wait some time before next frame */
9226 if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9228 int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
9229 int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
9231 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
9234 if (!MovDelay[x][y])
9236 if (MovDir[x][y] == MV_LEFT)
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_RIGHT)
9243 if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
9244 TEST_DrawLevelField(x + 1, y);
9246 else if (MovDir[x][y] == MV_UP)
9248 if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
9249 TEST_DrawLevelField(x, y - 1);
9253 if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
9254 TEST_DrawLevelField(x, y + 1);
9257 Feld[x][y] = Store[x][y];
9259 GfxDir[x][y] = MovDir[x][y] = MV_NONE;
9260 TEST_DrawLevelField(x, y);
9265 void MauerAbleger(int ax, int ay)
9267 int element = Feld[ax][ay];
9268 int graphic = el2img(element);
9269 boolean oben_frei = FALSE, unten_frei = FALSE;
9270 boolean links_frei = FALSE, rechts_frei = FALSE;
9271 boolean oben_massiv = FALSE, unten_massiv = FALSE;
9272 boolean links_massiv = FALSE, rechts_massiv = FALSE;
9273 boolean new_wall = FALSE;
9275 if (IS_ANIMATED(graphic))
9276 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9278 if (!MovDelay[ax][ay]) /* start building new wall */
9279 MovDelay[ax][ay] = 6;
9281 if (MovDelay[ax][ay]) /* wait some time before building new wall */
9284 if (MovDelay[ax][ay])
9288 if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9290 if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9292 if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9294 if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9297 if (element == EL_EXPANDABLE_WALL_VERTICAL ||
9298 element == EL_EXPANDABLE_WALL_ANY)
9302 Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
9303 Store[ax][ay-1] = element;
9304 GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9305 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9306 DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9307 IMG_EXPANDABLE_WALL_GROWING_UP, 0);
9312 Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
9313 Store[ax][ay+1] = element;
9314 GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9315 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9316 DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9317 IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
9322 if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9323 element == EL_EXPANDABLE_WALL_ANY ||
9324 element == EL_EXPANDABLE_WALL ||
9325 element == EL_BD_EXPANDABLE_WALL)
9329 Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
9330 Store[ax-1][ay] = element;
9331 GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9332 if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9333 DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9334 IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
9340 Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
9341 Store[ax+1][ay] = element;
9342 GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9343 if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9344 DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9345 IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
9350 if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
9351 TEST_DrawLevelField(ax, ay);
9353 if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
9355 if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
9356 unten_massiv = TRUE;
9357 if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
9358 links_massiv = TRUE;
9359 if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
9360 rechts_massiv = TRUE;
9362 if (((oben_massiv && unten_massiv) ||
9363 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9364 element == EL_EXPANDABLE_WALL) &&
9365 ((links_massiv && rechts_massiv) ||
9366 element == EL_EXPANDABLE_WALL_VERTICAL))
9367 Feld[ax][ay] = EL_WALL;
9370 PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9373 void MauerAblegerStahl(int ax, int ay)
9375 int element = Feld[ax][ay];
9376 int graphic = el2img(element);
9377 boolean oben_frei = FALSE, unten_frei = FALSE;
9378 boolean links_frei = FALSE, rechts_frei = FALSE;
9379 boolean oben_massiv = FALSE, unten_massiv = FALSE;
9380 boolean links_massiv = FALSE, rechts_massiv = FALSE;
9381 boolean new_wall = FALSE;
9383 if (IS_ANIMATED(graphic))
9384 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9386 if (!MovDelay[ax][ay]) /* start building new wall */
9387 MovDelay[ax][ay] = 6;
9389 if (MovDelay[ax][ay]) /* wait some time before building new wall */
9392 if (MovDelay[ax][ay])
9396 if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9398 if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9400 if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9402 if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9405 if (element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
9406 element == EL_EXPANDABLE_STEELWALL_ANY)
9410 Feld[ax][ay-1] = EL_EXPANDABLE_STEELWALL_GROWING;
9411 Store[ax][ay-1] = element;
9412 GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9413 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9414 DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9415 IMG_EXPANDABLE_STEELWALL_GROWING_UP, 0);
9420 Feld[ax][ay+1] = EL_EXPANDABLE_STEELWALL_GROWING;
9421 Store[ax][ay+1] = element;
9422 GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9423 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9424 DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9425 IMG_EXPANDABLE_STEELWALL_GROWING_DOWN, 0);
9430 if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
9431 element == EL_EXPANDABLE_STEELWALL_ANY)
9435 Feld[ax-1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9436 Store[ax-1][ay] = element;
9437 GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9438 if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9439 DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9440 IMG_EXPANDABLE_STEELWALL_GROWING_LEFT, 0);
9446 Feld[ax+1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9447 Store[ax+1][ay] = element;
9448 GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9449 if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9450 DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9451 IMG_EXPANDABLE_STEELWALL_GROWING_RIGHT, 0);
9456 if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
9458 if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
9459 unten_massiv = TRUE;
9460 if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
9461 links_massiv = TRUE;
9462 if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
9463 rechts_massiv = TRUE;
9465 if (((oben_massiv && unten_massiv) ||
9466 element == EL_EXPANDABLE_STEELWALL_HORIZONTAL) &&
9467 ((links_massiv && rechts_massiv) ||
9468 element == EL_EXPANDABLE_STEELWALL_VERTICAL))
9469 Feld[ax][ay] = EL_STEELWALL;
9472 PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9475 void CheckForDragon(int x, int y)
9478 boolean dragon_found = FALSE;
9479 static int xy[4][2] =
9487 for (i = 0; i < NUM_DIRECTIONS; i++)
9489 for (j = 0; j < 4; j++)
9491 int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9493 if (IN_LEV_FIELD(xx, yy) &&
9494 (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
9496 if (Feld[xx][yy] == EL_DRAGON)
9497 dragon_found = TRUE;
9506 for (i = 0; i < NUM_DIRECTIONS; i++)
9508 for (j = 0; j < 3; j++)
9510 int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9512 if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
9514 Feld[xx][yy] = EL_EMPTY;
9515 TEST_DrawLevelField(xx, yy);
9524 static void InitBuggyBase(int x, int y)
9526 int element = Feld[x][y];
9527 int activating_delay = FRAMES_PER_SECOND / 4;
9530 (element == EL_SP_BUGGY_BASE ?
9531 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
9532 element == EL_SP_BUGGY_BASE_ACTIVATING ?
9534 element == EL_SP_BUGGY_BASE_ACTIVE ?
9535 1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
9538 static void WarnBuggyBase(int x, int y)
9541 static int xy[4][2] =
9549 for (i = 0; i < NUM_DIRECTIONS; i++)
9551 int xx = x + xy[i][0];
9552 int yy = y + xy[i][1];
9554 if (IN_LEV_FIELD(xx, yy) && IS_PLAYER(xx, yy))
9556 PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
9563 static void InitTrap(int x, int y)
9565 ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
9568 static void ActivateTrap(int x, int y)
9570 PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
9573 static void ChangeActiveTrap(int x, int y)
9575 int graphic = IMG_TRAP_ACTIVE;
9577 /* if new animation frame was drawn, correct crumbled sand border */
9578 if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
9579 TEST_DrawLevelFieldCrumbled(x, y);
9582 static int getSpecialActionElement(int element, int number, int base_element)
9584 return (element != EL_EMPTY ? element :
9585 number != -1 ? base_element + number - 1 :
9589 static int getModifiedActionNumber(int value_old, int operator, int operand,
9590 int value_min, int value_max)
9592 int value_new = (operator == CA_MODE_SET ? operand :
9593 operator == CA_MODE_ADD ? value_old + operand :
9594 operator == CA_MODE_SUBTRACT ? value_old - operand :
9595 operator == CA_MODE_MULTIPLY ? value_old * operand :
9596 operator == CA_MODE_DIVIDE ? value_old / MAX(1, operand) :
9597 operator == CA_MODE_MODULO ? value_old % MAX(1, operand) :
9600 return (value_new < value_min ? value_min :
9601 value_new > value_max ? value_max :
9605 static void ExecuteCustomElementAction(int x, int y, int element, int page)
9607 struct ElementInfo *ei = &element_info[element];
9608 struct ElementChangeInfo *change = &ei->change_page[page];
9609 int target_element = change->target_element;
9610 int action_type = change->action_type;
9611 int action_mode = change->action_mode;
9612 int action_arg = change->action_arg;
9613 int action_element = change->action_element;
9616 if (!change->has_action)
9619 /* ---------- determine action paramater values -------------------------- */
9621 int level_time_value =
9622 (level.time > 0 ? TimeLeft :
9625 int action_arg_element_raw =
9626 (action_arg == CA_ARG_PLAYER_TRIGGER ? change->actual_trigger_player :
9627 action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
9628 action_arg == CA_ARG_ELEMENT_TARGET ? change->target_element :
9629 action_arg == CA_ARG_ELEMENT_ACTION ? change->action_element :
9630 action_arg == CA_ARG_INVENTORY_RM_TRIGGER ? change->actual_trigger_element:
9631 action_arg == CA_ARG_INVENTORY_RM_TARGET ? change->target_element :
9632 action_arg == CA_ARG_INVENTORY_RM_ACTION ? change->action_element :
9634 int action_arg_element = GetElementFromGroupElement(action_arg_element_raw);
9636 int action_arg_direction =
9637 (action_arg >= CA_ARG_DIRECTION_LEFT &&
9638 action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
9639 action_arg == CA_ARG_DIRECTION_TRIGGER ?
9640 change->actual_trigger_side :
9641 action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
9642 MV_DIR_OPPOSITE(change->actual_trigger_side) :
9645 int action_arg_number_min =
9646 (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
9649 int action_arg_number_max =
9650 (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
9651 action_type == CA_SET_LEVEL_GEMS ? 999 :
9652 action_type == CA_SET_LEVEL_TIME ? 9999 :
9653 action_type == CA_SET_LEVEL_SCORE ? 99999 :
9654 action_type == CA_SET_CE_VALUE ? 9999 :
9655 action_type == CA_SET_CE_SCORE ? 9999 :
9658 int action_arg_number_reset =
9659 (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize[0] :
9660 action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
9661 action_type == CA_SET_LEVEL_TIME ? level.time :
9662 action_type == CA_SET_LEVEL_SCORE ? 0 :
9663 action_type == CA_SET_CE_VALUE ? GET_NEW_CE_VALUE(element) :
9664 action_type == CA_SET_CE_SCORE ? 0 :
9667 int action_arg_number =
9668 (action_arg <= CA_ARG_MAX ? action_arg :
9669 action_arg >= CA_ARG_SPEED_NOT_MOVING &&
9670 action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
9671 action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
9672 action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
9673 action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
9674 action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
9675 action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
9676 action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
9677 action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
9678 action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
9679 action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? local_player->gems_still_needed :
9680 action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? local_player->score :
9681 action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CE_VALUE(target_element):
9682 action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
9683 action_arg == CA_ARG_ELEMENT_CV_ACTION ? GET_NEW_CE_VALUE(action_element):
9684 action_arg == CA_ARG_ELEMENT_CS_TARGET ? GET_CE_SCORE(target_element) :
9685 action_arg == CA_ARG_ELEMENT_CS_TRIGGER ? change->actual_trigger_ce_score:
9686 action_arg == CA_ARG_ELEMENT_CS_ACTION ? GET_CE_SCORE(action_element) :
9687 action_arg == CA_ARG_ELEMENT_NR_TARGET ? change->target_element :
9688 action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
9689 action_arg == CA_ARG_ELEMENT_NR_ACTION ? change->action_element :
9692 int action_arg_number_old =
9693 (action_type == CA_SET_LEVEL_GEMS ? local_player->gems_still_needed :
9694 action_type == CA_SET_LEVEL_TIME ? TimeLeft :
9695 action_type == CA_SET_LEVEL_SCORE ? local_player->score :
9696 action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
9697 action_type == CA_SET_CE_SCORE ? ei->collect_score :
9700 int action_arg_number_new =
9701 getModifiedActionNumber(action_arg_number_old,
9702 action_mode, action_arg_number,
9703 action_arg_number_min, action_arg_number_max);
9705 int trigger_player_bits =
9706 (change->actual_trigger_player_bits != CH_PLAYER_NONE ?
9707 change->actual_trigger_player_bits : change->trigger_player);
9709 int action_arg_player_bits =
9710 (action_arg >= CA_ARG_PLAYER_1 &&
9711 action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
9712 action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
9713 action_arg == CA_ARG_PLAYER_ACTION ? 1 << GET_PLAYER_NR(action_element) :
9716 /* ---------- execute action -------------------------------------------- */
9718 switch (action_type)
9725 /* ---------- level actions ------------------------------------------- */
9727 case CA_RESTART_LEVEL:
9729 game.restart_level = TRUE;
9734 case CA_SHOW_ENVELOPE:
9736 int element = getSpecialActionElement(action_arg_element,
9737 action_arg_number, EL_ENVELOPE_1);
9739 if (IS_ENVELOPE(element))
9740 local_player->show_envelope = element;
9745 case CA_SET_LEVEL_TIME:
9747 if (level.time > 0) /* only modify limited time value */
9749 TimeLeft = action_arg_number_new;
9751 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
9753 DisplayGameControlValues();
9755 if (!TimeLeft && setup.time_limit)
9756 for (i = 0; i < MAX_PLAYERS; i++)
9757 KillPlayer(&stored_player[i]);
9763 case CA_SET_LEVEL_SCORE:
9765 local_player->score = action_arg_number_new;
9767 game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
9769 DisplayGameControlValues();
9774 case CA_SET_LEVEL_GEMS:
9776 local_player->gems_still_needed = action_arg_number_new;
9778 game.snapshot.collected_item = TRUE;
9780 game_panel_controls[GAME_PANEL_GEMS].value =
9781 local_player->gems_still_needed;
9783 DisplayGameControlValues();
9788 case CA_SET_LEVEL_WIND:
9790 game.wind_direction = action_arg_direction;
9795 case CA_SET_LEVEL_RANDOM_SEED:
9797 /* ensure that setting a new random seed while playing is predictable */
9798 InitRND(action_arg_number_new ? action_arg_number_new : RND(1000000) + 1);
9803 /* ---------- player actions ------------------------------------------ */
9805 case CA_MOVE_PLAYER:
9807 /* automatically move to the next field in specified direction */
9808 for (i = 0; i < MAX_PLAYERS; i++)
9809 if (trigger_player_bits & (1 << i))
9810 stored_player[i].programmed_action = action_arg_direction;
9815 case CA_EXIT_PLAYER:
9817 for (i = 0; i < MAX_PLAYERS; i++)
9818 if (action_arg_player_bits & (1 << i))
9819 PlayerWins(&stored_player[i]);
9824 case CA_KILL_PLAYER:
9826 for (i = 0; i < MAX_PLAYERS; i++)
9827 if (action_arg_player_bits & (1 << i))
9828 KillPlayer(&stored_player[i]);
9833 case CA_SET_PLAYER_KEYS:
9835 int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
9836 int element = getSpecialActionElement(action_arg_element,
9837 action_arg_number, EL_KEY_1);
9839 if (IS_KEY(element))
9841 for (i = 0; i < MAX_PLAYERS; i++)
9843 if (trigger_player_bits & (1 << i))
9845 stored_player[i].key[KEY_NR(element)] = key_state;
9847 DrawGameDoorValues();
9855 case CA_SET_PLAYER_SPEED:
9857 for (i = 0; i < MAX_PLAYERS; i++)
9859 if (trigger_player_bits & (1 << i))
9861 int move_stepsize = TILEX / stored_player[i].move_delay_value;
9863 if (action_arg == CA_ARG_SPEED_FASTER &&
9864 stored_player[i].cannot_move)
9866 action_arg_number = STEPSIZE_VERY_SLOW;
9868 else if (action_arg == CA_ARG_SPEED_SLOWER ||
9869 action_arg == CA_ARG_SPEED_FASTER)
9871 action_arg_number = 2;
9872 action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
9875 else if (action_arg == CA_ARG_NUMBER_RESET)
9877 action_arg_number = level.initial_player_stepsize[i];
9881 getModifiedActionNumber(move_stepsize,
9884 action_arg_number_min,
9885 action_arg_number_max);
9887 SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
9894 case CA_SET_PLAYER_SHIELD:
9896 for (i = 0; i < MAX_PLAYERS; i++)
9898 if (trigger_player_bits & (1 << i))
9900 if (action_arg == CA_ARG_SHIELD_OFF)
9902 stored_player[i].shield_normal_time_left = 0;
9903 stored_player[i].shield_deadly_time_left = 0;
9905 else if (action_arg == CA_ARG_SHIELD_NORMAL)
9907 stored_player[i].shield_normal_time_left = 999999;
9909 else if (action_arg == CA_ARG_SHIELD_DEADLY)
9911 stored_player[i].shield_normal_time_left = 999999;
9912 stored_player[i].shield_deadly_time_left = 999999;
9920 case CA_SET_PLAYER_GRAVITY:
9922 for (i = 0; i < MAX_PLAYERS; i++)
9924 if (trigger_player_bits & (1 << i))
9926 stored_player[i].gravity =
9927 (action_arg == CA_ARG_GRAVITY_OFF ? FALSE :
9928 action_arg == CA_ARG_GRAVITY_ON ? TRUE :
9929 action_arg == CA_ARG_GRAVITY_TOGGLE ? !stored_player[i].gravity :
9930 stored_player[i].gravity);
9937 case CA_SET_PLAYER_ARTWORK:
9939 for (i = 0; i < MAX_PLAYERS; i++)
9941 if (trigger_player_bits & (1 << i))
9943 int artwork_element = action_arg_element;
9945 if (action_arg == CA_ARG_ELEMENT_RESET)
9947 (level.use_artwork_element[i] ? level.artwork_element[i] :
9948 stored_player[i].element_nr);
9950 if (stored_player[i].artwork_element != artwork_element)
9951 stored_player[i].Frame = 0;
9953 stored_player[i].artwork_element = artwork_element;
9955 SetPlayerWaiting(&stored_player[i], FALSE);
9957 /* set number of special actions for bored and sleeping animation */
9958 stored_player[i].num_special_action_bored =
9959 get_num_special_action(artwork_element,
9960 ACTION_BORING_1, ACTION_BORING_LAST);
9961 stored_player[i].num_special_action_sleeping =
9962 get_num_special_action(artwork_element,
9963 ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
9970 case CA_SET_PLAYER_INVENTORY:
9972 for (i = 0; i < MAX_PLAYERS; i++)
9974 struct PlayerInfo *player = &stored_player[i];
9977 if (trigger_player_bits & (1 << i))
9979 int inventory_element = action_arg_element;
9981 if (action_arg == CA_ARG_ELEMENT_TARGET ||
9982 action_arg == CA_ARG_ELEMENT_TRIGGER ||
9983 action_arg == CA_ARG_ELEMENT_ACTION)
9985 int element = inventory_element;
9986 int collect_count = element_info[element].collect_count_initial;
9988 if (!IS_CUSTOM_ELEMENT(element))
9991 if (collect_count == 0)
9992 player->inventory_infinite_element = element;
9994 for (k = 0; k < collect_count; k++)
9995 if (player->inventory_size < MAX_INVENTORY_SIZE)
9996 player->inventory_element[player->inventory_size++] =
9999 else if (action_arg == CA_ARG_INVENTORY_RM_TARGET ||
10000 action_arg == CA_ARG_INVENTORY_RM_TRIGGER ||
10001 action_arg == CA_ARG_INVENTORY_RM_ACTION)
10003 if (player->inventory_infinite_element != EL_UNDEFINED &&
10004 IS_EQUAL_OR_IN_GROUP(player->inventory_infinite_element,
10005 action_arg_element_raw))
10006 player->inventory_infinite_element = EL_UNDEFINED;
10008 for (k = 0, j = 0; j < player->inventory_size; j++)
10010 if (!IS_EQUAL_OR_IN_GROUP(player->inventory_element[j],
10011 action_arg_element_raw))
10012 player->inventory_element[k++] = player->inventory_element[j];
10015 player->inventory_size = k;
10017 else if (action_arg == CA_ARG_INVENTORY_RM_FIRST)
10019 if (player->inventory_size > 0)
10021 for (j = 0; j < player->inventory_size - 1; j++)
10022 player->inventory_element[j] = player->inventory_element[j + 1];
10024 player->inventory_size--;
10027 else if (action_arg == CA_ARG_INVENTORY_RM_LAST)
10029 if (player->inventory_size > 0)
10030 player->inventory_size--;
10032 else if (action_arg == CA_ARG_INVENTORY_RM_ALL)
10034 player->inventory_infinite_element = EL_UNDEFINED;
10035 player->inventory_size = 0;
10037 else if (action_arg == CA_ARG_INVENTORY_RESET)
10039 player->inventory_infinite_element = EL_UNDEFINED;
10040 player->inventory_size = 0;
10042 if (level.use_initial_inventory[i])
10044 for (j = 0; j < level.initial_inventory_size[i]; j++)
10046 int element = level.initial_inventory_content[i][j];
10047 int collect_count = element_info[element].collect_count_initial;
10049 if (!IS_CUSTOM_ELEMENT(element))
10052 if (collect_count == 0)
10053 player->inventory_infinite_element = element;
10055 for (k = 0; k < collect_count; k++)
10056 if (player->inventory_size < MAX_INVENTORY_SIZE)
10057 player->inventory_element[player->inventory_size++] =
10068 /* ---------- CE actions ---------------------------------------------- */
10070 case CA_SET_CE_VALUE:
10072 int last_ce_value = CustomValue[x][y];
10074 CustomValue[x][y] = action_arg_number_new;
10076 if (CustomValue[x][y] != last_ce_value)
10078 CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_CHANGES);
10079 CheckTriggeredElementChange(x, y, element, CE_VALUE_CHANGES_OF_X);
10081 if (CustomValue[x][y] == 0)
10083 CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
10084 CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
10091 case CA_SET_CE_SCORE:
10093 int last_ce_score = ei->collect_score;
10095 ei->collect_score = action_arg_number_new;
10097 if (ei->collect_score != last_ce_score)
10099 CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_CHANGES);
10100 CheckTriggeredElementChange(x, y, element, CE_SCORE_CHANGES_OF_X);
10102 if (ei->collect_score == 0)
10106 CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_GETS_ZERO);
10107 CheckTriggeredElementChange(x, y, element, CE_SCORE_GETS_ZERO_OF_X);
10110 This is a very special case that seems to be a mixture between
10111 CheckElementChange() and CheckTriggeredElementChange(): while
10112 the first one only affects single elements that are triggered
10113 directly, the second one affects multiple elements in the playfield
10114 that are triggered indirectly by another element. This is a third
10115 case: Changing the CE score always affects multiple identical CEs,
10116 so every affected CE must be checked, not only the single CE for
10117 which the CE score was changed in the first place (as every instance
10118 of that CE shares the same CE score, and therefore also can change)!
10120 SCAN_PLAYFIELD(xx, yy)
10122 if (Feld[xx][yy] == element)
10123 CheckElementChange(xx, yy, element, EL_UNDEFINED,
10124 CE_SCORE_GETS_ZERO);
10132 case CA_SET_CE_ARTWORK:
10134 int artwork_element = action_arg_element;
10135 boolean reset_frame = FALSE;
10138 if (action_arg == CA_ARG_ELEMENT_RESET)
10139 artwork_element = (ei->use_gfx_element ? ei->gfx_element_initial :
10142 if (ei->gfx_element != artwork_element)
10143 reset_frame = TRUE;
10145 ei->gfx_element = artwork_element;
10147 SCAN_PLAYFIELD(xx, yy)
10149 if (Feld[xx][yy] == element)
10153 ResetGfxAnimation(xx, yy);
10154 ResetRandomAnimationValue(xx, yy);
10157 TEST_DrawLevelField(xx, yy);
10164 /* ---------- engine actions ------------------------------------------ */
10166 case CA_SET_ENGINE_SCAN_MODE:
10168 InitPlayfieldScanMode(action_arg);
10178 static void CreateFieldExt(int x, int y, int element, boolean is_change)
10180 int old_element = Feld[x][y];
10181 int new_element = GetElementFromGroupElement(element);
10182 int previous_move_direction = MovDir[x][y];
10183 int last_ce_value = CustomValue[x][y];
10184 boolean player_explosion_protected = PLAYER_EXPLOSION_PROTECTED(x, y);
10185 boolean new_element_is_player = ELEM_IS_PLAYER(new_element);
10186 boolean add_player_onto_element = (new_element_is_player &&
10187 new_element != EL_SOKOBAN_FIELD_PLAYER &&
10188 IS_WALKABLE(old_element));
10190 if (!add_player_onto_element)
10192 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
10193 RemoveMovingField(x, y);
10197 Feld[x][y] = new_element;
10199 if (element_info[new_element].move_direction_initial == MV_START_PREVIOUS)
10200 MovDir[x][y] = previous_move_direction;
10202 if (element_info[new_element].use_last_ce_value)
10203 CustomValue[x][y] = last_ce_value;
10205 InitField_WithBug1(x, y, FALSE);
10207 new_element = Feld[x][y]; /* element may have changed */
10209 ResetGfxAnimation(x, y);
10210 ResetRandomAnimationValue(x, y);
10212 TEST_DrawLevelField(x, y);
10214 if (GFX_CRUMBLED(new_element))
10215 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
10218 /* check if element under the player changes from accessible to unaccessible
10219 (needed for special case of dropping element which then changes) */
10220 /* (must be checked after creating new element for walkable group elements) */
10221 if (IS_PLAYER(x, y) && !player_explosion_protected &&
10222 IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
10229 /* "ChangeCount" not set yet to allow "entered by player" change one time */
10230 if (new_element_is_player)
10231 RelocatePlayer(x, y, new_element);
10234 ChangeCount[x][y]++; /* count number of changes in the same frame */
10236 TestIfBadThingTouchesPlayer(x, y);
10237 TestIfPlayerTouchesCustomElement(x, y);
10238 TestIfElementTouchesCustomElement(x, y);
10241 static void CreateField(int x, int y, int element)
10243 CreateFieldExt(x, y, element, FALSE);
10246 static void CreateElementFromChange(int x, int y, int element)
10248 element = GET_VALID_RUNTIME_ELEMENT(element);
10250 if (game.engine_version >= VERSION_IDENT(3,2,0,7))
10252 int old_element = Feld[x][y];
10254 /* prevent changed element from moving in same engine frame
10255 unless both old and new element can either fall or move */
10256 if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
10257 (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
10261 CreateFieldExt(x, y, element, TRUE);
10264 static boolean ChangeElement(int x, int y, int element, int page)
10266 struct ElementInfo *ei = &element_info[element];
10267 struct ElementChangeInfo *change = &ei->change_page[page];
10268 int ce_value = CustomValue[x][y];
10269 int ce_score = ei->collect_score;
10270 int target_element;
10271 int old_element = Feld[x][y];
10273 /* always use default change event to prevent running into a loop */
10274 if (ChangeEvent[x][y] == -1)
10275 ChangeEvent[x][y] = CE_DELAY;
10277 if (ChangeEvent[x][y] == CE_DELAY)
10279 /* reset actual trigger element, trigger player and action element */
10280 change->actual_trigger_element = EL_EMPTY;
10281 change->actual_trigger_player = EL_EMPTY;
10282 change->actual_trigger_player_bits = CH_PLAYER_NONE;
10283 change->actual_trigger_side = CH_SIDE_NONE;
10284 change->actual_trigger_ce_value = 0;
10285 change->actual_trigger_ce_score = 0;
10288 /* do not change elements more than a specified maximum number of changes */
10289 if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
10292 ChangeCount[x][y]++; /* count number of changes in the same frame */
10294 if (change->explode)
10301 if (change->use_target_content)
10303 boolean complete_replace = TRUE;
10304 boolean can_replace[3][3];
10307 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10310 boolean is_walkable;
10311 boolean is_diggable;
10312 boolean is_collectible;
10313 boolean is_removable;
10314 boolean is_destructible;
10315 int ex = x + xx - 1;
10316 int ey = y + yy - 1;
10317 int content_element = change->target_content.e[xx][yy];
10320 can_replace[xx][yy] = TRUE;
10322 if (ex == x && ey == y) /* do not check changing element itself */
10325 if (content_element == EL_EMPTY_SPACE)
10327 can_replace[xx][yy] = FALSE; /* do not replace border with space */
10332 if (!IN_LEV_FIELD(ex, ey))
10334 can_replace[xx][yy] = FALSE;
10335 complete_replace = FALSE;
10342 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10343 e = MovingOrBlocked2Element(ex, ey);
10345 is_empty = (IS_FREE(ex, ey) ||
10346 (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
10348 is_walkable = (is_empty || IS_WALKABLE(e));
10349 is_diggable = (is_empty || IS_DIGGABLE(e));
10350 is_collectible = (is_empty || IS_COLLECTIBLE(e));
10351 is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
10352 is_removable = (is_diggable || is_collectible);
10354 can_replace[xx][yy] =
10355 (((change->replace_when == CP_WHEN_EMPTY && is_empty) ||
10356 (change->replace_when == CP_WHEN_WALKABLE && is_walkable) ||
10357 (change->replace_when == CP_WHEN_DIGGABLE && is_diggable) ||
10358 (change->replace_when == CP_WHEN_COLLECTIBLE && is_collectible) ||
10359 (change->replace_when == CP_WHEN_REMOVABLE && is_removable) ||
10360 (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
10361 !(IS_PLAYER(ex, ey) && ELEM_IS_PLAYER(content_element)));
10363 if (!can_replace[xx][yy])
10364 complete_replace = FALSE;
10367 if (!change->only_if_complete || complete_replace)
10369 boolean something_has_changed = FALSE;
10371 if (change->only_if_complete && change->use_random_replace &&
10372 RND(100) < change->random_percentage)
10375 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10377 int ex = x + xx - 1;
10378 int ey = y + yy - 1;
10379 int content_element;
10381 if (can_replace[xx][yy] && (!change->use_random_replace ||
10382 RND(100) < change->random_percentage))
10384 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10385 RemoveMovingField(ex, ey);
10387 ChangeEvent[ex][ey] = ChangeEvent[x][y];
10389 content_element = change->target_content.e[xx][yy];
10390 target_element = GET_TARGET_ELEMENT(element, content_element, change,
10391 ce_value, ce_score);
10393 CreateElementFromChange(ex, ey, target_element);
10395 something_has_changed = TRUE;
10397 /* for symmetry reasons, freeze newly created border elements */
10398 if (ex != x || ey != y)
10399 Stop[ex][ey] = TRUE; /* no more moving in this frame */
10403 if (something_has_changed)
10405 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10406 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10412 target_element = GET_TARGET_ELEMENT(element, change->target_element, change,
10413 ce_value, ce_score);
10415 if (element == EL_DIAGONAL_GROWING ||
10416 element == EL_DIAGONAL_SHRINKING)
10418 target_element = Store[x][y];
10420 Store[x][y] = EL_EMPTY;
10423 CreateElementFromChange(x, y, target_element);
10425 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10426 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10429 /* this uses direct change before indirect change */
10430 CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
10435 static void HandleElementChange(int x, int y, int page)
10437 int element = MovingOrBlocked2Element(x, y);
10438 struct ElementInfo *ei = &element_info[element];
10439 struct ElementChangeInfo *change = &ei->change_page[page];
10440 boolean handle_action_before_change = FALSE;
10443 if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
10444 !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
10447 printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
10448 x, y, element, element_info[element].token_name);
10449 printf("HandleElementChange(): This should never happen!\n");
10454 /* this can happen with classic bombs on walkable, changing elements */
10455 if (!CAN_CHANGE_OR_HAS_ACTION(element))
10460 if (ChangeDelay[x][y] == 0) /* initialize element change */
10462 ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
10464 if (change->can_change)
10466 /* !!! not clear why graphic animation should be reset at all here !!! */
10467 /* !!! UPDATE: but is needed for correct Snake Bite tail animation !!! */
10468 /* !!! SOLUTION: do not reset if graphics engine set to 4 or above !!! */
10471 GRAPHICAL BUG ADDRESSED BY CHECKING GRAPHICS ENGINE VERSION:
10473 When using an animation frame delay of 1 (this only happens with
10474 "sp_zonk.moving.left/right" in the classic graphics), the default
10475 (non-moving) animation shows wrong animation frames (while the
10476 moving animation, like "sp_zonk.moving.left/right", is correct,
10477 so this graphical bug never shows up with the classic graphics).
10478 For an animation with 4 frames, this causes wrong frames 0,0,1,2
10479 be drawn instead of the correct frames 0,1,2,3. This is caused by
10480 "GfxFrame[][]" being reset *twice* (in two successive frames) after
10481 an element change: First when the change delay ("ChangeDelay[][]")
10482 counter has reached zero after decrementing, then a second time in
10483 the next frame (after "GfxFrame[][]" was already incremented) when
10484 "ChangeDelay[][]" is reset to the initial delay value again.
10486 This causes frame 0 to be drawn twice, while the last frame won't
10487 be drawn anymore, resulting in the wrong frame sequence 0,0,1,2.
10489 As some animations may already be cleverly designed around this bug
10490 (at least the "Snake Bite" snake tail animation does this), it cannot
10491 simply be fixed here without breaking such existing animations.
10492 Unfortunately, it cannot easily be detected if a graphics set was
10493 designed "before" or "after" the bug was fixed. As a workaround,
10494 a new graphics set option "game.graphics_engine_version" was added
10495 to be able to specify the game's major release version for which the
10496 graphics set was designed, which can then be used to decide if the
10497 bugfix should be used (version 4 and above) or not (version 3 or
10498 below, or if no version was specified at all, as with old sets).
10500 (The wrong/fixed animation frames can be tested with the test level set
10501 "test_gfxframe" and level "000", which contains a specially prepared
10502 custom element at level position (x/y) == (11/9) which uses the zonk
10503 animation mentioned above. Using "game.graphics_engine_version: 4"
10504 fixes the wrong animation frames, showing the correct frames 0,1,2,3.
10505 This can also be seen from the debug output for this test element.)
10508 /* when a custom element is about to change (for example by change delay),
10509 do not reset graphic animation when the custom element is moving */
10510 if (game.graphics_engine_version < 4 &&
10513 ResetGfxAnimation(x, y);
10514 ResetRandomAnimationValue(x, y);
10517 if (change->pre_change_function)
10518 change->pre_change_function(x, y);
10522 ChangeDelay[x][y]--;
10524 if (ChangeDelay[x][y] != 0) /* continue element change */
10526 if (change->can_change)
10528 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
10530 if (IS_ANIMATED(graphic))
10531 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10533 if (change->change_function)
10534 change->change_function(x, y);
10537 else /* finish element change */
10539 if (ChangePage[x][y] != -1) /* remember page from delayed change */
10541 page = ChangePage[x][y];
10542 ChangePage[x][y] = -1;
10544 change = &ei->change_page[page];
10547 if (IS_MOVING(x, y)) /* never change a running system ;-) */
10549 ChangeDelay[x][y] = 1; /* try change after next move step */
10550 ChangePage[x][y] = page; /* remember page to use for change */
10555 /* special case: set new level random seed before changing element */
10556 if (change->has_action && change->action_type == CA_SET_LEVEL_RANDOM_SEED)
10557 handle_action_before_change = TRUE;
10559 if (change->has_action && handle_action_before_change)
10560 ExecuteCustomElementAction(x, y, element, page);
10562 if (change->can_change)
10564 if (ChangeElement(x, y, element, page))
10566 if (change->post_change_function)
10567 change->post_change_function(x, y);
10571 if (change->has_action && !handle_action_before_change)
10572 ExecuteCustomElementAction(x, y, element, page);
10576 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
10577 int trigger_element,
10579 int trigger_player,
10583 boolean change_done_any = FALSE;
10584 int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
10587 if (!(trigger_events[trigger_element][trigger_event]))
10590 RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
10592 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
10594 int element = EL_CUSTOM_START + i;
10595 boolean change_done = FALSE;
10598 if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
10599 !HAS_ANY_CHANGE_EVENT(element, trigger_event))
10602 for (p = 0; p < element_info[element].num_change_pages; p++)
10604 struct ElementChangeInfo *change = &element_info[element].change_page[p];
10606 if (change->can_change_or_has_action &&
10607 change->has_event[trigger_event] &&
10608 change->trigger_side & trigger_side &&
10609 change->trigger_player & trigger_player &&
10610 change->trigger_page & trigger_page_bits &&
10611 IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
10613 change->actual_trigger_element = trigger_element;
10614 change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
10615 change->actual_trigger_player_bits = trigger_player;
10616 change->actual_trigger_side = trigger_side;
10617 change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
10618 change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10620 if ((change->can_change && !change_done) || change->has_action)
10624 SCAN_PLAYFIELD(x, y)
10626 if (Feld[x][y] == element)
10628 if (change->can_change && !change_done)
10630 /* if element already changed in this frame, not only prevent
10631 another element change (checked in ChangeElement()), but
10632 also prevent additional element actions for this element */
10634 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
10635 !level.use_action_after_change_bug)
10638 ChangeDelay[x][y] = 1;
10639 ChangeEvent[x][y] = trigger_event;
10641 HandleElementChange(x, y, p);
10643 else if (change->has_action)
10645 /* if element already changed in this frame, not only prevent
10646 another element change (checked in ChangeElement()), but
10647 also prevent additional element actions for this element */
10649 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
10650 !level.use_action_after_change_bug)
10653 ExecuteCustomElementAction(x, y, element, p);
10654 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
10659 if (change->can_change)
10661 change_done = TRUE;
10662 change_done_any = TRUE;
10669 RECURSION_LOOP_DETECTION_END();
10671 return change_done_any;
10674 static boolean CheckElementChangeExt(int x, int y,
10676 int trigger_element,
10678 int trigger_player,
10681 boolean change_done = FALSE;
10684 if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
10685 !HAS_ANY_CHANGE_EVENT(element, trigger_event))
10688 if (Feld[x][y] == EL_BLOCKED)
10690 Blocked2Moving(x, y, &x, &y);
10691 element = Feld[x][y];
10694 /* check if element has already changed or is about to change after moving */
10695 if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
10696 Feld[x][y] != element) ||
10698 (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
10699 (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
10700 ChangePage[x][y] != -1)))
10703 RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
10705 for (p = 0; p < element_info[element].num_change_pages; p++)
10707 struct ElementChangeInfo *change = &element_info[element].change_page[p];
10709 /* check trigger element for all events where the element that is checked
10710 for changing interacts with a directly adjacent element -- this is
10711 different to element changes that affect other elements to change on the
10712 whole playfield (which is handeld by CheckTriggeredElementChangeExt()) */
10713 boolean check_trigger_element =
10714 (trigger_event == CE_TOUCHING_X ||
10715 trigger_event == CE_HITTING_X ||
10716 trigger_event == CE_HIT_BY_X ||
10717 trigger_event == CE_DIGGING_X); /* this one was forgotten until 3.2.3 */
10719 if (change->can_change_or_has_action &&
10720 change->has_event[trigger_event] &&
10721 change->trigger_side & trigger_side &&
10722 change->trigger_player & trigger_player &&
10723 (!check_trigger_element ||
10724 IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
10726 change->actual_trigger_element = trigger_element;
10727 change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
10728 change->actual_trigger_player_bits = trigger_player;
10729 change->actual_trigger_side = trigger_side;
10730 change->actual_trigger_ce_value = CustomValue[x][y];
10731 change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10733 /* special case: trigger element not at (x,y) position for some events */
10734 if (check_trigger_element)
10746 { 0, 0 }, { 0, 0 }, { 0, 0 },
10750 int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
10751 int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
10753 change->actual_trigger_ce_value = CustomValue[xx][yy];
10754 change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10757 if (change->can_change && !change_done)
10759 ChangeDelay[x][y] = 1;
10760 ChangeEvent[x][y] = trigger_event;
10762 HandleElementChange(x, y, p);
10764 change_done = TRUE;
10766 else if (change->has_action)
10768 ExecuteCustomElementAction(x, y, element, p);
10769 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
10774 RECURSION_LOOP_DETECTION_END();
10776 return change_done;
10779 static void PlayPlayerSound(struct PlayerInfo *player)
10781 int jx = player->jx, jy = player->jy;
10782 int sound_element = player->artwork_element;
10783 int last_action = player->last_action_waiting;
10784 int action = player->action_waiting;
10786 if (player->is_waiting)
10788 if (action != last_action)
10789 PlayLevelSoundElementAction(jx, jy, sound_element, action);
10791 PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
10795 if (action != last_action)
10796 StopSound(element_info[sound_element].sound[last_action]);
10798 if (last_action == ACTION_SLEEPING)
10799 PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
10803 static void PlayAllPlayersSound()
10807 for (i = 0; i < MAX_PLAYERS; i++)
10808 if (stored_player[i].active)
10809 PlayPlayerSound(&stored_player[i]);
10812 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
10814 boolean last_waiting = player->is_waiting;
10815 int move_dir = player->MovDir;
10817 player->dir_waiting = move_dir;
10818 player->last_action_waiting = player->action_waiting;
10822 if (!last_waiting) /* not waiting -> waiting */
10824 player->is_waiting = TRUE;
10826 player->frame_counter_bored =
10828 game.player_boring_delay_fixed +
10829 GetSimpleRandom(game.player_boring_delay_random);
10830 player->frame_counter_sleeping =
10832 game.player_sleeping_delay_fixed +
10833 GetSimpleRandom(game.player_sleeping_delay_random);
10835 InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
10838 if (game.player_sleeping_delay_fixed +
10839 game.player_sleeping_delay_random > 0 &&
10840 player->anim_delay_counter == 0 &&
10841 player->post_delay_counter == 0 &&
10842 FrameCounter >= player->frame_counter_sleeping)
10843 player->is_sleeping = TRUE;
10844 else if (game.player_boring_delay_fixed +
10845 game.player_boring_delay_random > 0 &&
10846 FrameCounter >= player->frame_counter_bored)
10847 player->is_bored = TRUE;
10849 player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
10850 player->is_bored ? ACTION_BORING :
10853 if (player->is_sleeping && player->use_murphy)
10855 /* special case for sleeping Murphy when leaning against non-free tile */
10857 if (!IN_LEV_FIELD(player->jx - 1, player->jy) ||
10858 (Feld[player->jx - 1][player->jy] != EL_EMPTY &&
10859 !IS_MOVING(player->jx - 1, player->jy)))
10860 move_dir = MV_LEFT;
10861 else if (!IN_LEV_FIELD(player->jx + 1, player->jy) ||
10862 (Feld[player->jx + 1][player->jy] != EL_EMPTY &&
10863 !IS_MOVING(player->jx + 1, player->jy)))
10864 move_dir = MV_RIGHT;
10866 player->is_sleeping = FALSE;
10868 player->dir_waiting = move_dir;
10871 if (player->is_sleeping)
10873 if (player->num_special_action_sleeping > 0)
10875 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
10877 int last_special_action = player->special_action_sleeping;
10878 int num_special_action = player->num_special_action_sleeping;
10879 int special_action =
10880 (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
10881 last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
10882 last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
10883 last_special_action + 1 : ACTION_SLEEPING);
10884 int special_graphic =
10885 el_act_dir2img(player->artwork_element, special_action, move_dir);
10887 player->anim_delay_counter =
10888 graphic_info[special_graphic].anim_delay_fixed +
10889 GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
10890 player->post_delay_counter =
10891 graphic_info[special_graphic].post_delay_fixed +
10892 GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
10894 player->special_action_sleeping = special_action;
10897 if (player->anim_delay_counter > 0)
10899 player->action_waiting = player->special_action_sleeping;
10900 player->anim_delay_counter--;
10902 else if (player->post_delay_counter > 0)
10904 player->post_delay_counter--;
10908 else if (player->is_bored)
10910 if (player->num_special_action_bored > 0)
10912 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
10914 int special_action =
10915 ACTION_BORING_1 + GetSimpleRandom(player->num_special_action_bored);
10916 int special_graphic =
10917 el_act_dir2img(player->artwork_element, special_action, move_dir);
10919 player->anim_delay_counter =
10920 graphic_info[special_graphic].anim_delay_fixed +
10921 GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
10922 player->post_delay_counter =
10923 graphic_info[special_graphic].post_delay_fixed +
10924 GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
10926 player->special_action_bored = special_action;
10929 if (player->anim_delay_counter > 0)
10931 player->action_waiting = player->special_action_bored;
10932 player->anim_delay_counter--;
10934 else if (player->post_delay_counter > 0)
10936 player->post_delay_counter--;
10941 else if (last_waiting) /* waiting -> not waiting */
10943 player->is_waiting = FALSE;
10944 player->is_bored = FALSE;
10945 player->is_sleeping = FALSE;
10947 player->frame_counter_bored = -1;
10948 player->frame_counter_sleeping = -1;
10950 player->anim_delay_counter = 0;
10951 player->post_delay_counter = 0;
10953 player->dir_waiting = player->MovDir;
10954 player->action_waiting = ACTION_DEFAULT;
10956 player->special_action_bored = ACTION_DEFAULT;
10957 player->special_action_sleeping = ACTION_DEFAULT;
10961 static void CheckSaveEngineSnapshot(struct PlayerInfo *player)
10963 if ((!player->is_moving && player->was_moving) ||
10964 (player->MovPos == 0 && player->was_moving) ||
10965 (player->is_snapping && !player->was_snapping) ||
10966 (player->is_dropping && !player->was_dropping))
10968 if (!CheckSaveEngineSnapshotToList())
10971 player->was_moving = FALSE;
10972 player->was_snapping = TRUE;
10973 player->was_dropping = TRUE;
10977 if (player->is_moving)
10978 player->was_moving = TRUE;
10980 if (!player->is_snapping)
10981 player->was_snapping = FALSE;
10983 if (!player->is_dropping)
10984 player->was_dropping = FALSE;
10988 static void CheckSingleStepMode(struct PlayerInfo *player)
10990 if (tape.single_step && tape.recording && !tape.pausing)
10992 /* as it is called "single step mode", just return to pause mode when the
10993 player stopped moving after one tile (or never starts moving at all) */
10994 if (!player->is_moving &&
10995 !player->is_pushing &&
10996 !player->is_dropping_pressed)
10998 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
10999 SnapField(player, 0, 0); /* stop snapping */
11003 CheckSaveEngineSnapshot(player);
11006 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
11008 int left = player_action & JOY_LEFT;
11009 int right = player_action & JOY_RIGHT;
11010 int up = player_action & JOY_UP;
11011 int down = player_action & JOY_DOWN;
11012 int button1 = player_action & JOY_BUTTON_1;
11013 int button2 = player_action & JOY_BUTTON_2;
11014 int dx = (left ? -1 : right ? 1 : 0);
11015 int dy = (up ? -1 : down ? 1 : 0);
11017 if (!player->active || tape.pausing)
11023 SnapField(player, dx, dy);
11027 DropElement(player);
11029 MovePlayer(player, dx, dy);
11032 CheckSingleStepMode(player);
11034 SetPlayerWaiting(player, FALSE);
11036 return player_action;
11040 /* no actions for this player (no input at player's configured device) */
11042 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
11043 SnapField(player, 0, 0);
11044 CheckGravityMovementWhenNotMoving(player);
11046 if (player->MovPos == 0)
11047 SetPlayerWaiting(player, TRUE);
11049 if (player->MovPos == 0) /* needed for tape.playing */
11050 player->is_moving = FALSE;
11052 player->is_dropping = FALSE;
11053 player->is_dropping_pressed = FALSE;
11054 player->drop_pressed_delay = 0;
11056 CheckSingleStepMode(player);
11062 static void SetMouseActionFromTapeAction(struct MouseActionInfo *mouse_action,
11065 if (!tape.use_mouse)
11068 mouse_action->lx = tape_action[TAPE_ACTION_LX];
11069 mouse_action->ly = tape_action[TAPE_ACTION_LY];
11070 mouse_action->button = tape_action[TAPE_ACTION_BUTTON];
11073 static void SetTapeActionFromMouseAction(byte *tape_action,
11074 struct MouseActionInfo *mouse_action)
11076 if (!tape.use_mouse)
11079 tape_action[TAPE_ACTION_LX] = mouse_action->lx;
11080 tape_action[TAPE_ACTION_LY] = mouse_action->ly;
11081 tape_action[TAPE_ACTION_BUTTON] = mouse_action->button;
11084 static void CheckLevelTime()
11088 /* !!! SAME CODE AS IN "GameActions()" -- FIX THIS !!! */
11089 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11091 if (level.native_em_level->lev->home == 0) /* all players at home */
11093 PlayerWins(local_player);
11095 AllPlayersGone = TRUE;
11097 level.native_em_level->lev->home = -1;
11100 if (level.native_em_level->ply[0]->alive == 0 &&
11101 level.native_em_level->ply[1]->alive == 0 &&
11102 level.native_em_level->ply[2]->alive == 0 &&
11103 level.native_em_level->ply[3]->alive == 0) /* all dead */
11104 AllPlayersGone = TRUE;
11106 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11108 if (game_sp.LevelSolved &&
11109 !game_sp.GameOver) /* game won */
11111 PlayerWins(local_player);
11113 game_sp.GameOver = TRUE;
11115 AllPlayersGone = TRUE;
11118 if (game_sp.GameOver) /* game lost */
11119 AllPlayersGone = TRUE;
11121 else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
11123 if (game_mm.level_solved &&
11124 !game_mm.game_over) /* game won */
11126 PlayerWins(local_player);
11128 game_mm.game_over = TRUE;
11130 AllPlayersGone = TRUE;
11133 if (game_mm.game_over) /* game lost */
11134 AllPlayersGone = TRUE;
11137 if (TimeFrames >= FRAMES_PER_SECOND)
11142 for (i = 0; i < MAX_PLAYERS; i++)
11144 struct PlayerInfo *player = &stored_player[i];
11146 if (SHIELD_ON(player))
11148 player->shield_normal_time_left--;
11150 if (player->shield_deadly_time_left > 0)
11151 player->shield_deadly_time_left--;
11155 if (!local_player->LevelSolved && !level.use_step_counter)
11163 if (TimeLeft <= 10 && setup.time_limit)
11164 PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
11166 /* this does not make sense: game_panel_controls[GAME_PANEL_TIME].value
11167 is reset from other values in UpdateGameDoorValues() -- FIX THIS */
11169 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
11171 if (!TimeLeft && setup.time_limit)
11173 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11174 level.native_em_level->lev->killed_out_of_time = TRUE;
11176 for (i = 0; i < MAX_PLAYERS; i++)
11177 KillPlayer(&stored_player[i]);
11180 else if (game.no_time_limit && !AllPlayersGone) /* level w/o time limit */
11182 game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
11185 level.native_em_level->lev->time =
11186 (game.no_time_limit ? TimePlayed : TimeLeft);
11189 if (tape.recording || tape.playing)
11190 DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
11193 if (tape.recording || tape.playing)
11194 DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
11196 UpdateAndDisplayGameControlValues();
11199 void AdvanceFrameAndPlayerCounters(int player_nr)
11203 /* advance frame counters (global frame counter and time frame counter) */
11207 /* advance player counters (counters for move delay, move animation etc.) */
11208 for (i = 0; i < MAX_PLAYERS; i++)
11210 boolean advance_player_counters = (player_nr == -1 || player_nr == i);
11211 int move_delay_value = stored_player[i].move_delay_value;
11212 int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
11214 if (!advance_player_counters) /* not all players may be affected */
11217 if (move_frames == 0) /* less than one move per game frame */
11219 int stepsize = TILEX / move_delay_value;
11220 int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
11221 int count = (stored_player[i].is_moving ?
11222 ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
11224 if (count % delay == 0)
11228 stored_player[i].Frame += move_frames;
11230 if (stored_player[i].MovPos != 0)
11231 stored_player[i].StepFrame += move_frames;
11233 if (stored_player[i].move_delay > 0)
11234 stored_player[i].move_delay--;
11236 /* due to bugs in previous versions, counter must count up, not down */
11237 if (stored_player[i].push_delay != -1)
11238 stored_player[i].push_delay++;
11240 if (stored_player[i].drop_delay > 0)
11241 stored_player[i].drop_delay--;
11243 if (stored_player[i].is_dropping_pressed)
11244 stored_player[i].drop_pressed_delay++;
11248 void StartGameActions(boolean init_network_game, boolean record_tape,
11251 unsigned int new_random_seed = InitRND(random_seed);
11254 TapeStartRecording(new_random_seed);
11256 #if defined(NETWORK_AVALIABLE)
11257 if (init_network_game)
11259 SendToServer_StartPlaying();
11268 void GameActionsExt()
11271 static unsigned int game_frame_delay = 0;
11273 unsigned int game_frame_delay_value;
11274 byte *recorded_player_action;
11275 byte summarized_player_action = 0;
11276 byte tape_action[MAX_PLAYERS];
11279 /* detect endless loops, caused by custom element programming */
11280 if (recursion_loop_detected && recursion_loop_depth == 0)
11282 char *message = getStringCat3("Internal Error! Element ",
11283 EL_NAME(recursion_loop_element),
11284 " caused endless loop! Quit the game?");
11286 Error(ERR_WARN, "element '%s' caused endless loop in game engine",
11287 EL_NAME(recursion_loop_element));
11289 RequestQuitGameExt(FALSE, level_editor_test_game, message);
11291 recursion_loop_detected = FALSE; /* if game should be continued */
11298 if (game.restart_level)
11299 StartGameActions(options.network, setup.autorecord, level.random_seed);
11301 /* !!! SAME CODE AS IN "CheckLevelTime()" -- FIX THIS !!! */
11302 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11304 if (level.native_em_level->lev->home == 0) /* all players at home */
11306 PlayerWins(local_player);
11308 AllPlayersGone = TRUE;
11310 level.native_em_level->lev->home = -1;
11313 if (level.native_em_level->ply[0]->alive == 0 &&
11314 level.native_em_level->ply[1]->alive == 0 &&
11315 level.native_em_level->ply[2]->alive == 0 &&
11316 level.native_em_level->ply[3]->alive == 0) /* all dead */
11317 AllPlayersGone = TRUE;
11319 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11321 if (game_sp.LevelSolved &&
11322 !game_sp.GameOver) /* game won */
11324 PlayerWins(local_player);
11326 game_sp.GameOver = TRUE;
11328 AllPlayersGone = TRUE;
11331 if (game_sp.GameOver) /* game lost */
11332 AllPlayersGone = TRUE;
11334 else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
11336 if (game_mm.level_solved &&
11337 !game_mm.game_over) /* game won */
11339 PlayerWins(local_player);
11341 game_mm.game_over = TRUE;
11343 AllPlayersGone = TRUE;
11346 if (game_mm.game_over) /* game lost */
11347 AllPlayersGone = TRUE;
11350 if (local_player->LevelSolved && !local_player->LevelSolved_GameEnd)
11353 if (AllPlayersGone && !TAPE_IS_STOPPED(tape))
11356 if (game_status != GAME_MODE_PLAYING) /* status might have changed */
11359 game_frame_delay_value =
11360 (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
11362 if (tape.playing && tape.warp_forward && !tape.pausing)
11363 game_frame_delay_value = 0;
11365 SetVideoFrameDelay(game_frame_delay_value);
11369 /* ---------- main game synchronization point ---------- */
11371 int skip = WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11373 printf("::: skip == %d\n", skip);
11376 /* ---------- main game synchronization point ---------- */
11378 WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11382 if (network_playing && !network_player_action_received)
11384 /* try to get network player actions in time */
11386 #if defined(NETWORK_AVALIABLE)
11387 /* last chance to get network player actions without main loop delay */
11388 HandleNetworking();
11391 /* game was quit by network peer */
11392 if (game_status != GAME_MODE_PLAYING)
11395 if (!network_player_action_received)
11396 return; /* failed to get network player actions in time */
11398 /* do not yet reset "network_player_action_received" (for tape.pausing) */
11404 /* at this point we know that we really continue executing the game */
11406 network_player_action_received = FALSE;
11408 /* when playing tape, read previously recorded player input from tape data */
11409 recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
11411 local_player->effective_mouse_action = local_player->mouse_action;
11413 if (recorded_player_action != NULL)
11414 SetMouseActionFromTapeAction(&local_player->effective_mouse_action,
11415 recorded_player_action);
11417 /* TapePlayAction() may return NULL when toggling to "pause before death" */
11421 if (tape.set_centered_player)
11423 game.centered_player_nr_next = tape.centered_player_nr_next;
11424 game.set_centered_player = TRUE;
11427 for (i = 0; i < MAX_PLAYERS; i++)
11429 summarized_player_action |= stored_player[i].action;
11431 if (!network_playing && (game.team_mode || tape.playing))
11432 stored_player[i].effective_action = stored_player[i].action;
11435 #if defined(NETWORK_AVALIABLE)
11436 if (network_playing)
11437 SendToServer_MovePlayer(summarized_player_action);
11440 // summarize all actions at local players mapped input device position
11441 // (this allows using different input devices in single player mode)
11442 if (!options.network && !game.team_mode)
11443 stored_player[map_player_action[local_player->index_nr]].effective_action =
11444 summarized_player_action;
11446 if (tape.recording &&
11448 setup.input_on_focus &&
11449 game.centered_player_nr != -1)
11451 for (i = 0; i < MAX_PLAYERS; i++)
11452 stored_player[i].effective_action =
11453 (i == game.centered_player_nr ? summarized_player_action : 0);
11456 if (recorded_player_action != NULL)
11457 for (i = 0; i < MAX_PLAYERS; i++)
11458 stored_player[i].effective_action = recorded_player_action[i];
11460 for (i = 0; i < MAX_PLAYERS; i++)
11462 tape_action[i] = stored_player[i].effective_action;
11464 /* (this may happen in the RND game engine if a player was not present on
11465 the playfield on level start, but appeared later from a custom element */
11466 if (setup.team_mode &&
11469 !tape.player_participates[i])
11470 tape.player_participates[i] = TRUE;
11473 SetTapeActionFromMouseAction(tape_action,
11474 &local_player->effective_mouse_action);
11476 /* only record actions from input devices, but not programmed actions */
11477 if (tape.recording)
11478 TapeRecordAction(tape_action);
11480 #if USE_NEW_PLAYER_ASSIGNMENTS
11481 // !!! also map player actions in single player mode !!!
11482 // if (game.team_mode)
11485 byte mapped_action[MAX_PLAYERS];
11487 #if DEBUG_PLAYER_ACTIONS
11489 for (i = 0; i < MAX_PLAYERS; i++)
11490 printf(" %d, ", stored_player[i].effective_action);
11493 for (i = 0; i < MAX_PLAYERS; i++)
11494 mapped_action[i] = stored_player[map_player_action[i]].effective_action;
11496 for (i = 0; i < MAX_PLAYERS; i++)
11497 stored_player[i].effective_action = mapped_action[i];
11499 #if DEBUG_PLAYER_ACTIONS
11501 for (i = 0; i < MAX_PLAYERS; i++)
11502 printf(" %d, ", stored_player[i].effective_action);
11506 #if DEBUG_PLAYER_ACTIONS
11510 for (i = 0; i < MAX_PLAYERS; i++)
11511 printf(" %d, ", stored_player[i].effective_action);
11517 for (i = 0; i < MAX_PLAYERS; i++)
11519 // allow engine snapshot in case of changed movement attempt
11520 if ((game.snapshot.last_action[i] & KEY_MOTION) !=
11521 (stored_player[i].effective_action & KEY_MOTION))
11522 game.snapshot.changed_action = TRUE;
11524 // allow engine snapshot in case of snapping/dropping attempt
11525 if ((game.snapshot.last_action[i] & KEY_BUTTON) == 0 &&
11526 (stored_player[i].effective_action & KEY_BUTTON) != 0)
11527 game.snapshot.changed_action = TRUE;
11529 game.snapshot.last_action[i] = stored_player[i].effective_action;
11532 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11534 GameActions_EM_Main();
11536 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11538 GameActions_SP_Main();
11540 else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
11542 GameActions_MM_Main();
11546 GameActions_RND_Main();
11549 BlitScreenToBitmap(backbuffer);
11553 AdvanceFrameAndPlayerCounters(-1); /* advance counters for all players */
11555 if (global.show_frames_per_second)
11557 static unsigned int fps_counter = 0;
11558 static int fps_frames = 0;
11559 unsigned int fps_delay_ms = Counter() - fps_counter;
11563 if (fps_delay_ms >= 500) /* calculate FPS every 0.5 seconds */
11565 global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
11568 fps_counter = Counter();
11570 /* always draw FPS to screen after FPS value was updated */
11571 redraw_mask |= REDRAW_FPS;
11574 /* only draw FPS if no screen areas are deactivated (invisible warp mode) */
11575 if (GetDrawDeactivationMask() == REDRAW_NONE)
11576 redraw_mask |= REDRAW_FPS;
11580 static void GameActions_CheckSaveEngineSnapshot()
11582 if (!game.snapshot.save_snapshot)
11585 // clear flag for saving snapshot _before_ saving snapshot
11586 game.snapshot.save_snapshot = FALSE;
11588 SaveEngineSnapshotToList();
11595 GameActions_CheckSaveEngineSnapshot();
11598 void GameActions_EM_Main()
11600 byte effective_action[MAX_PLAYERS];
11601 boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11604 for (i = 0; i < MAX_PLAYERS; i++)
11605 effective_action[i] = stored_player[i].effective_action;
11607 GameActions_EM(effective_action, warp_mode);
11610 void GameActions_SP_Main()
11612 byte effective_action[MAX_PLAYERS];
11613 boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11616 for (i = 0; i < MAX_PLAYERS; i++)
11617 effective_action[i] = stored_player[i].effective_action;
11619 GameActions_SP(effective_action, warp_mode);
11621 for (i = 0; i < MAX_PLAYERS; i++)
11623 if (stored_player[i].force_dropping)
11624 stored_player[i].action |= KEY_BUTTON_DROP;
11626 stored_player[i].force_dropping = FALSE;
11630 void GameActions_MM_Main()
11632 boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11634 GameActions_MM(local_player->effective_mouse_action, warp_mode);
11637 void GameActions_RND_Main()
11642 void GameActions_RND()
11644 int magic_wall_x = 0, magic_wall_y = 0;
11645 int i, x, y, element, graphic, last_gfx_frame;
11647 InitPlayfieldScanModeVars();
11649 if (game.engine_version >= VERSION_IDENT(3,2,0,7))
11651 SCAN_PLAYFIELD(x, y)
11653 ChangeCount[x][y] = 0;
11654 ChangeEvent[x][y] = -1;
11658 if (game.set_centered_player)
11660 boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
11662 /* switching to "all players" only possible if all players fit to screen */
11663 if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
11665 game.centered_player_nr_next = game.centered_player_nr;
11666 game.set_centered_player = FALSE;
11669 /* do not switch focus to non-existing (or non-active) player */
11670 if (game.centered_player_nr_next >= 0 &&
11671 !stored_player[game.centered_player_nr_next].active)
11673 game.centered_player_nr_next = game.centered_player_nr;
11674 game.set_centered_player = FALSE;
11678 if (game.set_centered_player &&
11679 ScreenMovPos == 0) /* screen currently aligned at tile position */
11683 if (game.centered_player_nr_next == -1)
11685 setScreenCenteredToAllPlayers(&sx, &sy);
11689 sx = stored_player[game.centered_player_nr_next].jx;
11690 sy = stored_player[game.centered_player_nr_next].jy;
11693 game.centered_player_nr = game.centered_player_nr_next;
11694 game.set_centered_player = FALSE;
11696 DrawRelocateScreen(0, 0, sx, sy, MV_NONE, TRUE, setup.quick_switch);
11697 DrawGameDoorValues();
11700 for (i = 0; i < MAX_PLAYERS; i++)
11702 int actual_player_action = stored_player[i].effective_action;
11705 /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
11706 - rnd_equinox_tetrachloride 048
11707 - rnd_equinox_tetrachloride_ii 096
11708 - rnd_emanuel_schmieg 002
11709 - doctor_sloan_ww 001, 020
11711 if (stored_player[i].MovPos == 0)
11712 CheckGravityMovement(&stored_player[i]);
11715 /* overwrite programmed action with tape action */
11716 if (stored_player[i].programmed_action)
11717 actual_player_action = stored_player[i].programmed_action;
11719 PlayerActions(&stored_player[i], actual_player_action);
11721 ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
11724 ScrollScreen(NULL, SCROLL_GO_ON);
11726 /* for backwards compatibility, the following code emulates a fixed bug that
11727 occured when pushing elements (causing elements that just made their last
11728 pushing step to already (if possible) make their first falling step in the
11729 same game frame, which is bad); this code is also needed to use the famous
11730 "spring push bug" which is used in older levels and might be wanted to be
11731 used also in newer levels, but in this case the buggy pushing code is only
11732 affecting the "spring" element and no other elements */
11734 if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
11736 for (i = 0; i < MAX_PLAYERS; i++)
11738 struct PlayerInfo *player = &stored_player[i];
11739 int x = player->jx;
11740 int y = player->jy;
11742 if (player->active && player->is_pushing && player->is_moving &&
11744 (game.engine_version < VERSION_IDENT(2,2,0,7) ||
11745 Feld[x][y] == EL_SPRING))
11747 ContinueMoving(x, y);
11749 /* continue moving after pushing (this is actually a bug) */
11750 if (!IS_MOVING(x, y))
11751 Stop[x][y] = FALSE;
11756 SCAN_PLAYFIELD(x, y)
11758 ChangeCount[x][y] = 0;
11759 ChangeEvent[x][y] = -1;
11761 /* this must be handled before main playfield loop */
11762 if (Feld[x][y] == EL_PLAYER_IS_LEAVING)
11765 if (MovDelay[x][y] <= 0)
11769 if (Feld[x][y] == EL_ELEMENT_SNAPPING)
11772 if (MovDelay[x][y] <= 0)
11775 TEST_DrawLevelField(x, y);
11777 TestIfElementTouchesCustomElement(x, y); /* for empty space */
11782 if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
11784 printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
11785 printf("GameActions(): This should never happen!\n");
11787 ChangePage[x][y] = -1;
11791 Stop[x][y] = FALSE;
11792 if (WasJustMoving[x][y] > 0)
11793 WasJustMoving[x][y]--;
11794 if (WasJustFalling[x][y] > 0)
11795 WasJustFalling[x][y]--;
11796 if (CheckCollision[x][y] > 0)
11797 CheckCollision[x][y]--;
11798 if (CheckImpact[x][y] > 0)
11799 CheckImpact[x][y]--;
11803 /* reset finished pushing action (not done in ContinueMoving() to allow
11804 continuous pushing animation for elements with zero push delay) */
11805 if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
11807 ResetGfxAnimation(x, y);
11808 TEST_DrawLevelField(x, y);
11812 if (IS_BLOCKED(x, y))
11816 Blocked2Moving(x, y, &oldx, &oldy);
11817 if (!IS_MOVING(oldx, oldy))
11819 printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
11820 printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
11821 printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
11822 printf("GameActions(): This should never happen!\n");
11828 SCAN_PLAYFIELD(x, y)
11830 element = Feld[x][y];
11831 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11832 last_gfx_frame = GfxFrame[x][y];
11834 ResetGfxFrame(x, y);
11836 if (GfxFrame[x][y] != last_gfx_frame && !Stop[x][y])
11837 DrawLevelGraphicAnimation(x, y, graphic);
11839 if (ANIM_MODE(graphic) == ANIM_RANDOM &&
11840 IS_NEXT_FRAME(GfxFrame[x][y], graphic))
11841 ResetRandomAnimationValue(x, y);
11843 SetRandomAnimationValue(x, y);
11845 PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
11847 if (IS_INACTIVE(element))
11849 if (IS_ANIMATED(graphic))
11850 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11855 /* this may take place after moving, so 'element' may have changed */
11856 if (IS_CHANGING(x, y) &&
11857 (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
11859 int page = element_info[element].event_page_nr[CE_DELAY];
11861 HandleElementChange(x, y, page);
11863 element = Feld[x][y];
11864 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11867 if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
11871 element = Feld[x][y];
11872 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11874 if (IS_ANIMATED(graphic) &&
11875 !IS_MOVING(x, y) &&
11877 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11879 if (IS_GEM(element) || element == EL_SP_INFOTRON)
11880 TEST_DrawTwinkleOnField(x, y);
11882 else if (element == EL_ACID)
11885 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11887 else if ((element == EL_EXIT_OPEN ||
11888 element == EL_EM_EXIT_OPEN ||
11889 element == EL_SP_EXIT_OPEN ||
11890 element == EL_STEEL_EXIT_OPEN ||
11891 element == EL_EM_STEEL_EXIT_OPEN ||
11892 element == EL_SP_TERMINAL ||
11893 element == EL_SP_TERMINAL_ACTIVE ||
11894 element == EL_EXTRA_TIME ||
11895 element == EL_SHIELD_NORMAL ||
11896 element == EL_SHIELD_DEADLY) &&
11897 IS_ANIMATED(graphic))
11898 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11899 else if (IS_MOVING(x, y))
11900 ContinueMoving(x, y);
11901 else if (IS_ACTIVE_BOMB(element))
11902 CheckDynamite(x, y);
11903 else if (element == EL_AMOEBA_GROWING)
11904 AmoebeWaechst(x, y);
11905 else if (element == EL_AMOEBA_SHRINKING)
11906 AmoebaDisappearing(x, y);
11908 #if !USE_NEW_AMOEBA_CODE
11909 else if (IS_AMOEBALIVE(element))
11910 AmoebeAbleger(x, y);
11913 else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
11915 else if (element == EL_EXIT_CLOSED)
11917 else if (element == EL_EM_EXIT_CLOSED)
11919 else if (element == EL_STEEL_EXIT_CLOSED)
11920 CheckExitSteel(x, y);
11921 else if (element == EL_EM_STEEL_EXIT_CLOSED)
11922 CheckExitSteelEM(x, y);
11923 else if (element == EL_SP_EXIT_CLOSED)
11925 else if (element == EL_EXPANDABLE_WALL_GROWING ||
11926 element == EL_EXPANDABLE_STEELWALL_GROWING)
11927 MauerWaechst(x, y);
11928 else if (element == EL_EXPANDABLE_WALL ||
11929 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
11930 element == EL_EXPANDABLE_WALL_VERTICAL ||
11931 element == EL_EXPANDABLE_WALL_ANY ||
11932 element == EL_BD_EXPANDABLE_WALL)
11933 MauerAbleger(x, y);
11934 else if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
11935 element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
11936 element == EL_EXPANDABLE_STEELWALL_ANY)
11937 MauerAblegerStahl(x, y);
11938 else if (element == EL_FLAMES)
11939 CheckForDragon(x, y);
11940 else if (element == EL_EXPLOSION)
11941 ; /* drawing of correct explosion animation is handled separately */
11942 else if (element == EL_ELEMENT_SNAPPING ||
11943 element == EL_DIAGONAL_SHRINKING ||
11944 element == EL_DIAGONAL_GROWING)
11946 graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
11948 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11950 else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
11951 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11953 if (IS_BELT_ACTIVE(element))
11954 PlayLevelSoundAction(x, y, ACTION_ACTIVE);
11956 if (game.magic_wall_active)
11958 int jx = local_player->jx, jy = local_player->jy;
11960 /* play the element sound at the position nearest to the player */
11961 if ((element == EL_MAGIC_WALL_FULL ||
11962 element == EL_MAGIC_WALL_ACTIVE ||
11963 element == EL_MAGIC_WALL_EMPTYING ||
11964 element == EL_BD_MAGIC_WALL_FULL ||
11965 element == EL_BD_MAGIC_WALL_ACTIVE ||
11966 element == EL_BD_MAGIC_WALL_EMPTYING ||
11967 element == EL_DC_MAGIC_WALL_FULL ||
11968 element == EL_DC_MAGIC_WALL_ACTIVE ||
11969 element == EL_DC_MAGIC_WALL_EMPTYING) &&
11970 ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
11978 #if USE_NEW_AMOEBA_CODE
11979 /* new experimental amoeba growth stuff */
11980 if (!(FrameCounter % 8))
11982 static unsigned int random = 1684108901;
11984 for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
11986 x = RND(lev_fieldx);
11987 y = RND(lev_fieldy);
11988 element = Feld[x][y];
11990 if (!IS_PLAYER(x,y) &&
11991 (element == EL_EMPTY ||
11992 CAN_GROW_INTO(element) ||
11993 element == EL_QUICKSAND_EMPTY ||
11994 element == EL_QUICKSAND_FAST_EMPTY ||
11995 element == EL_ACID_SPLASH_LEFT ||
11996 element == EL_ACID_SPLASH_RIGHT))
11998 if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
11999 (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
12000 (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
12001 (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
12002 Feld[x][y] = EL_AMOEBA_DROP;
12005 random = random * 129 + 1;
12010 game.explosions_delayed = FALSE;
12012 SCAN_PLAYFIELD(x, y)
12014 element = Feld[x][y];
12016 if (ExplodeField[x][y])
12017 Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
12018 else if (element == EL_EXPLOSION)
12019 Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
12021 ExplodeField[x][y] = EX_TYPE_NONE;
12024 game.explosions_delayed = TRUE;
12026 if (game.magic_wall_active)
12028 if (!(game.magic_wall_time_left % 4))
12030 int element = Feld[magic_wall_x][magic_wall_y];
12032 if (element == EL_BD_MAGIC_WALL_FULL ||
12033 element == EL_BD_MAGIC_WALL_ACTIVE ||
12034 element == EL_BD_MAGIC_WALL_EMPTYING)
12035 PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
12036 else if (element == EL_DC_MAGIC_WALL_FULL ||
12037 element == EL_DC_MAGIC_WALL_ACTIVE ||
12038 element == EL_DC_MAGIC_WALL_EMPTYING)
12039 PlayLevelSound(magic_wall_x, magic_wall_y, SND_DC_MAGIC_WALL_ACTIVE);
12041 PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
12044 if (game.magic_wall_time_left > 0)
12046 game.magic_wall_time_left--;
12048 if (!game.magic_wall_time_left)
12050 SCAN_PLAYFIELD(x, y)
12052 element = Feld[x][y];
12054 if (element == EL_MAGIC_WALL_ACTIVE ||
12055 element == EL_MAGIC_WALL_FULL)
12057 Feld[x][y] = EL_MAGIC_WALL_DEAD;
12058 TEST_DrawLevelField(x, y);
12060 else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
12061 element == EL_BD_MAGIC_WALL_FULL)
12063 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
12064 TEST_DrawLevelField(x, y);
12066 else if (element == EL_DC_MAGIC_WALL_ACTIVE ||
12067 element == EL_DC_MAGIC_WALL_FULL)
12069 Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
12070 TEST_DrawLevelField(x, y);
12074 game.magic_wall_active = FALSE;
12079 if (game.light_time_left > 0)
12081 game.light_time_left--;
12083 if (game.light_time_left == 0)
12084 RedrawAllLightSwitchesAndInvisibleElements();
12087 if (game.timegate_time_left > 0)
12089 game.timegate_time_left--;
12091 if (game.timegate_time_left == 0)
12092 CloseAllOpenTimegates();
12095 if (game.lenses_time_left > 0)
12097 game.lenses_time_left--;
12099 if (game.lenses_time_left == 0)
12100 RedrawAllInvisibleElementsForLenses();
12103 if (game.magnify_time_left > 0)
12105 game.magnify_time_left--;
12107 if (game.magnify_time_left == 0)
12108 RedrawAllInvisibleElementsForMagnifier();
12111 for (i = 0; i < MAX_PLAYERS; i++)
12113 struct PlayerInfo *player = &stored_player[i];
12115 if (SHIELD_ON(player))
12117 if (player->shield_deadly_time_left)
12118 PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
12119 else if (player->shield_normal_time_left)
12120 PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
12124 #if USE_DELAYED_GFX_REDRAW
12125 SCAN_PLAYFIELD(x, y)
12127 if (GfxRedraw[x][y] != GFX_REDRAW_NONE)
12129 /* !!! PROBLEM: THIS REDRAWS THE PLAYFIELD _AFTER_ THE SCAN, BUT TILES
12130 !!! MAY HAVE CHANGED AFTER BEING DRAWN DURING PLAYFIELD SCAN !!! */
12132 if (GfxRedraw[x][y] & GFX_REDRAW_TILE)
12133 DrawLevelField(x, y);
12135 if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED)
12136 DrawLevelFieldCrumbled(x, y);
12138 if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS)
12139 DrawLevelFieldCrumbledNeighbours(x, y);
12141 if (GfxRedraw[x][y] & GFX_REDRAW_TILE_TWINKLED)
12142 DrawTwinkleOnField(x, y);
12145 GfxRedraw[x][y] = GFX_REDRAW_NONE;
12150 PlayAllPlayersSound();
12152 if (local_player->show_envelope != 0 && local_player->MovPos == 0)
12154 ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
12156 local_player->show_envelope = 0;
12159 /* use random number generator in every frame to make it less predictable */
12160 if (game.engine_version >= VERSION_IDENT(3,1,1,0))
12164 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
12166 int min_x = x, min_y = y, max_x = x, max_y = y;
12169 for (i = 0; i < MAX_PLAYERS; i++)
12171 int jx = stored_player[i].jx, jy = stored_player[i].jy;
12173 if (!stored_player[i].active || &stored_player[i] == player)
12176 min_x = MIN(min_x, jx);
12177 min_y = MIN(min_y, jy);
12178 max_x = MAX(max_x, jx);
12179 max_y = MAX(max_y, jy);
12182 return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
12185 static boolean AllPlayersInVisibleScreen()
12189 for (i = 0; i < MAX_PLAYERS; i++)
12191 int jx = stored_player[i].jx, jy = stored_player[i].jy;
12193 if (!stored_player[i].active)
12196 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12203 void ScrollLevel(int dx, int dy)
12205 int scroll_offset = 2 * TILEX_VAR;
12208 BlitBitmap(drawto_field, drawto_field,
12209 FX + TILEX_VAR * (dx == -1) - scroll_offset,
12210 FY + TILEY_VAR * (dy == -1) - scroll_offset,
12211 SXSIZE - TILEX_VAR * (dx != 0) + 2 * scroll_offset,
12212 SYSIZE - TILEY_VAR * (dy != 0) + 2 * scroll_offset,
12213 FX + TILEX_VAR * (dx == 1) - scroll_offset,
12214 FY + TILEY_VAR * (dy == 1) - scroll_offset);
12218 x = (dx == 1 ? BX1 : BX2);
12219 for (y = BY1; y <= BY2; y++)
12220 DrawScreenField(x, y);
12225 y = (dy == 1 ? BY1 : BY2);
12226 for (x = BX1; x <= BX2; x++)
12227 DrawScreenField(x, y);
12230 redraw_mask |= REDRAW_FIELD;
12233 static boolean canFallDown(struct PlayerInfo *player)
12235 int jx = player->jx, jy = player->jy;
12237 return (IN_LEV_FIELD(jx, jy + 1) &&
12238 (IS_FREE(jx, jy + 1) ||
12239 (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
12240 IS_WALKABLE_FROM(Feld[jx][jy], MV_DOWN) &&
12241 !IS_WALKABLE_INSIDE(Feld[jx][jy]));
12244 static boolean canPassField(int x, int y, int move_dir)
12246 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
12247 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
12248 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
12249 int nextx = x + dx;
12250 int nexty = y + dy;
12251 int element = Feld[x][y];
12253 return (IS_PASSABLE_FROM(element, opposite_dir) &&
12254 !CAN_MOVE(element) &&
12255 IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
12256 IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
12257 (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
12260 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
12262 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
12263 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
12264 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
12268 return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
12269 IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
12270 (IS_DIGGABLE(Feld[newx][newy]) ||
12271 IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
12272 canPassField(newx, newy, move_dir)));
12275 static void CheckGravityMovement(struct PlayerInfo *player)
12277 if (player->gravity && !player->programmed_action)
12279 int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
12280 int move_dir_vertical = player->effective_action & MV_VERTICAL;
12281 boolean player_is_snapping = (player->effective_action & JOY_BUTTON_1);
12282 int jx = player->jx, jy = player->jy;
12283 boolean player_is_moving_to_valid_field =
12284 (!player_is_snapping &&
12285 (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
12286 canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
12287 boolean player_can_fall_down = canFallDown(player);
12289 if (player_can_fall_down &&
12290 !player_is_moving_to_valid_field)
12291 player->programmed_action = MV_DOWN;
12295 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
12297 return CheckGravityMovement(player);
12299 if (player->gravity && !player->programmed_action)
12301 int jx = player->jx, jy = player->jy;
12302 boolean field_under_player_is_free =
12303 (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
12304 boolean player_is_standing_on_valid_field =
12305 (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
12306 (IS_WALKABLE(Feld[jx][jy]) &&
12307 !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
12309 if (field_under_player_is_free && !player_is_standing_on_valid_field)
12310 player->programmed_action = MV_DOWN;
12315 MovePlayerOneStep()
12316 -----------------------------------------------------------------------------
12317 dx, dy: direction (non-diagonal) to try to move the player to
12318 real_dx, real_dy: direction as read from input device (can be diagonal)
12321 boolean MovePlayerOneStep(struct PlayerInfo *player,
12322 int dx, int dy, int real_dx, int real_dy)
12324 int jx = player->jx, jy = player->jy;
12325 int new_jx = jx + dx, new_jy = jy + dy;
12327 boolean player_can_move = !player->cannot_move;
12329 if (!player->active || (!dx && !dy))
12330 return MP_NO_ACTION;
12332 player->MovDir = (dx < 0 ? MV_LEFT :
12333 dx > 0 ? MV_RIGHT :
12335 dy > 0 ? MV_DOWN : MV_NONE);
12337 if (!IN_LEV_FIELD(new_jx, new_jy))
12338 return MP_NO_ACTION;
12340 if (!player_can_move)
12342 if (player->MovPos == 0)
12344 player->is_moving = FALSE;
12345 player->is_digging = FALSE;
12346 player->is_collecting = FALSE;
12347 player->is_snapping = FALSE;
12348 player->is_pushing = FALSE;
12352 if (!options.network && game.centered_player_nr == -1 &&
12353 !AllPlayersInSight(player, new_jx, new_jy))
12354 return MP_NO_ACTION;
12356 can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
12357 if (can_move != MP_MOVING)
12360 /* check if DigField() has caused relocation of the player */
12361 if (player->jx != jx || player->jy != jy)
12362 return MP_NO_ACTION; /* <-- !!! CHECK THIS [-> MP_ACTION ?] !!! */
12364 StorePlayer[jx][jy] = 0;
12365 player->last_jx = jx;
12366 player->last_jy = jy;
12367 player->jx = new_jx;
12368 player->jy = new_jy;
12369 StorePlayer[new_jx][new_jy] = player->element_nr;
12371 if (player->move_delay_value_next != -1)
12373 player->move_delay_value = player->move_delay_value_next;
12374 player->move_delay_value_next = -1;
12378 (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
12380 player->step_counter++;
12382 PlayerVisit[jx][jy] = FrameCounter;
12384 player->is_moving = TRUE;
12387 /* should better be called in MovePlayer(), but this breaks some tapes */
12388 ScrollPlayer(player, SCROLL_INIT);
12394 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
12396 int jx = player->jx, jy = player->jy;
12397 int old_jx = jx, old_jy = jy;
12398 int moved = MP_NO_ACTION;
12400 if (!player->active)
12405 if (player->MovPos == 0)
12407 player->is_moving = FALSE;
12408 player->is_digging = FALSE;
12409 player->is_collecting = FALSE;
12410 player->is_snapping = FALSE;
12411 player->is_pushing = FALSE;
12417 if (player->move_delay > 0)
12420 player->move_delay = -1; /* set to "uninitialized" value */
12422 /* store if player is automatically moved to next field */
12423 player->is_auto_moving = (player->programmed_action != MV_NONE);
12425 /* remove the last programmed player action */
12426 player->programmed_action = 0;
12428 if (player->MovPos)
12430 /* should only happen if pre-1.2 tape recordings are played */
12431 /* this is only for backward compatibility */
12433 int original_move_delay_value = player->move_delay_value;
12436 printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%d]\n",
12440 /* scroll remaining steps with finest movement resolution */
12441 player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
12443 while (player->MovPos)
12445 ScrollPlayer(player, SCROLL_GO_ON);
12446 ScrollScreen(NULL, SCROLL_GO_ON);
12448 AdvanceFrameAndPlayerCounters(player->index_nr);
12451 BackToFront_WithFrameDelay(0);
12454 player->move_delay_value = original_move_delay_value;
12457 player->is_active = FALSE;
12459 if (player->last_move_dir & MV_HORIZONTAL)
12461 if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
12462 moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
12466 if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
12467 moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
12470 if (!moved && !player->is_active)
12472 player->is_moving = FALSE;
12473 player->is_digging = FALSE;
12474 player->is_collecting = FALSE;
12475 player->is_snapping = FALSE;
12476 player->is_pushing = FALSE;
12482 if (moved & MP_MOVING && !ScreenMovPos &&
12483 (player->index_nr == game.centered_player_nr ||
12484 game.centered_player_nr == -1))
12486 int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
12487 int offset = game.scroll_delay_value;
12489 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12491 /* actual player has left the screen -- scroll in that direction */
12492 if (jx != old_jx) /* player has moved horizontally */
12493 scroll_x += (jx - old_jx);
12494 else /* player has moved vertically */
12495 scroll_y += (jy - old_jy);
12499 if (jx != old_jx) /* player has moved horizontally */
12501 if ((player->MovDir == MV_LEFT && scroll_x > jx - MIDPOSX + offset) ||
12502 (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
12503 scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
12505 /* don't scroll over playfield boundaries */
12506 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
12507 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
12509 /* don't scroll more than one field at a time */
12510 scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
12512 /* don't scroll against the player's moving direction */
12513 if ((player->MovDir == MV_LEFT && scroll_x > old_scroll_x) ||
12514 (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
12515 scroll_x = old_scroll_x;
12517 else /* player has moved vertically */
12519 if ((player->MovDir == MV_UP && scroll_y > jy - MIDPOSY + offset) ||
12520 (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
12521 scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
12523 /* don't scroll over playfield boundaries */
12524 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
12525 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
12527 /* don't scroll more than one field at a time */
12528 scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
12530 /* don't scroll against the player's moving direction */
12531 if ((player->MovDir == MV_UP && scroll_y > old_scroll_y) ||
12532 (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
12533 scroll_y = old_scroll_y;
12537 if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
12539 if (!options.network && game.centered_player_nr == -1 &&
12540 !AllPlayersInVisibleScreen())
12542 scroll_x = old_scroll_x;
12543 scroll_y = old_scroll_y;
12547 ScrollScreen(player, SCROLL_INIT);
12548 ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
12553 player->StepFrame = 0;
12555 if (moved & MP_MOVING)
12557 if (old_jx != jx && old_jy == jy)
12558 player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
12559 else if (old_jx == jx && old_jy != jy)
12560 player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
12562 TEST_DrawLevelField(jx, jy); /* for "crumbled sand" */
12564 player->last_move_dir = player->MovDir;
12565 player->is_moving = TRUE;
12566 player->is_snapping = FALSE;
12567 player->is_switching = FALSE;
12568 player->is_dropping = FALSE;
12569 player->is_dropping_pressed = FALSE;
12570 player->drop_pressed_delay = 0;
12573 /* should better be called here than above, but this breaks some tapes */
12574 ScrollPlayer(player, SCROLL_INIT);
12579 CheckGravityMovementWhenNotMoving(player);
12581 player->is_moving = FALSE;
12583 /* at this point, the player is allowed to move, but cannot move right now
12584 (e.g. because of something blocking the way) -- ensure that the player
12585 is also allowed to move in the next frame (in old versions before 3.1.1,
12586 the player was forced to wait again for eight frames before next try) */
12588 if (game.engine_version >= VERSION_IDENT(3,1,1,0))
12589 player->move_delay = 0; /* allow direct movement in the next frame */
12592 if (player->move_delay == -1) /* not yet initialized by DigField() */
12593 player->move_delay = player->move_delay_value;
12595 if (game.engine_version < VERSION_IDENT(3,0,7,0))
12597 TestIfPlayerTouchesBadThing(jx, jy);
12598 TestIfPlayerTouchesCustomElement(jx, jy);
12601 if (!player->active)
12602 RemovePlayer(player);
12607 void ScrollPlayer(struct PlayerInfo *player, int mode)
12609 int jx = player->jx, jy = player->jy;
12610 int last_jx = player->last_jx, last_jy = player->last_jy;
12611 int move_stepsize = TILEX / player->move_delay_value;
12613 if (!player->active)
12616 if (player->MovPos == 0 && mode == SCROLL_GO_ON) /* player not moving */
12619 if (mode == SCROLL_INIT)
12621 player->actual_frame_counter = FrameCounter;
12622 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
12624 if ((player->block_last_field || player->block_delay_adjustment > 0) &&
12625 Feld[last_jx][last_jy] == EL_EMPTY)
12627 int last_field_block_delay = 0; /* start with no blocking at all */
12628 int block_delay_adjustment = player->block_delay_adjustment;
12630 /* if player blocks last field, add delay for exactly one move */
12631 if (player->block_last_field)
12633 last_field_block_delay += player->move_delay_value;
12635 /* when blocking enabled, prevent moving up despite gravity */
12636 if (player->gravity && player->MovDir == MV_UP)
12637 block_delay_adjustment = -1;
12640 /* add block delay adjustment (also possible when not blocking) */
12641 last_field_block_delay += block_delay_adjustment;
12643 Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
12644 MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
12647 if (player->MovPos != 0) /* player has not yet reached destination */
12650 else if (!FrameReached(&player->actual_frame_counter, 1))
12653 if (player->MovPos != 0)
12655 player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
12656 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
12658 /* before DrawPlayer() to draw correct player graphic for this case */
12659 if (player->MovPos == 0)
12660 CheckGravityMovement(player);
12663 if (player->MovPos == 0) /* player reached destination field */
12665 if (player->move_delay_reset_counter > 0)
12667 player->move_delay_reset_counter--;
12669 if (player->move_delay_reset_counter == 0)
12671 /* continue with normal speed after quickly moving through gate */
12672 HALVE_PLAYER_SPEED(player);
12674 /* be able to make the next move without delay */
12675 player->move_delay = 0;
12679 player->last_jx = jx;
12680 player->last_jy = jy;
12682 if (Feld[jx][jy] == EL_EXIT_OPEN ||
12683 Feld[jx][jy] == EL_EM_EXIT_OPEN ||
12684 Feld[jx][jy] == EL_EM_EXIT_OPENING ||
12685 Feld[jx][jy] == EL_STEEL_EXIT_OPEN ||
12686 Feld[jx][jy] == EL_EM_STEEL_EXIT_OPEN ||
12687 Feld[jx][jy] == EL_EM_STEEL_EXIT_OPENING ||
12688 Feld[jx][jy] == EL_SP_EXIT_OPEN ||
12689 Feld[jx][jy] == EL_SP_EXIT_OPENING) /* <-- special case */
12691 DrawPlayer(player); /* needed here only to cleanup last field */
12692 RemovePlayer(player);
12694 if (local_player->friends_still_needed == 0 ||
12695 IS_SP_ELEMENT(Feld[jx][jy]))
12696 PlayerWins(player);
12699 /* this breaks one level: "machine", level 000 */
12701 int move_direction = player->MovDir;
12702 int enter_side = MV_DIR_OPPOSITE(move_direction);
12703 int leave_side = move_direction;
12704 int old_jx = last_jx;
12705 int old_jy = last_jy;
12706 int old_element = Feld[old_jx][old_jy];
12707 int new_element = Feld[jx][jy];
12709 if (IS_CUSTOM_ELEMENT(old_element))
12710 CheckElementChangeByPlayer(old_jx, old_jy, old_element,
12712 player->index_bit, leave_side);
12714 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
12715 CE_PLAYER_LEAVES_X,
12716 player->index_bit, leave_side);
12718 if (IS_CUSTOM_ELEMENT(new_element))
12719 CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
12720 player->index_bit, enter_side);
12722 CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
12723 CE_PLAYER_ENTERS_X,
12724 player->index_bit, enter_side);
12726 CheckTriggeredElementChangeBySide(jx, jy, player->initial_element,
12727 CE_MOVE_OF_X, move_direction);
12730 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
12732 TestIfPlayerTouchesBadThing(jx, jy);
12733 TestIfPlayerTouchesCustomElement(jx, jy);
12735 /* needed because pushed element has not yet reached its destination,
12736 so it would trigger a change event at its previous field location */
12737 if (!player->is_pushing)
12738 TestIfElementTouchesCustomElement(jx, jy); /* for empty space */
12740 if (!player->active)
12741 RemovePlayer(player);
12744 if (!local_player->LevelSolved && level.use_step_counter)
12754 if (TimeLeft <= 10 && setup.time_limit)
12755 PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
12757 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
12759 DisplayGameControlValues();
12761 if (!TimeLeft && setup.time_limit)
12762 for (i = 0; i < MAX_PLAYERS; i++)
12763 KillPlayer(&stored_player[i]);
12765 else if (game.no_time_limit && !AllPlayersGone) /* level w/o time limit */
12767 game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
12769 DisplayGameControlValues();
12773 if (tape.single_step && tape.recording && !tape.pausing &&
12774 !player->programmed_action)
12775 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
12777 if (!player->programmed_action)
12778 CheckSaveEngineSnapshot(player);
12782 void ScrollScreen(struct PlayerInfo *player, int mode)
12784 static unsigned int screen_frame_counter = 0;
12786 if (mode == SCROLL_INIT)
12788 /* set scrolling step size according to actual player's moving speed */
12789 ScrollStepSize = TILEX / player->move_delay_value;
12791 screen_frame_counter = FrameCounter;
12792 ScreenMovDir = player->MovDir;
12793 ScreenMovPos = player->MovPos;
12794 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
12797 else if (!FrameReached(&screen_frame_counter, 1))
12802 ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
12803 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
12804 redraw_mask |= REDRAW_FIELD;
12807 ScreenMovDir = MV_NONE;
12810 void TestIfPlayerTouchesCustomElement(int x, int y)
12812 static int xy[4][2] =
12819 static int trigger_sides[4][2] =
12821 /* center side border side */
12822 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
12823 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
12824 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
12825 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
12827 static int touch_dir[4] =
12829 MV_LEFT | MV_RIGHT,
12834 int center_element = Feld[x][y]; /* should always be non-moving! */
12837 for (i = 0; i < NUM_DIRECTIONS; i++)
12839 int xx = x + xy[i][0];
12840 int yy = y + xy[i][1];
12841 int center_side = trigger_sides[i][0];
12842 int border_side = trigger_sides[i][1];
12843 int border_element;
12845 if (!IN_LEV_FIELD(xx, yy))
12848 if (IS_PLAYER(x, y)) /* player found at center element */
12850 struct PlayerInfo *player = PLAYERINFO(x, y);
12852 if (game.engine_version < VERSION_IDENT(3,0,7,0))
12853 border_element = Feld[xx][yy]; /* may be moving! */
12854 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
12855 border_element = Feld[xx][yy];
12856 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
12857 border_element = MovingOrBlocked2Element(xx, yy);
12859 continue; /* center and border element do not touch */
12861 CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
12862 player->index_bit, border_side);
12863 CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
12864 CE_PLAYER_TOUCHES_X,
12865 player->index_bit, border_side);
12868 /* use player element that is initially defined in the level playfield,
12869 not the player element that corresponds to the runtime player number
12870 (example: a level that contains EL_PLAYER_3 as the only player would
12871 incorrectly give EL_PLAYER_1 for "player->element_nr") */
12872 int player_element = PLAYERINFO(x, y)->initial_element;
12874 CheckElementChangeBySide(xx, yy, border_element, player_element,
12875 CE_TOUCHING_X, border_side);
12878 else if (IS_PLAYER(xx, yy)) /* player found at border element */
12880 struct PlayerInfo *player = PLAYERINFO(xx, yy);
12882 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
12884 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
12885 continue; /* center and border element do not touch */
12888 CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
12889 player->index_bit, center_side);
12890 CheckTriggeredElementChangeByPlayer(x, y, center_element,
12891 CE_PLAYER_TOUCHES_X,
12892 player->index_bit, center_side);
12895 /* use player element that is initially defined in the level playfield,
12896 not the player element that corresponds to the runtime player number
12897 (example: a level that contains EL_PLAYER_3 as the only player would
12898 incorrectly give EL_PLAYER_1 for "player->element_nr") */
12899 int player_element = PLAYERINFO(xx, yy)->initial_element;
12901 CheckElementChangeBySide(x, y, center_element, player_element,
12902 CE_TOUCHING_X, center_side);
12910 void TestIfElementTouchesCustomElement(int x, int y)
12912 static int xy[4][2] =
12919 static int trigger_sides[4][2] =
12921 /* center side border side */
12922 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
12923 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
12924 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
12925 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
12927 static int touch_dir[4] =
12929 MV_LEFT | MV_RIGHT,
12934 boolean change_center_element = FALSE;
12935 int center_element = Feld[x][y]; /* should always be non-moving! */
12936 int border_element_old[NUM_DIRECTIONS];
12939 for (i = 0; i < NUM_DIRECTIONS; i++)
12941 int xx = x + xy[i][0];
12942 int yy = y + xy[i][1];
12943 int border_element;
12945 border_element_old[i] = -1;
12947 if (!IN_LEV_FIELD(xx, yy))
12950 if (game.engine_version < VERSION_IDENT(3,0,7,0))
12951 border_element = Feld[xx][yy]; /* may be moving! */
12952 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
12953 border_element = Feld[xx][yy];
12954 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
12955 border_element = MovingOrBlocked2Element(xx, yy);
12957 continue; /* center and border element do not touch */
12959 border_element_old[i] = border_element;
12962 for (i = 0; i < NUM_DIRECTIONS; i++)
12964 int xx = x + xy[i][0];
12965 int yy = y + xy[i][1];
12966 int center_side = trigger_sides[i][0];
12967 int border_element = border_element_old[i];
12969 if (border_element == -1)
12972 /* check for change of border element */
12973 CheckElementChangeBySide(xx, yy, border_element, center_element,
12974 CE_TOUCHING_X, center_side);
12976 /* (center element cannot be player, so we dont have to check this here) */
12979 for (i = 0; i < NUM_DIRECTIONS; i++)
12981 int xx = x + xy[i][0];
12982 int yy = y + xy[i][1];
12983 int border_side = trigger_sides[i][1];
12984 int border_element = border_element_old[i];
12986 if (border_element == -1)
12989 /* check for change of center element (but change it only once) */
12990 if (!change_center_element)
12991 change_center_element =
12992 CheckElementChangeBySide(x, y, center_element, border_element,
12993 CE_TOUCHING_X, border_side);
12995 if (IS_PLAYER(xx, yy))
12997 /* use player element that is initially defined in the level playfield,
12998 not the player element that corresponds to the runtime player number
12999 (example: a level that contains EL_PLAYER_3 as the only player would
13000 incorrectly give EL_PLAYER_1 for "player->element_nr") */
13001 int player_element = PLAYERINFO(xx, yy)->initial_element;
13003 CheckElementChangeBySide(x, y, center_element, player_element,
13004 CE_TOUCHING_X, border_side);
13009 void TestIfElementHitsCustomElement(int x, int y, int direction)
13011 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
13012 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
13013 int hitx = x + dx, hity = y + dy;
13014 int hitting_element = Feld[x][y];
13015 int touched_element;
13017 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
13020 touched_element = (IN_LEV_FIELD(hitx, hity) ?
13021 MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
13023 if (IN_LEV_FIELD(hitx, hity))
13025 int opposite_direction = MV_DIR_OPPOSITE(direction);
13026 int hitting_side = direction;
13027 int touched_side = opposite_direction;
13028 boolean object_hit = (!IS_MOVING(hitx, hity) ||
13029 MovDir[hitx][hity] != direction ||
13030 ABS(MovPos[hitx][hity]) <= TILEY / 2);
13036 CheckElementChangeBySide(x, y, hitting_element, touched_element,
13037 CE_HITTING_X, touched_side);
13039 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13040 CE_HIT_BY_X, hitting_side);
13042 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13043 CE_HIT_BY_SOMETHING, opposite_direction);
13045 if (IS_PLAYER(hitx, hity))
13047 /* use player element that is initially defined in the level playfield,
13048 not the player element that corresponds to the runtime player number
13049 (example: a level that contains EL_PLAYER_3 as the only player would
13050 incorrectly give EL_PLAYER_1 for "player->element_nr") */
13051 int player_element = PLAYERINFO(hitx, hity)->initial_element;
13053 CheckElementChangeBySide(x, y, hitting_element, player_element,
13054 CE_HITTING_X, touched_side);
13059 /* "hitting something" is also true when hitting the playfield border */
13060 CheckElementChangeBySide(x, y, hitting_element, touched_element,
13061 CE_HITTING_SOMETHING, direction);
13064 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
13066 int i, kill_x = -1, kill_y = -1;
13068 int bad_element = -1;
13069 static int test_xy[4][2] =
13076 static int test_dir[4] =
13084 for (i = 0; i < NUM_DIRECTIONS; i++)
13086 int test_x, test_y, test_move_dir, test_element;
13088 test_x = good_x + test_xy[i][0];
13089 test_y = good_y + test_xy[i][1];
13091 if (!IN_LEV_FIELD(test_x, test_y))
13095 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13097 test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
13099 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
13100 2nd case: DONT_TOUCH style bad thing does not move away from good thing
13102 if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
13103 (DONT_TOUCH(test_element) && test_move_dir != test_dir[i]))
13107 bad_element = test_element;
13113 if (kill_x != -1 || kill_y != -1)
13115 if (IS_PLAYER(good_x, good_y))
13117 struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
13119 if (player->shield_deadly_time_left > 0 &&
13120 !IS_INDESTRUCTIBLE(bad_element))
13121 Bang(kill_x, kill_y);
13122 else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
13123 KillPlayer(player);
13126 Bang(good_x, good_y);
13130 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
13132 int i, kill_x = -1, kill_y = -1;
13133 int bad_element = Feld[bad_x][bad_y];
13134 static int test_xy[4][2] =
13141 static int touch_dir[4] =
13143 MV_LEFT | MV_RIGHT,
13148 static int test_dir[4] =
13156 if (bad_element == EL_EXPLOSION) /* skip just exploding bad things */
13159 for (i = 0; i < NUM_DIRECTIONS; i++)
13161 int test_x, test_y, test_move_dir, test_element;
13163 test_x = bad_x + test_xy[i][0];
13164 test_y = bad_y + test_xy[i][1];
13166 if (!IN_LEV_FIELD(test_x, test_y))
13170 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13172 test_element = Feld[test_x][test_y];
13174 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
13175 2nd case: DONT_TOUCH style bad thing does not move away from good thing
13177 if ((DONT_RUN_INTO(bad_element) && bad_move_dir == test_dir[i]) ||
13178 (DONT_TOUCH(bad_element) && test_move_dir != test_dir[i]))
13180 /* good thing is player or penguin that does not move away */
13181 if (IS_PLAYER(test_x, test_y))
13183 struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
13185 if (bad_element == EL_ROBOT && player->is_moving)
13186 continue; /* robot does not kill player if he is moving */
13188 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13190 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
13191 continue; /* center and border element do not touch */
13199 else if (test_element == EL_PENGUIN)
13209 if (kill_x != -1 || kill_y != -1)
13211 if (IS_PLAYER(kill_x, kill_y))
13213 struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
13215 if (player->shield_deadly_time_left > 0 &&
13216 !IS_INDESTRUCTIBLE(bad_element))
13217 Bang(bad_x, bad_y);
13218 else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
13219 KillPlayer(player);
13222 Bang(kill_x, kill_y);
13226 void TestIfGoodThingGetsHitByBadThing(int bad_x, int bad_y, int bad_move_dir)
13228 int bad_element = Feld[bad_x][bad_y];
13229 int dx = (bad_move_dir == MV_LEFT ? -1 : bad_move_dir == MV_RIGHT ? +1 : 0);
13230 int dy = (bad_move_dir == MV_UP ? -1 : bad_move_dir == MV_DOWN ? +1 : 0);
13231 int test_x = bad_x + dx, test_y = bad_y + dy;
13232 int test_move_dir, test_element;
13233 int kill_x = -1, kill_y = -1;
13235 if (!IN_LEV_FIELD(test_x, test_y))
13239 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13241 test_element = Feld[test_x][test_y];
13243 if (test_move_dir != bad_move_dir)
13245 /* good thing can be player or penguin that does not move away */
13246 if (IS_PLAYER(test_x, test_y))
13248 struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
13250 /* (note: in comparison to DONT_RUN_TO and DONT_TOUCH, also handle the
13251 player as being hit when he is moving towards the bad thing, because
13252 the "get hit by" condition would be lost after the player stops) */
13253 if (player->MovPos != 0 && player->MovDir == bad_move_dir)
13254 return; /* player moves away from bad thing */
13259 else if (test_element == EL_PENGUIN)
13266 if (kill_x != -1 || kill_y != -1)
13268 if (IS_PLAYER(kill_x, kill_y))
13270 struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
13272 if (player->shield_deadly_time_left > 0 &&
13273 !IS_INDESTRUCTIBLE(bad_element))
13274 Bang(bad_x, bad_y);
13275 else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
13276 KillPlayer(player);
13279 Bang(kill_x, kill_y);
13283 void TestIfPlayerTouchesBadThing(int x, int y)
13285 TestIfGoodThingHitsBadThing(x, y, MV_NONE);
13288 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
13290 TestIfGoodThingHitsBadThing(x, y, move_dir);
13293 void TestIfBadThingTouchesPlayer(int x, int y)
13295 TestIfBadThingHitsGoodThing(x, y, MV_NONE);
13298 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
13300 TestIfBadThingHitsGoodThing(x, y, move_dir);
13303 void TestIfFriendTouchesBadThing(int x, int y)
13305 TestIfGoodThingHitsBadThing(x, y, MV_NONE);
13308 void TestIfBadThingTouchesFriend(int x, int y)
13310 TestIfBadThingHitsGoodThing(x, y, MV_NONE);
13313 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
13315 int i, kill_x = bad_x, kill_y = bad_y;
13316 static int xy[4][2] =
13324 for (i = 0; i < NUM_DIRECTIONS; i++)
13328 x = bad_x + xy[i][0];
13329 y = bad_y + xy[i][1];
13330 if (!IN_LEV_FIELD(x, y))
13333 element = Feld[x][y];
13334 if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
13335 element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
13343 if (kill_x != bad_x || kill_y != bad_y)
13344 Bang(bad_x, bad_y);
13347 void KillPlayer(struct PlayerInfo *player)
13349 int jx = player->jx, jy = player->jy;
13351 if (!player->active)
13355 printf("::: 0: killed == %d, active == %d, reanimated == %d\n",
13356 player->killed, player->active, player->reanimated);
13359 /* the following code was introduced to prevent an infinite loop when calling
13361 -> CheckTriggeredElementChangeExt()
13362 -> ExecuteCustomElementAction()
13364 -> (infinitely repeating the above sequence of function calls)
13365 which occurs when killing the player while having a CE with the setting
13366 "kill player X when explosion of <player X>"; the solution using a new
13367 field "player->killed" was chosen for backwards compatibility, although
13368 clever use of the fields "player->active" etc. would probably also work */
13370 if (player->killed)
13374 player->killed = TRUE;
13376 /* remove accessible field at the player's position */
13377 Feld[jx][jy] = EL_EMPTY;
13379 /* deactivate shield (else Bang()/Explode() would not work right) */
13380 player->shield_normal_time_left = 0;
13381 player->shield_deadly_time_left = 0;
13384 printf("::: 1: killed == %d, active == %d, reanimated == %d\n",
13385 player->killed, player->active, player->reanimated);
13391 printf("::: 2: killed == %d, active == %d, reanimated == %d\n",
13392 player->killed, player->active, player->reanimated);
13395 if (player->reanimated) /* killed player may have been reanimated */
13396 player->killed = player->reanimated = FALSE;
13398 BuryPlayer(player);
13401 static void KillPlayerUnlessEnemyProtected(int x, int y)
13403 if (!PLAYER_ENEMY_PROTECTED(x, y))
13404 KillPlayer(PLAYERINFO(x, y));
13407 static void KillPlayerUnlessExplosionProtected(int x, int y)
13409 if (!PLAYER_EXPLOSION_PROTECTED(x, y))
13410 KillPlayer(PLAYERINFO(x, y));
13413 void BuryPlayer(struct PlayerInfo *player)
13415 int jx = player->jx, jy = player->jy;
13417 if (!player->active)
13420 PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
13421 PlayLevelSound(jx, jy, SND_GAME_LOSING);
13423 player->GameOver = TRUE;
13424 RemovePlayer(player);
13427 void RemovePlayer(struct PlayerInfo *player)
13429 int jx = player->jx, jy = player->jy;
13430 int i, found = FALSE;
13432 player->present = FALSE;
13433 player->active = FALSE;
13435 if (!ExplodeField[jx][jy])
13436 StorePlayer[jx][jy] = 0;
13438 if (player->is_moving)
13439 TEST_DrawLevelField(player->last_jx, player->last_jy);
13441 for (i = 0; i < MAX_PLAYERS; i++)
13442 if (stored_player[i].active)
13446 AllPlayersGone = TRUE;
13452 static void setFieldForSnapping(int x, int y, int element, int direction)
13454 struct ElementInfo *ei = &element_info[element];
13455 int direction_bit = MV_DIR_TO_BIT(direction);
13456 int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
13457 int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
13458 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
13460 Feld[x][y] = EL_ELEMENT_SNAPPING;
13461 MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
13463 ResetGfxAnimation(x, y);
13465 GfxElement[x][y] = element;
13466 GfxAction[x][y] = action;
13467 GfxDir[x][y] = direction;
13468 GfxFrame[x][y] = -1;
13472 =============================================================================
13473 checkDiagonalPushing()
13474 -----------------------------------------------------------------------------
13475 check if diagonal input device direction results in pushing of object
13476 (by checking if the alternative direction is walkable, diggable, ...)
13477 =============================================================================
13480 static boolean checkDiagonalPushing(struct PlayerInfo *player,
13481 int x, int y, int real_dx, int real_dy)
13483 int jx, jy, dx, dy, xx, yy;
13485 if (real_dx == 0 || real_dy == 0) /* no diagonal direction => push */
13488 /* diagonal direction: check alternative direction */
13493 xx = jx + (dx == 0 ? real_dx : 0);
13494 yy = jy + (dy == 0 ? real_dy : 0);
13496 return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
13500 =============================================================================
13502 -----------------------------------------------------------------------------
13503 x, y: field next to player (non-diagonal) to try to dig to
13504 real_dx, real_dy: direction as read from input device (can be diagonal)
13505 =============================================================================
13508 static int DigField(struct PlayerInfo *player,
13509 int oldx, int oldy, int x, int y,
13510 int real_dx, int real_dy, int mode)
13512 boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
13513 boolean player_was_pushing = player->is_pushing;
13514 boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
13515 boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
13516 int jx = oldx, jy = oldy;
13517 int dx = x - jx, dy = y - jy;
13518 int nextx = x + dx, nexty = y + dy;
13519 int move_direction = (dx == -1 ? MV_LEFT :
13520 dx == +1 ? MV_RIGHT :
13522 dy == +1 ? MV_DOWN : MV_NONE);
13523 int opposite_direction = MV_DIR_OPPOSITE(move_direction);
13524 int dig_side = MV_DIR_OPPOSITE(move_direction);
13525 int old_element = Feld[jx][jy];
13526 int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
13529 if (is_player) /* function can also be called by EL_PENGUIN */
13531 if (player->MovPos == 0)
13533 player->is_digging = FALSE;
13534 player->is_collecting = FALSE;
13537 if (player->MovPos == 0) /* last pushing move finished */
13538 player->is_pushing = FALSE;
13540 if (mode == DF_NO_PUSH) /* player just stopped pushing */
13542 player->is_switching = FALSE;
13543 player->push_delay = -1;
13545 return MP_NO_ACTION;
13549 if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
13550 old_element = Back[jx][jy];
13552 /* in case of element dropped at player position, check background */
13553 else if (Back[jx][jy] != EL_EMPTY &&
13554 game.engine_version >= VERSION_IDENT(2,2,0,0))
13555 old_element = Back[jx][jy];
13557 if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
13558 return MP_NO_ACTION; /* field has no opening in this direction */
13560 if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
13561 return MP_NO_ACTION; /* field has no opening in this direction */
13563 if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
13567 Feld[jx][jy] = player->artwork_element;
13568 InitMovingField(jx, jy, MV_DOWN);
13569 Store[jx][jy] = EL_ACID;
13570 ContinueMoving(jx, jy);
13571 BuryPlayer(player);
13573 return MP_DONT_RUN_INTO;
13576 if (player_can_move && DONT_RUN_INTO(element))
13578 TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
13580 return MP_DONT_RUN_INTO;
13583 if (IS_MOVING(x, y) || IS_PLAYER(x, y))
13584 return MP_NO_ACTION;
13586 collect_count = element_info[element].collect_count_initial;
13588 if (!is_player && !IS_COLLECTIBLE(element)) /* penguin cannot collect it */
13589 return MP_NO_ACTION;
13591 if (game.engine_version < VERSION_IDENT(2,2,0,0))
13592 player_can_move = player_can_move_or_snap;
13594 if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
13595 game.engine_version >= VERSION_IDENT(2,2,0,0))
13597 CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
13598 player->index_bit, dig_side);
13599 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13600 player->index_bit, dig_side);
13602 if (element == EL_DC_LANDMINE)
13605 if (Feld[x][y] != element) /* field changed by snapping */
13608 return MP_NO_ACTION;
13611 if (player->gravity && is_player && !player->is_auto_moving &&
13612 canFallDown(player) && move_direction != MV_DOWN &&
13613 !canMoveToValidFieldWithGravity(jx, jy, move_direction))
13614 return MP_NO_ACTION; /* player cannot walk here due to gravity */
13616 if (player_can_move &&
13617 IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
13619 int sound_element = SND_ELEMENT(element);
13620 int sound_action = ACTION_WALKING;
13622 if (IS_RND_GATE(element))
13624 if (!player->key[RND_GATE_NR(element)])
13625 return MP_NO_ACTION;
13627 else if (IS_RND_GATE_GRAY(element))
13629 if (!player->key[RND_GATE_GRAY_NR(element)])
13630 return MP_NO_ACTION;
13632 else if (IS_RND_GATE_GRAY_ACTIVE(element))
13634 if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
13635 return MP_NO_ACTION;
13637 else if (element == EL_EXIT_OPEN ||
13638 element == EL_EM_EXIT_OPEN ||
13639 element == EL_EM_EXIT_OPENING ||
13640 element == EL_STEEL_EXIT_OPEN ||
13641 element == EL_EM_STEEL_EXIT_OPEN ||
13642 element == EL_EM_STEEL_EXIT_OPENING ||
13643 element == EL_SP_EXIT_OPEN ||
13644 element == EL_SP_EXIT_OPENING)
13646 sound_action = ACTION_PASSING; /* player is passing exit */
13648 else if (element == EL_EMPTY)
13650 sound_action = ACTION_MOVING; /* nothing to walk on */
13653 /* play sound from background or player, whatever is available */
13654 if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
13655 PlayLevelSoundElementAction(x, y, sound_element, sound_action);
13657 PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
13659 else if (player_can_move &&
13660 IS_PASSABLE(element) && canPassField(x, y, move_direction))
13662 if (!ACCESS_FROM(element, opposite_direction))
13663 return MP_NO_ACTION; /* field not accessible from this direction */
13665 if (CAN_MOVE(element)) /* only fixed elements can be passed! */
13666 return MP_NO_ACTION;
13668 if (IS_EM_GATE(element))
13670 if (!player->key[EM_GATE_NR(element)])
13671 return MP_NO_ACTION;
13673 else if (IS_EM_GATE_GRAY(element))
13675 if (!player->key[EM_GATE_GRAY_NR(element)])
13676 return MP_NO_ACTION;
13678 else if (IS_EM_GATE_GRAY_ACTIVE(element))
13680 if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
13681 return MP_NO_ACTION;
13683 else if (IS_EMC_GATE(element))
13685 if (!player->key[EMC_GATE_NR(element)])
13686 return MP_NO_ACTION;
13688 else if (IS_EMC_GATE_GRAY(element))
13690 if (!player->key[EMC_GATE_GRAY_NR(element)])
13691 return MP_NO_ACTION;
13693 else if (IS_EMC_GATE_GRAY_ACTIVE(element))
13695 if (!player->key[EMC_GATE_GRAY_ACTIVE_NR(element)])
13696 return MP_NO_ACTION;
13698 else if (element == EL_DC_GATE_WHITE ||
13699 element == EL_DC_GATE_WHITE_GRAY ||
13700 element == EL_DC_GATE_WHITE_GRAY_ACTIVE)
13702 if (player->num_white_keys == 0)
13703 return MP_NO_ACTION;
13705 player->num_white_keys--;
13707 else if (IS_SP_PORT(element))
13709 if (element == EL_SP_GRAVITY_PORT_LEFT ||
13710 element == EL_SP_GRAVITY_PORT_RIGHT ||
13711 element == EL_SP_GRAVITY_PORT_UP ||
13712 element == EL_SP_GRAVITY_PORT_DOWN)
13713 player->gravity = !player->gravity;
13714 else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
13715 element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
13716 element == EL_SP_GRAVITY_ON_PORT_UP ||
13717 element == EL_SP_GRAVITY_ON_PORT_DOWN)
13718 player->gravity = TRUE;
13719 else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
13720 element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
13721 element == EL_SP_GRAVITY_OFF_PORT_UP ||
13722 element == EL_SP_GRAVITY_OFF_PORT_DOWN)
13723 player->gravity = FALSE;
13726 /* automatically move to the next field with double speed */
13727 player->programmed_action = move_direction;
13729 if (player->move_delay_reset_counter == 0)
13731 player->move_delay_reset_counter = 2; /* two double speed steps */
13733 DOUBLE_PLAYER_SPEED(player);
13736 PlayLevelSoundAction(x, y, ACTION_PASSING);
13738 else if (player_can_move_or_snap && IS_DIGGABLE(element))
13742 if (mode != DF_SNAP)
13744 GfxElement[x][y] = GFX_ELEMENT(element);
13745 player->is_digging = TRUE;
13748 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
13750 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
13751 player->index_bit, dig_side);
13753 if (mode == DF_SNAP)
13755 if (level.block_snap_field)
13756 setFieldForSnapping(x, y, element, move_direction);
13758 TestIfElementTouchesCustomElement(x, y); /* for empty space */
13760 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13761 player->index_bit, dig_side);
13764 else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
13768 if (is_player && mode != DF_SNAP)
13770 GfxElement[x][y] = element;
13771 player->is_collecting = TRUE;
13774 if (element == EL_SPEED_PILL)
13776 player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
13778 else if (element == EL_EXTRA_TIME && level.time > 0)
13780 TimeLeft += level.extra_time;
13782 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
13784 DisplayGameControlValues();
13786 else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
13788 player->shield_normal_time_left += level.shield_normal_time;
13789 if (element == EL_SHIELD_DEADLY)
13790 player->shield_deadly_time_left += level.shield_deadly_time;
13792 else if (element == EL_DYNAMITE ||
13793 element == EL_EM_DYNAMITE ||
13794 element == EL_SP_DISK_RED)
13796 if (player->inventory_size < MAX_INVENTORY_SIZE)
13797 player->inventory_element[player->inventory_size++] = element;
13799 DrawGameDoorValues();
13801 else if (element == EL_DYNABOMB_INCREASE_NUMBER)
13803 player->dynabomb_count++;
13804 player->dynabombs_left++;
13806 else if (element == EL_DYNABOMB_INCREASE_SIZE)
13808 player->dynabomb_size++;
13810 else if (element == EL_DYNABOMB_INCREASE_POWER)
13812 player->dynabomb_xl = TRUE;
13814 else if (IS_KEY(element))
13816 player->key[KEY_NR(element)] = TRUE;
13818 DrawGameDoorValues();
13820 else if (element == EL_DC_KEY_WHITE)
13822 player->num_white_keys++;
13824 /* display white keys? */
13825 /* DrawGameDoorValues(); */
13827 else if (IS_ENVELOPE(element))
13829 player->show_envelope = element;
13831 else if (element == EL_EMC_LENSES)
13833 game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
13835 RedrawAllInvisibleElementsForLenses();
13837 else if (element == EL_EMC_MAGNIFIER)
13839 game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
13841 RedrawAllInvisibleElementsForMagnifier();
13843 else if (IS_DROPPABLE(element) ||
13844 IS_THROWABLE(element)) /* can be collected and dropped */
13848 if (collect_count == 0)
13849 player->inventory_infinite_element = element;
13851 for (i = 0; i < collect_count; i++)
13852 if (player->inventory_size < MAX_INVENTORY_SIZE)
13853 player->inventory_element[player->inventory_size++] = element;
13855 DrawGameDoorValues();
13857 else if (collect_count > 0)
13859 local_player->gems_still_needed -= collect_count;
13860 if (local_player->gems_still_needed < 0)
13861 local_player->gems_still_needed = 0;
13863 game.snapshot.collected_item = TRUE;
13865 game_panel_controls[GAME_PANEL_GEMS].value = local_player->gems_still_needed;
13867 DisplayGameControlValues();
13870 RaiseScoreElement(element);
13871 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
13874 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
13875 player->index_bit, dig_side);
13877 if (mode == DF_SNAP)
13879 if (level.block_snap_field)
13880 setFieldForSnapping(x, y, element, move_direction);
13882 TestIfElementTouchesCustomElement(x, y); /* for empty space */
13884 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13885 player->index_bit, dig_side);
13888 else if (player_can_move_or_snap && IS_PUSHABLE(element))
13890 if (mode == DF_SNAP && element != EL_BD_ROCK)
13891 return MP_NO_ACTION;
13893 if (CAN_FALL(element) && dy)
13894 return MP_NO_ACTION;
13896 if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
13897 !(element == EL_SPRING && level.use_spring_bug))
13898 return MP_NO_ACTION;
13900 if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
13901 ((move_direction & MV_VERTICAL &&
13902 ((element_info[element].move_pattern & MV_LEFT &&
13903 IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
13904 (element_info[element].move_pattern & MV_RIGHT &&
13905 IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
13906 (move_direction & MV_HORIZONTAL &&
13907 ((element_info[element].move_pattern & MV_UP &&
13908 IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
13909 (element_info[element].move_pattern & MV_DOWN &&
13910 IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
13911 return MP_NO_ACTION;
13913 /* do not push elements already moving away faster than player */
13914 if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
13915 ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
13916 return MP_NO_ACTION;
13918 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
13920 if (player->push_delay_value == -1 || !player_was_pushing)
13921 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13923 else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
13925 if (player->push_delay_value == -1)
13926 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13928 else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
13930 if (!player->is_pushing)
13931 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13934 player->is_pushing = TRUE;
13935 player->is_active = TRUE;
13937 if (!(IN_LEV_FIELD(nextx, nexty) &&
13938 (IS_FREE(nextx, nexty) ||
13939 (IS_SB_ELEMENT(element) &&
13940 Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY) ||
13941 (IS_CUSTOM_ELEMENT(element) &&
13942 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty)))))
13943 return MP_NO_ACTION;
13945 if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
13946 return MP_NO_ACTION;
13948 if (player->push_delay == -1) /* new pushing; restart delay */
13949 player->push_delay = 0;
13951 if (player->push_delay < player->push_delay_value &&
13952 !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
13953 element != EL_SPRING && element != EL_BALLOON)
13955 /* make sure that there is no move delay before next try to push */
13956 if (game.engine_version >= VERSION_IDENT(3,0,7,1))
13957 player->move_delay = 0;
13959 return MP_NO_ACTION;
13962 if (IS_CUSTOM_ELEMENT(element) &&
13963 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty))
13965 if (!DigFieldByCE(nextx, nexty, element))
13966 return MP_NO_ACTION;
13969 if (IS_SB_ELEMENT(element))
13971 if (element == EL_SOKOBAN_FIELD_FULL)
13973 Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
13974 local_player->sokobanfields_still_needed++;
13977 if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
13979 Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
13980 local_player->sokobanfields_still_needed--;
13983 Feld[x][y] = EL_SOKOBAN_OBJECT;
13985 if (Back[x][y] == Back[nextx][nexty])
13986 PlayLevelSoundAction(x, y, ACTION_PUSHING);
13987 else if (Back[x][y] != 0)
13988 PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
13991 PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
13994 if (local_player->sokobanfields_still_needed == 0 &&
13995 (game.emulation == EMU_SOKOBAN || level.auto_exit_sokoban))
13997 PlayerWins(player);
13999 PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
14003 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14005 InitMovingField(x, y, move_direction);
14006 GfxAction[x][y] = ACTION_PUSHING;
14008 if (mode == DF_SNAP)
14009 ContinueMoving(x, y);
14011 MovPos[x][y] = (dx != 0 ? dx : dy);
14013 Pushed[x][y] = TRUE;
14014 Pushed[nextx][nexty] = TRUE;
14016 if (game.engine_version < VERSION_IDENT(2,2,0,7))
14017 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14019 player->push_delay_value = -1; /* get new value later */
14021 /* check for element change _after_ element has been pushed */
14022 if (game.use_change_when_pushing_bug)
14024 CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
14025 player->index_bit, dig_side);
14026 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
14027 player->index_bit, dig_side);
14030 else if (IS_SWITCHABLE(element))
14032 if (PLAYER_SWITCHING(player, x, y))
14034 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14035 player->index_bit, dig_side);
14040 player->is_switching = TRUE;
14041 player->switch_x = x;
14042 player->switch_y = y;
14044 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
14046 if (element == EL_ROBOT_WHEEL)
14048 Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
14052 game.robot_wheel_active = TRUE;
14054 TEST_DrawLevelField(x, y);
14056 else if (element == EL_SP_TERMINAL)
14060 SCAN_PLAYFIELD(xx, yy)
14062 if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
14066 else if (Feld[xx][yy] == EL_SP_TERMINAL)
14068 Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
14070 ResetGfxAnimation(xx, yy);
14071 TEST_DrawLevelField(xx, yy);
14075 else if (IS_BELT_SWITCH(element))
14077 ToggleBeltSwitch(x, y);
14079 else if (element == EL_SWITCHGATE_SWITCH_UP ||
14080 element == EL_SWITCHGATE_SWITCH_DOWN ||
14081 element == EL_DC_SWITCHGATE_SWITCH_UP ||
14082 element == EL_DC_SWITCHGATE_SWITCH_DOWN)
14084 ToggleSwitchgateSwitch(x, y);
14086 else if (element == EL_LIGHT_SWITCH ||
14087 element == EL_LIGHT_SWITCH_ACTIVE)
14089 ToggleLightSwitch(x, y);
14091 else if (element == EL_TIMEGATE_SWITCH ||
14092 element == EL_DC_TIMEGATE_SWITCH)
14094 ActivateTimegateSwitch(x, y);
14096 else if (element == EL_BALLOON_SWITCH_LEFT ||
14097 element == EL_BALLOON_SWITCH_RIGHT ||
14098 element == EL_BALLOON_SWITCH_UP ||
14099 element == EL_BALLOON_SWITCH_DOWN ||
14100 element == EL_BALLOON_SWITCH_NONE ||
14101 element == EL_BALLOON_SWITCH_ANY)
14103 game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT ? MV_LEFT :
14104 element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
14105 element == EL_BALLOON_SWITCH_UP ? MV_UP :
14106 element == EL_BALLOON_SWITCH_DOWN ? MV_DOWN :
14107 element == EL_BALLOON_SWITCH_NONE ? MV_NONE :
14110 else if (element == EL_LAMP)
14112 Feld[x][y] = EL_LAMP_ACTIVE;
14113 local_player->lights_still_needed--;
14115 ResetGfxAnimation(x, y);
14116 TEST_DrawLevelField(x, y);
14118 else if (element == EL_TIME_ORB_FULL)
14120 Feld[x][y] = EL_TIME_ORB_EMPTY;
14122 if (level.time > 0 || level.use_time_orb_bug)
14124 TimeLeft += level.time_orb_time;
14125 game.no_time_limit = FALSE;
14127 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
14129 DisplayGameControlValues();
14132 ResetGfxAnimation(x, y);
14133 TEST_DrawLevelField(x, y);
14135 else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
14136 element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
14140 game.ball_state = !game.ball_state;
14142 SCAN_PLAYFIELD(xx, yy)
14144 int e = Feld[xx][yy];
14146 if (game.ball_state)
14148 if (e == EL_EMC_MAGIC_BALL)
14149 CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
14150 else if (e == EL_EMC_MAGIC_BALL_SWITCH)
14151 CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
14155 if (e == EL_EMC_MAGIC_BALL_ACTIVE)
14156 CreateField(xx, yy, EL_EMC_MAGIC_BALL);
14157 else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
14158 CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
14163 CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
14164 player->index_bit, dig_side);
14166 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
14167 player->index_bit, dig_side);
14169 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14170 player->index_bit, dig_side);
14176 if (!PLAYER_SWITCHING(player, x, y))
14178 player->is_switching = TRUE;
14179 player->switch_x = x;
14180 player->switch_y = y;
14182 CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
14183 player->index_bit, dig_side);
14184 CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
14185 player->index_bit, dig_side);
14187 CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
14188 player->index_bit, dig_side);
14189 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
14190 player->index_bit, dig_side);
14193 CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
14194 player->index_bit, dig_side);
14195 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14196 player->index_bit, dig_side);
14198 return MP_NO_ACTION;
14201 player->push_delay = -1;
14203 if (is_player) /* function can also be called by EL_PENGUIN */
14205 if (Feld[x][y] != element) /* really digged/collected something */
14207 player->is_collecting = !player->is_digging;
14208 player->is_active = TRUE;
14215 static boolean DigFieldByCE(int x, int y, int digging_element)
14217 int element = Feld[x][y];
14219 if (!IS_FREE(x, y))
14221 int action = (IS_DIGGABLE(element) ? ACTION_DIGGING :
14222 IS_COLLECTIBLE(element) ? ACTION_COLLECTING :
14225 /* no element can dig solid indestructible elements */
14226 if (IS_INDESTRUCTIBLE(element) &&
14227 !IS_DIGGABLE(element) &&
14228 !IS_COLLECTIBLE(element))
14231 if (AmoebaNr[x][y] &&
14232 (element == EL_AMOEBA_FULL ||
14233 element == EL_BD_AMOEBA ||
14234 element == EL_AMOEBA_GROWING))
14236 AmoebaCnt[AmoebaNr[x][y]]--;
14237 AmoebaCnt2[AmoebaNr[x][y]]--;
14240 if (IS_MOVING(x, y))
14241 RemoveMovingField(x, y);
14245 TEST_DrawLevelField(x, y);
14248 /* if digged element was about to explode, prevent the explosion */
14249 ExplodeField[x][y] = EX_TYPE_NONE;
14251 PlayLevelSoundAction(x, y, action);
14254 Store[x][y] = EL_EMPTY;
14256 /* this makes it possible to leave the removed element again */
14257 if (IS_EQUAL_OR_IN_GROUP(element, MOVE_ENTER_EL(digging_element)))
14258 Store[x][y] = element;
14263 static boolean SnapField(struct PlayerInfo *player, int dx, int dy)
14265 int jx = player->jx, jy = player->jy;
14266 int x = jx + dx, y = jy + dy;
14267 int snap_direction = (dx == -1 ? MV_LEFT :
14268 dx == +1 ? MV_RIGHT :
14270 dy == +1 ? MV_DOWN : MV_NONE);
14271 boolean can_continue_snapping = (level.continuous_snapping &&
14272 WasJustFalling[x][y] < CHECK_DELAY_FALLING);
14274 if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
14277 if (!player->active || !IN_LEV_FIELD(x, y))
14285 if (player->MovPos == 0)
14286 player->is_pushing = FALSE;
14288 player->is_snapping = FALSE;
14290 if (player->MovPos == 0)
14292 player->is_moving = FALSE;
14293 player->is_digging = FALSE;
14294 player->is_collecting = FALSE;
14300 /* prevent snapping with already pressed snap key when not allowed */
14301 if (player->is_snapping && !can_continue_snapping)
14304 player->MovDir = snap_direction;
14306 if (player->MovPos == 0)
14308 player->is_moving = FALSE;
14309 player->is_digging = FALSE;
14310 player->is_collecting = FALSE;
14313 player->is_dropping = FALSE;
14314 player->is_dropping_pressed = FALSE;
14315 player->drop_pressed_delay = 0;
14317 if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
14320 player->is_snapping = TRUE;
14321 player->is_active = TRUE;
14323 if (player->MovPos == 0)
14325 player->is_moving = FALSE;
14326 player->is_digging = FALSE;
14327 player->is_collecting = FALSE;
14330 if (player->MovPos != 0) /* prevent graphic bugs in versions < 2.2.0 */
14331 TEST_DrawLevelField(player->last_jx, player->last_jy);
14333 TEST_DrawLevelField(x, y);
14338 static boolean DropElement(struct PlayerInfo *player)
14340 int old_element, new_element;
14341 int dropx = player->jx, dropy = player->jy;
14342 int drop_direction = player->MovDir;
14343 int drop_side = drop_direction;
14344 int drop_element = get_next_dropped_element(player);
14346 /* do not drop an element on top of another element; when holding drop key
14347 pressed without moving, dropped element must move away before the next
14348 element can be dropped (this is especially important if the next element
14349 is dynamite, which can be placed on background for historical reasons) */
14350 if (PLAYER_DROPPING(player, dropx, dropy) && Feld[dropx][dropy] != EL_EMPTY)
14353 if (IS_THROWABLE(drop_element))
14355 dropx += GET_DX_FROM_DIR(drop_direction);
14356 dropy += GET_DY_FROM_DIR(drop_direction);
14358 if (!IN_LEV_FIELD(dropx, dropy))
14362 old_element = Feld[dropx][dropy]; /* old element at dropping position */
14363 new_element = drop_element; /* default: no change when dropping */
14365 /* check if player is active, not moving and ready to drop */
14366 if (!player->active || player->MovPos || player->drop_delay > 0)
14369 /* check if player has anything that can be dropped */
14370 if (new_element == EL_UNDEFINED)
14373 /* only set if player has anything that can be dropped */
14374 player->is_dropping_pressed = TRUE;
14376 /* check if drop key was pressed long enough for EM style dynamite */
14377 if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
14380 /* check if anything can be dropped at the current position */
14381 if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
14384 /* collected custom elements can only be dropped on empty fields */
14385 if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
14388 if (old_element != EL_EMPTY)
14389 Back[dropx][dropy] = old_element; /* store old element on this field */
14391 ResetGfxAnimation(dropx, dropy);
14392 ResetRandomAnimationValue(dropx, dropy);
14394 if (player->inventory_size > 0 ||
14395 player->inventory_infinite_element != EL_UNDEFINED)
14397 if (player->inventory_size > 0)
14399 player->inventory_size--;
14401 DrawGameDoorValues();
14403 if (new_element == EL_DYNAMITE)
14404 new_element = EL_DYNAMITE_ACTIVE;
14405 else if (new_element == EL_EM_DYNAMITE)
14406 new_element = EL_EM_DYNAMITE_ACTIVE;
14407 else if (new_element == EL_SP_DISK_RED)
14408 new_element = EL_SP_DISK_RED_ACTIVE;
14411 Feld[dropx][dropy] = new_element;
14413 if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
14414 DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
14415 el2img(Feld[dropx][dropy]), 0);
14417 PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
14419 /* needed if previous element just changed to "empty" in the last frame */
14420 ChangeCount[dropx][dropy] = 0; /* allow at least one more change */
14422 CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
14423 player->index_bit, drop_side);
14424 CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
14426 player->index_bit, drop_side);
14428 TestIfElementTouchesCustomElement(dropx, dropy);
14430 else /* player is dropping a dyna bomb */
14432 player->dynabombs_left--;
14434 Feld[dropx][dropy] = new_element;
14436 if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
14437 DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
14438 el2img(Feld[dropx][dropy]), 0);
14440 PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
14443 if (Feld[dropx][dropy] == new_element) /* uninitialized unless CE change */
14444 InitField_WithBug1(dropx, dropy, FALSE);
14446 new_element = Feld[dropx][dropy]; /* element might have changed */
14448 if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
14449 element_info[new_element].move_pattern == MV_WHEN_DROPPED)
14451 if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
14452 MovDir[dropx][dropy] = drop_direction;
14454 ChangeCount[dropx][dropy] = 0; /* allow at least one more change */
14456 /* do not cause impact style collision by dropping elements that can fall */
14457 CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
14460 player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
14461 player->is_dropping = TRUE;
14463 player->drop_pressed_delay = 0;
14464 player->is_dropping_pressed = FALSE;
14466 player->drop_x = dropx;
14467 player->drop_y = dropy;
14472 /* ------------------------------------------------------------------------- */
14473 /* game sound playing functions */
14474 /* ------------------------------------------------------------------------- */
14476 static int *loop_sound_frame = NULL;
14477 static int *loop_sound_volume = NULL;
14479 void InitPlayLevelSound()
14481 int num_sounds = getSoundListSize();
14483 checked_free(loop_sound_frame);
14484 checked_free(loop_sound_volume);
14486 loop_sound_frame = checked_calloc(num_sounds * sizeof(int));
14487 loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
14490 static void PlayLevelSound(int x, int y, int nr)
14492 int sx = SCREENX(x), sy = SCREENY(y);
14493 int volume, stereo_position;
14494 int max_distance = 8;
14495 int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
14497 if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
14498 (!setup.sound_loops && IS_LOOP_SOUND(nr)))
14501 if (!IN_LEV_FIELD(x, y) ||
14502 sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
14503 sy < -max_distance || sy >= SCR_FIELDY + max_distance)
14506 volume = SOUND_MAX_VOLUME;
14508 if (!IN_SCR_FIELD(sx, sy))
14510 int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
14511 int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
14513 volume -= volume * (dx > dy ? dx : dy) / max_distance;
14516 stereo_position = (SOUND_MAX_LEFT +
14517 (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
14518 (SCR_FIELDX + 2 * max_distance));
14520 if (IS_LOOP_SOUND(nr))
14522 /* This assures that quieter loop sounds do not overwrite louder ones,
14523 while restarting sound volume comparison with each new game frame. */
14525 if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
14528 loop_sound_volume[nr] = volume;
14529 loop_sound_frame[nr] = FrameCounter;
14532 PlaySoundExt(nr, volume, stereo_position, type);
14535 static void PlayLevelSoundNearest(int x, int y, int sound_action)
14537 PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
14538 x > LEVELX(BX2) ? LEVELX(BX2) : x,
14539 y < LEVELY(BY1) ? LEVELY(BY1) :
14540 y > LEVELY(BY2) ? LEVELY(BY2) : y,
14544 static void PlayLevelSoundAction(int x, int y, int action)
14546 PlayLevelSoundElementAction(x, y, Feld[x][y], action);
14549 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
14551 int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
14553 if (sound_effect != SND_UNDEFINED)
14554 PlayLevelSound(x, y, sound_effect);
14557 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
14560 int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
14562 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14563 PlayLevelSound(x, y, sound_effect);
14566 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
14568 int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
14570 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14571 PlayLevelSound(x, y, sound_effect);
14574 static void StopLevelSoundActionIfLoop(int x, int y, int action)
14576 int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
14578 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14579 StopSound(sound_effect);
14582 static int getLevelMusicNr()
14584 if (levelset.music[level_nr] != MUS_UNDEFINED)
14585 return levelset.music[level_nr]; /* from config file */
14587 return MAP_NOCONF_MUSIC(level_nr); /* from music dir */
14590 static void FadeLevelSounds()
14595 static void FadeLevelMusic()
14597 int music_nr = getLevelMusicNr();
14598 char *curr_music = getCurrentlyPlayingMusicFilename();
14599 char *next_music = getMusicInfoEntryFilename(music_nr);
14601 if (!strEqual(curr_music, next_music))
14605 void FadeLevelSoundsAndMusic()
14611 static void PlayLevelMusic()
14613 int music_nr = getLevelMusicNr();
14614 char *curr_music = getCurrentlyPlayingMusicFilename();
14615 char *next_music = getMusicInfoEntryFilename(music_nr);
14617 if (!strEqual(curr_music, next_music))
14618 PlayMusic(music_nr);
14621 void PlayLevelSound_EM(int xx, int yy, int element_em, int sample)
14623 int element = (element_em > -1 ? map_element_EM_to_RND(element_em) : 0);
14624 int offset = (BorderElement == EL_STEELWALL ? 1 : 0);
14625 int x = xx - 1 - offset;
14626 int y = yy - 1 - offset;
14631 PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
14635 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14639 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14643 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14647 PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
14651 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14655 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14658 case SAMPLE_android_clone:
14659 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14662 case SAMPLE_android_move:
14663 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14666 case SAMPLE_spring:
14667 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14671 PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
14675 PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
14678 case SAMPLE_eater_eat:
14679 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14683 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14686 case SAMPLE_collect:
14687 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
14690 case SAMPLE_diamond:
14691 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14694 case SAMPLE_squash:
14695 /* !!! CHECK THIS !!! */
14697 PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
14699 PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
14703 case SAMPLE_wonderfall:
14704 PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
14708 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14712 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14716 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14720 PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
14724 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14728 PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
14731 case SAMPLE_wonder:
14732 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14736 PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
14739 case SAMPLE_exit_open:
14740 PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
14743 case SAMPLE_exit_leave:
14744 PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
14747 case SAMPLE_dynamite:
14748 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14752 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14756 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
14760 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14764 PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
14768 PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
14772 PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
14776 PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
14781 void PlayLevelSound_SP(int xx, int yy, int element_sp, int action_sp)
14783 int element = map_element_SP_to_RND(element_sp);
14784 int action = map_action_SP_to_RND(action_sp);
14785 int offset = (setup.sp_show_border_elements ? 0 : 1);
14786 int x = xx - offset;
14787 int y = yy - offset;
14789 PlayLevelSoundElementAction(x, y, element, action);
14792 void PlayLevelSound_MM(int xx, int yy, int element_mm, int action_mm)
14794 int element = map_element_MM_to_RND(element_mm);
14795 int action = map_action_MM_to_RND(action_mm);
14797 int x = xx - offset;
14798 int y = yy - offset;
14800 if (!IS_MM_ELEMENT(element))
14801 element = EL_MM_DEFAULT;
14803 PlayLevelSoundElementAction(x, y, element, action);
14806 void PlaySound_MM(int sound_mm)
14808 int sound = map_sound_MM_to_RND(sound_mm);
14810 if (sound == SND_UNDEFINED)
14816 void PlaySoundLoop_MM(int sound_mm)
14818 int sound = map_sound_MM_to_RND(sound_mm);
14820 if (sound == SND_UNDEFINED)
14823 PlaySoundLoop(sound);
14826 void StopSound_MM(int sound_mm)
14828 int sound = map_sound_MM_to_RND(sound_mm);
14830 if (sound == SND_UNDEFINED)
14836 void RaiseScore(int value)
14838 local_player->score += value;
14840 game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
14842 DisplayGameControlValues();
14845 void RaiseScoreElement(int element)
14850 case EL_BD_DIAMOND:
14851 case EL_EMERALD_YELLOW:
14852 case EL_EMERALD_RED:
14853 case EL_EMERALD_PURPLE:
14854 case EL_SP_INFOTRON:
14855 RaiseScore(level.score[SC_EMERALD]);
14858 RaiseScore(level.score[SC_DIAMOND]);
14861 RaiseScore(level.score[SC_CRYSTAL]);
14864 RaiseScore(level.score[SC_PEARL]);
14867 case EL_BD_BUTTERFLY:
14868 case EL_SP_ELECTRON:
14869 RaiseScore(level.score[SC_BUG]);
14872 case EL_BD_FIREFLY:
14873 case EL_SP_SNIKSNAK:
14874 RaiseScore(level.score[SC_SPACESHIP]);
14877 case EL_DARK_YAMYAM:
14878 RaiseScore(level.score[SC_YAMYAM]);
14881 RaiseScore(level.score[SC_ROBOT]);
14884 RaiseScore(level.score[SC_PACMAN]);
14887 RaiseScore(level.score[SC_NUT]);
14890 case EL_EM_DYNAMITE:
14891 case EL_SP_DISK_RED:
14892 case EL_DYNABOMB_INCREASE_NUMBER:
14893 case EL_DYNABOMB_INCREASE_SIZE:
14894 case EL_DYNABOMB_INCREASE_POWER:
14895 RaiseScore(level.score[SC_DYNAMITE]);
14897 case EL_SHIELD_NORMAL:
14898 case EL_SHIELD_DEADLY:
14899 RaiseScore(level.score[SC_SHIELD]);
14901 case EL_EXTRA_TIME:
14902 RaiseScore(level.extra_time_score);
14916 case EL_DC_KEY_WHITE:
14917 RaiseScore(level.score[SC_KEY]);
14920 RaiseScore(element_info[element].collect_score);
14925 void RequestQuitGameExt(boolean skip_request, boolean quick_quit, char *message)
14927 if (skip_request || Request(message, REQ_ASK | REQ_STAY_CLOSED))
14929 /* closing door required in case of envelope style request dialogs */
14931 CloseDoor(DOOR_CLOSE_1);
14933 #if defined(NETWORK_AVALIABLE)
14934 if (options.network)
14935 SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
14940 FadeSkipNextFadeIn();
14942 SetGameStatus(GAME_MODE_MAIN);
14947 else /* continue playing the game */
14949 if (tape.playing && tape.deactivate_display)
14950 TapeDeactivateDisplayOff(TRUE);
14952 OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
14954 if (tape.playing && tape.deactivate_display)
14955 TapeDeactivateDisplayOn();
14959 void RequestQuitGame(boolean ask_if_really_quit)
14961 boolean quick_quit = (!ask_if_really_quit || level_editor_test_game);
14962 boolean skip_request = AllPlayersGone || quick_quit;
14964 RequestQuitGameExt(skip_request, quick_quit,
14965 "Do you really want to quit the game?");
14968 void RequestRestartGame(char *message)
14970 game.restart_game_message = NULL;
14972 if (Request(message, REQ_ASK | REQ_STAY_CLOSED))
14974 StartGameActions(options.network, setup.autorecord, level.random_seed);
14978 SetGameStatus(GAME_MODE_MAIN);
14985 /* ------------------------------------------------------------------------- */
14986 /* random generator functions */
14987 /* ------------------------------------------------------------------------- */
14989 unsigned int InitEngineRandom_RND(int seed)
14991 game.num_random_calls = 0;
14993 return InitEngineRandom(seed);
14996 unsigned int RND(int max)
15000 game.num_random_calls++;
15002 return GetEngineRandom(max);
15009 /* ------------------------------------------------------------------------- */
15010 /* game engine snapshot handling functions */
15011 /* ------------------------------------------------------------------------- */
15013 struct EngineSnapshotInfo
15015 /* runtime values for custom element collect score */
15016 int collect_score[NUM_CUSTOM_ELEMENTS];
15018 /* runtime values for group element choice position */
15019 int choice_pos[NUM_GROUP_ELEMENTS];
15021 /* runtime values for belt position animations */
15022 int belt_graphic[4][NUM_BELT_PARTS];
15023 int belt_anim_mode[4][NUM_BELT_PARTS];
15026 static struct EngineSnapshotInfo engine_snapshot_rnd;
15027 static char *snapshot_level_identifier = NULL;
15028 static int snapshot_level_nr = -1;
15030 static void SaveEngineSnapshotValues_RND()
15032 static int belt_base_active_element[4] =
15034 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
15035 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
15036 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
15037 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
15041 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
15043 int element = EL_CUSTOM_START + i;
15045 engine_snapshot_rnd.collect_score[i] = element_info[element].collect_score;
15048 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
15050 int element = EL_GROUP_START + i;
15052 engine_snapshot_rnd.choice_pos[i] = element_info[element].group->choice_pos;
15055 for (i = 0; i < 4; i++)
15057 for (j = 0; j < NUM_BELT_PARTS; j++)
15059 int element = belt_base_active_element[i] + j;
15060 int graphic = el2img(element);
15061 int anim_mode = graphic_info[graphic].anim_mode;
15063 engine_snapshot_rnd.belt_graphic[i][j] = graphic;
15064 engine_snapshot_rnd.belt_anim_mode[i][j] = anim_mode;
15069 static void LoadEngineSnapshotValues_RND()
15071 unsigned int num_random_calls = game.num_random_calls;
15074 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
15076 int element = EL_CUSTOM_START + i;
15078 element_info[element].collect_score = engine_snapshot_rnd.collect_score[i];
15081 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
15083 int element = EL_GROUP_START + i;
15085 element_info[element].group->choice_pos = engine_snapshot_rnd.choice_pos[i];
15088 for (i = 0; i < 4; i++)
15090 for (j = 0; j < NUM_BELT_PARTS; j++)
15092 int graphic = engine_snapshot_rnd.belt_graphic[i][j];
15093 int anim_mode = engine_snapshot_rnd.belt_anim_mode[i][j];
15095 graphic_info[graphic].anim_mode = anim_mode;
15099 if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15101 InitRND(tape.random_seed);
15102 for (i = 0; i < num_random_calls; i++)
15106 if (game.num_random_calls != num_random_calls)
15108 Error(ERR_INFO, "number of random calls out of sync");
15109 Error(ERR_INFO, "number of random calls should be %d", num_random_calls);
15110 Error(ERR_INFO, "number of random calls is %d", game.num_random_calls);
15111 Error(ERR_EXIT, "this should not happen -- please debug");
15115 void FreeEngineSnapshotSingle()
15117 FreeSnapshotSingle();
15119 setString(&snapshot_level_identifier, NULL);
15120 snapshot_level_nr = -1;
15123 void FreeEngineSnapshotList()
15125 FreeSnapshotList();
15128 ListNode *SaveEngineSnapshotBuffers()
15130 ListNode *buffers = NULL;
15132 /* copy some special values to a structure better suited for the snapshot */
15134 if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15135 SaveEngineSnapshotValues_RND();
15136 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15137 SaveEngineSnapshotValues_EM();
15138 if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15139 SaveEngineSnapshotValues_SP(&buffers);
15140 if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15141 SaveEngineSnapshotValues_MM(&buffers);
15143 /* save values stored in special snapshot structure */
15145 if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15146 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_rnd));
15147 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15148 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em));
15149 if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15150 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_sp));
15151 if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15152 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_mm));
15154 /* save further RND engine values */
15156 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(stored_player));
15157 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(game));
15158 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(tape));
15160 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ZX));
15161 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ZY));
15162 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExitX));
15163 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExitY));
15165 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(FrameCounter));
15166 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeFrames));
15167 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimePlayed));
15168 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeLeft));
15169 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TapeTime));
15171 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovDir));
15172 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovPos));
15173 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenGfxPos));
15175 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScrollStepSize));
15177 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AllPlayersGone));
15179 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt));
15180 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt2));
15182 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Feld));
15183 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovPos));
15184 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDir));
15185 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDelay));
15186 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeDelay));
15187 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangePage));
15188 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CustomValue));
15189 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store));
15190 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store2));
15191 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(StorePlayer));
15192 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Back));
15193 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaNr));
15194 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustMoving));
15195 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustFalling));
15196 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckCollision));
15197 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckImpact));
15198 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Stop));
15199 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Pushed));
15201 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeCount));
15202 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeEvent));
15204 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodePhase));
15205 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeDelay));
15206 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeField));
15208 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(RunnerVisit));
15209 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(PlayerVisit));
15211 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxFrame));
15212 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxRandom));
15213 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxElement));
15214 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxAction));
15215 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxDir));
15217 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_x));
15218 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_y));
15221 ListNode *node = engine_snapshot_list_rnd;
15224 while (node != NULL)
15226 num_bytes += ((struct EngineSnapshotNodeInfo *)node->content)->size;
15231 printf("::: size of engine snapshot: %d bytes\n", num_bytes);
15237 void SaveEngineSnapshotSingle()
15239 ListNode *buffers = SaveEngineSnapshotBuffers();
15241 /* finally save all snapshot buffers to single snapshot */
15242 SaveSnapshotSingle(buffers);
15244 /* save level identification information */
15245 setString(&snapshot_level_identifier, leveldir_current->identifier);
15246 snapshot_level_nr = level_nr;
15249 boolean CheckSaveEngineSnapshotToList()
15251 boolean save_snapshot =
15252 ((game.snapshot.mode == SNAPSHOT_MODE_EVERY_STEP) ||
15253 (game.snapshot.mode == SNAPSHOT_MODE_EVERY_MOVE &&
15254 game.snapshot.changed_action) ||
15255 (game.snapshot.mode == SNAPSHOT_MODE_EVERY_COLLECT &&
15256 game.snapshot.collected_item));
15258 game.snapshot.changed_action = FALSE;
15259 game.snapshot.collected_item = FALSE;
15260 game.snapshot.save_snapshot = save_snapshot;
15262 return save_snapshot;
15265 void SaveEngineSnapshotToList()
15267 if (game.snapshot.mode == SNAPSHOT_MODE_OFF ||
15271 ListNode *buffers = SaveEngineSnapshotBuffers();
15273 /* finally save all snapshot buffers to snapshot list */
15274 SaveSnapshotToList(buffers);
15277 void SaveEngineSnapshotToListInitial()
15279 FreeEngineSnapshotList();
15281 SaveEngineSnapshotToList();
15284 void LoadEngineSnapshotValues()
15286 /* restore special values from snapshot structure */
15288 if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15289 LoadEngineSnapshotValues_RND();
15290 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15291 LoadEngineSnapshotValues_EM();
15292 if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15293 LoadEngineSnapshotValues_SP();
15294 if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15295 LoadEngineSnapshotValues_MM();
15298 void LoadEngineSnapshotSingle()
15300 LoadSnapshotSingle();
15302 LoadEngineSnapshotValues();
15305 void LoadEngineSnapshot_Undo(int steps)
15307 LoadSnapshotFromList_Older(steps);
15309 LoadEngineSnapshotValues();
15312 void LoadEngineSnapshot_Redo(int steps)
15314 LoadSnapshotFromList_Newer(steps);
15316 LoadEngineSnapshotValues();
15319 boolean CheckEngineSnapshotSingle()
15321 return (strEqual(snapshot_level_identifier, leveldir_current->identifier) &&
15322 snapshot_level_nr == level_nr);
15325 boolean CheckEngineSnapshotList()
15327 return CheckSnapshotList();
15331 /* ---------- new game button stuff ---------------------------------------- */
15338 boolean *setup_value;
15339 boolean allowed_on_tape;
15341 } gamebutton_info[NUM_GAME_BUTTONS] =
15344 IMG_GFX_GAME_BUTTON_STOP, &game.button.stop,
15345 GAME_CTRL_ID_STOP, NULL,
15349 IMG_GFX_GAME_BUTTON_PAUSE, &game.button.pause,
15350 GAME_CTRL_ID_PAUSE, NULL,
15354 IMG_GFX_GAME_BUTTON_PLAY, &game.button.play,
15355 GAME_CTRL_ID_PLAY, NULL,
15359 IMG_GFX_GAME_BUTTON_UNDO, &game.button.undo,
15360 GAME_CTRL_ID_UNDO, NULL,
15364 IMG_GFX_GAME_BUTTON_REDO, &game.button.redo,
15365 GAME_CTRL_ID_REDO, NULL,
15369 IMG_GFX_GAME_BUTTON_SAVE, &game.button.save,
15370 GAME_CTRL_ID_SAVE, NULL,
15374 IMG_GFX_GAME_BUTTON_PAUSE2, &game.button.pause2,
15375 GAME_CTRL_ID_PAUSE2, NULL,
15379 IMG_GFX_GAME_BUTTON_LOAD, &game.button.load,
15380 GAME_CTRL_ID_LOAD, NULL,
15384 IMG_GFX_GAME_BUTTON_PANEL_STOP, &game.button.panel_stop,
15385 GAME_CTRL_ID_PANEL_STOP, NULL,
15389 IMG_GFX_GAME_BUTTON_PANEL_PAUSE, &game.button.panel_pause,
15390 GAME_CTRL_ID_PANEL_PAUSE, NULL,
15391 FALSE, "pause game"
15394 IMG_GFX_GAME_BUTTON_PANEL_PLAY, &game.button.panel_play,
15395 GAME_CTRL_ID_PANEL_PLAY, NULL,
15399 IMG_GFX_GAME_BUTTON_SOUND_MUSIC, &game.button.sound_music,
15400 SOUND_CTRL_ID_MUSIC, &setup.sound_music,
15401 TRUE, "background music on/off"
15404 IMG_GFX_GAME_BUTTON_SOUND_LOOPS, &game.button.sound_loops,
15405 SOUND_CTRL_ID_LOOPS, &setup.sound_loops,
15406 TRUE, "sound loops on/off"
15409 IMG_GFX_GAME_BUTTON_SOUND_SIMPLE, &game.button.sound_simple,
15410 SOUND_CTRL_ID_SIMPLE, &setup.sound_simple,
15411 TRUE, "normal sounds on/off"
15414 IMG_GFX_GAME_BUTTON_PANEL_SOUND_MUSIC, &game.button.panel_sound_music,
15415 SOUND_CTRL_ID_PANEL_MUSIC, &setup.sound_music,
15416 FALSE, "background music on/off"
15419 IMG_GFX_GAME_BUTTON_PANEL_SOUND_LOOPS, &game.button.panel_sound_loops,
15420 SOUND_CTRL_ID_PANEL_LOOPS, &setup.sound_loops,
15421 FALSE, "sound loops on/off"
15424 IMG_GFX_GAME_BUTTON_PANEL_SOUND_SIMPLE, &game.button.panel_sound_simple,
15425 SOUND_CTRL_ID_PANEL_SIMPLE, &setup.sound_simple,
15426 FALSE, "normal sounds on/off"
15430 void CreateGameButtons()
15434 for (i = 0; i < NUM_GAME_BUTTONS; i++)
15436 struct GraphicInfo *gfx = &graphic_info[gamebutton_info[i].graphic];
15437 struct XY *pos = gamebutton_info[i].pos;
15438 struct GadgetInfo *gi;
15441 unsigned int event_mask;
15442 boolean allowed_on_tape = gamebutton_info[i].allowed_on_tape;
15443 boolean on_tape = (tape.show_game_buttons && allowed_on_tape);
15444 int base_x = (on_tape ? VX : DX);
15445 int base_y = (on_tape ? VY : DY);
15446 int gd_x = gfx->src_x;
15447 int gd_y = gfx->src_y;
15448 int gd_xp = gfx->src_x + gfx->pressed_xoffset;
15449 int gd_yp = gfx->src_y + gfx->pressed_yoffset;
15450 int gd_xa = gfx->src_x + gfx->active_xoffset;
15451 int gd_ya = gfx->src_y + gfx->active_yoffset;
15452 int gd_xap = gfx->src_x + gfx->active_xoffset + gfx->pressed_xoffset;
15453 int gd_yap = gfx->src_y + gfx->active_yoffset + gfx->pressed_yoffset;
15456 if (gfx->bitmap == NULL)
15458 game_gadget[id] = NULL;
15463 if (id == GAME_CTRL_ID_STOP ||
15464 id == GAME_CTRL_ID_PANEL_STOP ||
15465 id == GAME_CTRL_ID_PLAY ||
15466 id == GAME_CTRL_ID_PANEL_PLAY ||
15467 id == GAME_CTRL_ID_SAVE ||
15468 id == GAME_CTRL_ID_LOAD)
15470 button_type = GD_TYPE_NORMAL_BUTTON;
15472 event_mask = GD_EVENT_RELEASED;
15474 else if (id == GAME_CTRL_ID_UNDO ||
15475 id == GAME_CTRL_ID_REDO)
15477 button_type = GD_TYPE_NORMAL_BUTTON;
15479 event_mask = GD_EVENT_PRESSED | GD_EVENT_REPEATED;
15483 button_type = GD_TYPE_CHECK_BUTTON;
15484 checked = (gamebutton_info[i].setup_value != NULL ?
15485 *gamebutton_info[i].setup_value : FALSE);
15486 event_mask = GD_EVENT_PRESSED;
15489 gi = CreateGadget(GDI_CUSTOM_ID, id,
15490 GDI_INFO_TEXT, gamebutton_info[i].infotext,
15491 GDI_X, base_x + GDI_ACTIVE_POS(pos->x),
15492 GDI_Y, base_y + GDI_ACTIVE_POS(pos->y),
15493 GDI_WIDTH, gfx->width,
15494 GDI_HEIGHT, gfx->height,
15495 GDI_TYPE, button_type,
15496 GDI_STATE, GD_BUTTON_UNPRESSED,
15497 GDI_CHECKED, checked,
15498 GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
15499 GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
15500 GDI_ALT_DESIGN_UNPRESSED, gfx->bitmap, gd_xa, gd_ya,
15501 GDI_ALT_DESIGN_PRESSED, gfx->bitmap, gd_xap, gd_yap,
15502 GDI_DIRECT_DRAW, FALSE,
15503 GDI_EVENT_MASK, event_mask,
15504 GDI_CALLBACK_ACTION, HandleGameButtons,
15508 Error(ERR_EXIT, "cannot create gadget");
15510 game_gadget[id] = gi;
15514 void FreeGameButtons()
15518 for (i = 0; i < NUM_GAME_BUTTONS; i++)
15519 FreeGadget(game_gadget[i]);
15522 static void UnmapGameButtonsAtSamePosition(int id)
15526 for (i = 0; i < NUM_GAME_BUTTONS; i++)
15528 gamebutton_info[i].pos->x == gamebutton_info[id].pos->x &&
15529 gamebutton_info[i].pos->y == gamebutton_info[id].pos->y)
15530 UnmapGadget(game_gadget[i]);
15533 static void UnmapGameButtonsAtSamePosition_All()
15535 if (setup.show_snapshot_buttons)
15537 UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_SAVE);
15538 UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE2);
15539 UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_LOAD);
15543 UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_STOP);
15544 UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE);
15545 UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PLAY);
15547 UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_STOP);
15548 UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_PAUSE);
15549 UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_PLAY);
15553 static void MapGameButtonsAtSamePosition(int id)
15557 for (i = 0; i < NUM_GAME_BUTTONS; i++)
15559 gamebutton_info[i].pos->x == gamebutton_info[id].pos->x &&
15560 gamebutton_info[i].pos->y == gamebutton_info[id].pos->y)
15561 MapGadget(game_gadget[i]);
15563 UnmapGameButtonsAtSamePosition_All();
15566 void MapUndoRedoButtons()
15568 UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
15569 UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
15571 MapGadget(game_gadget[GAME_CTRL_ID_UNDO]);
15572 MapGadget(game_gadget[GAME_CTRL_ID_REDO]);
15574 ModifyGadget(game_gadget[GAME_CTRL_ID_PAUSE2], GDI_CHECKED, TRUE, GDI_END);
15577 void UnmapUndoRedoButtons()
15579 UnmapGadget(game_gadget[GAME_CTRL_ID_UNDO]);
15580 UnmapGadget(game_gadget[GAME_CTRL_ID_REDO]);
15582 MapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
15583 MapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
15585 ModifyGadget(game_gadget[GAME_CTRL_ID_PAUSE2], GDI_CHECKED, FALSE, GDI_END);
15588 void MapGameButtonsExt(boolean on_tape)
15592 for (i = 0; i < NUM_GAME_BUTTONS; i++)
15593 if ((!on_tape || gamebutton_info[i].allowed_on_tape) &&
15594 i != GAME_CTRL_ID_UNDO &&
15595 i != GAME_CTRL_ID_REDO)
15596 MapGadget(game_gadget[i]);
15598 UnmapGameButtonsAtSamePosition_All();
15600 RedrawGameButtons();
15603 void UnmapGameButtonsExt(boolean on_tape)
15607 for (i = 0; i < NUM_GAME_BUTTONS; i++)
15608 if (!on_tape || gamebutton_info[i].allowed_on_tape)
15609 UnmapGadget(game_gadget[i]);
15612 void RedrawGameButtonsExt(boolean on_tape)
15616 for (i = 0; i < NUM_GAME_BUTTONS; i++)
15617 if (!on_tape || gamebutton_info[i].allowed_on_tape)
15618 RedrawGadget(game_gadget[i]);
15620 // RedrawGadget() may have set REDRAW_ALL if buttons are defined off-area
15621 redraw_mask &= ~REDRAW_ALL;
15624 void SetGadgetState(struct GadgetInfo *gi, boolean state)
15629 gi->checked = state;
15632 void RedrawSoundButtonGadget(int id)
15634 int id2 = (id == SOUND_CTRL_ID_MUSIC ? SOUND_CTRL_ID_PANEL_MUSIC :
15635 id == SOUND_CTRL_ID_LOOPS ? SOUND_CTRL_ID_PANEL_LOOPS :
15636 id == SOUND_CTRL_ID_SIMPLE ? SOUND_CTRL_ID_PANEL_SIMPLE :
15637 id == SOUND_CTRL_ID_PANEL_MUSIC ? SOUND_CTRL_ID_MUSIC :
15638 id == SOUND_CTRL_ID_PANEL_LOOPS ? SOUND_CTRL_ID_LOOPS :
15639 id == SOUND_CTRL_ID_PANEL_SIMPLE ? SOUND_CTRL_ID_SIMPLE :
15642 SetGadgetState(game_gadget[id2], *gamebutton_info[id2].setup_value);
15643 RedrawGadget(game_gadget[id2]);
15646 void MapGameButtons()
15648 MapGameButtonsExt(FALSE);
15651 void UnmapGameButtons()
15653 UnmapGameButtonsExt(FALSE);
15656 void RedrawGameButtons()
15658 RedrawGameButtonsExt(FALSE);
15661 void MapGameButtonsOnTape()
15663 MapGameButtonsExt(TRUE);
15666 void UnmapGameButtonsOnTape()
15668 UnmapGameButtonsExt(TRUE);
15671 void RedrawGameButtonsOnTape()
15673 RedrawGameButtonsExt(TRUE);
15676 void GameUndoRedoExt()
15678 ClearPlayerAction();
15680 tape.pausing = TRUE;
15683 UpdateAndDisplayGameControlValues();
15685 DrawCompleteVideoDisplay();
15686 DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
15687 DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
15688 DrawVideoDisplay(VIDEO_STATE_1STEP(tape.single_step), 0);
15693 void GameUndo(int steps)
15695 if (!CheckEngineSnapshotList())
15698 LoadEngineSnapshot_Undo(steps);
15703 void GameRedo(int steps)
15705 if (!CheckEngineSnapshotList())
15708 LoadEngineSnapshot_Redo(steps);
15713 static void HandleGameButtonsExt(int id, int button)
15715 static boolean game_undo_executed = FALSE;
15716 int steps = BUTTON_STEPSIZE(button);
15717 boolean handle_game_buttons =
15718 (game_status == GAME_MODE_PLAYING ||
15719 (game_status == GAME_MODE_MAIN && tape.show_game_buttons));
15721 if (!handle_game_buttons)
15726 case GAME_CTRL_ID_STOP:
15727 case GAME_CTRL_ID_PANEL_STOP:
15728 if (game_status == GAME_MODE_MAIN)
15734 RequestQuitGame(TRUE);
15738 case GAME_CTRL_ID_PAUSE:
15739 case GAME_CTRL_ID_PAUSE2:
15740 case GAME_CTRL_ID_PANEL_PAUSE:
15741 if (options.network && game_status == GAME_MODE_PLAYING)
15743 #if defined(NETWORK_AVALIABLE)
15745 SendToServer_ContinuePlaying();
15747 SendToServer_PausePlaying();
15751 TapeTogglePause(TAPE_TOGGLE_MANUAL);
15753 game_undo_executed = FALSE;
15757 case GAME_CTRL_ID_PLAY:
15758 case GAME_CTRL_ID_PANEL_PLAY:
15759 if (game_status == GAME_MODE_MAIN)
15761 StartGameActions(options.network, setup.autorecord, level.random_seed);
15763 else if (tape.pausing)
15765 #if defined(NETWORK_AVALIABLE)
15766 if (options.network)
15767 SendToServer_ContinuePlaying();
15770 TapeTogglePause(TAPE_TOGGLE_MANUAL | TAPE_TOGGLE_PLAY_PAUSE);
15774 case GAME_CTRL_ID_UNDO:
15775 // Important: When using "save snapshot when collecting an item" mode,
15776 // load last (current) snapshot for first "undo" after pressing "pause"
15777 // (else the last-but-one snapshot would be loaded, because the snapshot
15778 // pointer already points to the last snapshot when pressing "pause",
15779 // which is fine for "every step/move" mode, but not for "every collect")
15780 if (game.snapshot.mode == SNAPSHOT_MODE_EVERY_COLLECT &&
15781 !game_undo_executed)
15784 game_undo_executed = TRUE;
15789 case GAME_CTRL_ID_REDO:
15793 case GAME_CTRL_ID_SAVE:
15797 case GAME_CTRL_ID_LOAD:
15801 case SOUND_CTRL_ID_MUSIC:
15802 case SOUND_CTRL_ID_PANEL_MUSIC:
15803 if (setup.sound_music)
15805 setup.sound_music = FALSE;
15809 else if (audio.music_available)
15811 setup.sound = setup.sound_music = TRUE;
15813 SetAudioMode(setup.sound);
15815 if (game_status == GAME_MODE_PLAYING)
15819 RedrawSoundButtonGadget(id);
15823 case SOUND_CTRL_ID_LOOPS:
15824 case SOUND_CTRL_ID_PANEL_LOOPS:
15825 if (setup.sound_loops)
15826 setup.sound_loops = FALSE;
15827 else if (audio.loops_available)
15829 setup.sound = setup.sound_loops = TRUE;
15831 SetAudioMode(setup.sound);
15834 RedrawSoundButtonGadget(id);
15838 case SOUND_CTRL_ID_SIMPLE:
15839 case SOUND_CTRL_ID_PANEL_SIMPLE:
15840 if (setup.sound_simple)
15841 setup.sound_simple = FALSE;
15842 else if (audio.sound_available)
15844 setup.sound = setup.sound_simple = TRUE;
15846 SetAudioMode(setup.sound);
15849 RedrawSoundButtonGadget(id);
15858 static void HandleGameButtons(struct GadgetInfo *gi)
15860 HandleGameButtonsExt(gi->custom_id, gi->event.button);
15863 void HandleSoundButtonKeys(Key key)
15865 if (key == setup.shortcut.sound_simple)
15866 ClickOnGadget(game_gadget[SOUND_CTRL_ID_SIMPLE], MB_LEFTBUTTON);
15867 else if (key == setup.shortcut.sound_loops)
15868 ClickOnGadget(game_gadget[SOUND_CTRL_ID_LOOPS], MB_LEFTBUTTON);
15869 else if (key == setup.shortcut.sound_music)
15870 ClickOnGadget(game_gadget[SOUND_CTRL_ID_MUSIC], MB_LEFTBUTTON);