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(int);
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 (!network.enabled || player->connected_network)
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 =============================================================================
3276 #if DEBUG_INIT_PLAYER
3277 static void DebugPrintPlayerStatus(char *message)
3284 printf("%s:\n", message);
3286 for (i = 0; i < MAX_PLAYERS; i++)
3288 struct PlayerInfo *player = &stored_player[i];
3290 printf("- player %d: present == %d, connected == %d [%d/%d], active == %d",
3294 player->connected_locally,
3295 player->connected_network,
3298 if (local_player == player)
3299 printf(" (local player)");
3308 int full_lev_fieldx = lev_fieldx + (BorderElement != EL_EMPTY ? 2 : 0);
3309 int full_lev_fieldy = lev_fieldy + (BorderElement != EL_EMPTY ? 2 : 0);
3310 int fade_mask = REDRAW_FIELD;
3312 boolean emulate_bd = TRUE; /* unless non-BOULDERDASH elements found */
3313 boolean emulate_sb = TRUE; /* unless non-SOKOBAN elements found */
3314 boolean emulate_sp = TRUE; /* unless non-SUPAPLEX elements found */
3315 int initial_move_dir = MV_DOWN;
3318 // required here to update video display before fading (FIX THIS)
3319 DrawMaskedBorder(REDRAW_DOOR_2);
3321 if (!game.restart_level)
3322 CloseDoor(DOOR_CLOSE_1);
3324 SetGameStatus(GAME_MODE_PLAYING);
3326 if (level_editor_test_game)
3327 FadeSkipNextFadeIn();
3329 FadeSetEnterScreen();
3331 if (CheckIfGlobalBorderOrPlayfieldViewportHasChanged())
3332 fade_mask = REDRAW_ALL;
3334 FadeLevelSoundsAndMusic();
3336 ExpireSoundLoops(TRUE);
3338 if (!level_editor_test_game)
3341 /* needed if different viewport properties defined for playing */
3342 ChangeViewportPropertiesIfNeeded();
3346 DrawCompleteVideoDisplay();
3348 OpenDoor(GetDoorState() | DOOR_NO_DELAY | DOOR_FORCE_REDRAW);
3351 InitGameControlValues();
3353 /* don't play tapes over network */
3354 network_playing = (network.enabled && !tape.playing);
3356 for (i = 0; i < MAX_PLAYERS; i++)
3358 struct PlayerInfo *player = &stored_player[i];
3360 player->index_nr = i;
3361 player->index_bit = (1 << i);
3362 player->element_nr = EL_PLAYER_1 + i;
3364 player->present = FALSE;
3365 player->active = FALSE;
3366 player->mapped = FALSE;
3368 player->killed = FALSE;
3369 player->reanimated = FALSE;
3372 player->effective_action = 0;
3373 player->programmed_action = 0;
3375 player->mouse_action.lx = 0;
3376 player->mouse_action.ly = 0;
3377 player->mouse_action.button = 0;
3378 player->mouse_action.button_hint = 0;
3380 player->effective_mouse_action.lx = 0;
3381 player->effective_mouse_action.ly = 0;
3382 player->effective_mouse_action.button = 0;
3383 player->effective_mouse_action.button_hint = 0;
3386 player->score_final = 0;
3388 player->health = MAX_HEALTH;
3389 player->health_final = MAX_HEALTH;
3391 player->gems_still_needed = level.gems_needed;
3392 player->sokobanfields_still_needed = 0;
3393 player->lights_still_needed = 0;
3394 player->friends_still_needed = 0;
3396 for (j = 0; j < MAX_NUM_KEYS; j++)
3397 player->key[j] = FALSE;
3399 player->num_white_keys = 0;
3401 player->dynabomb_count = 0;
3402 player->dynabomb_size = 1;
3403 player->dynabombs_left = 0;
3404 player->dynabomb_xl = FALSE;
3406 player->MovDir = initial_move_dir;
3409 player->GfxDir = initial_move_dir;
3410 player->GfxAction = ACTION_DEFAULT;
3412 player->StepFrame = 0;
3414 player->initial_element = player->element_nr;
3415 player->artwork_element =
3416 (level.use_artwork_element[i] ? level.artwork_element[i] :
3417 player->element_nr);
3418 player->use_murphy = FALSE;
3420 player->block_last_field = FALSE; /* initialized in InitPlayerField() */
3421 player->block_delay_adjustment = 0; /* initialized in InitPlayerField() */
3423 player->gravity = level.initial_player_gravity[i];
3425 player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
3427 player->actual_frame_counter = 0;
3429 player->step_counter = 0;
3431 player->last_move_dir = initial_move_dir;
3433 player->is_active = FALSE;
3435 player->is_waiting = FALSE;
3436 player->is_moving = FALSE;
3437 player->is_auto_moving = FALSE;
3438 player->is_digging = FALSE;
3439 player->is_snapping = FALSE;
3440 player->is_collecting = FALSE;
3441 player->is_pushing = FALSE;
3442 player->is_switching = FALSE;
3443 player->is_dropping = FALSE;
3444 player->is_dropping_pressed = FALSE;
3446 player->is_bored = FALSE;
3447 player->is_sleeping = FALSE;
3449 player->was_waiting = TRUE;
3450 player->was_moving = FALSE;
3451 player->was_snapping = FALSE;
3452 player->was_dropping = FALSE;
3454 player->force_dropping = FALSE;
3456 player->frame_counter_bored = -1;
3457 player->frame_counter_sleeping = -1;
3459 player->anim_delay_counter = 0;
3460 player->post_delay_counter = 0;
3462 player->dir_waiting = initial_move_dir;
3463 player->action_waiting = ACTION_DEFAULT;
3464 player->last_action_waiting = ACTION_DEFAULT;
3465 player->special_action_bored = ACTION_DEFAULT;
3466 player->special_action_sleeping = ACTION_DEFAULT;
3468 player->switch_x = -1;
3469 player->switch_y = -1;
3471 player->drop_x = -1;
3472 player->drop_y = -1;
3474 player->show_envelope = 0;
3476 SetPlayerMoveSpeed(player, level.initial_player_stepsize[i], TRUE);
3478 player->push_delay = -1; /* initialized when pushing starts */
3479 player->push_delay_value = game.initial_push_delay_value;
3481 player->drop_delay = 0;
3482 player->drop_pressed_delay = 0;
3484 player->last_jx = -1;
3485 player->last_jy = -1;
3489 player->shield_normal_time_left = 0;
3490 player->shield_deadly_time_left = 0;
3492 player->inventory_infinite_element = EL_UNDEFINED;
3493 player->inventory_size = 0;
3495 if (level.use_initial_inventory[i])
3497 for (j = 0; j < level.initial_inventory_size[i]; j++)
3499 int element = level.initial_inventory_content[i][j];
3500 int collect_count = element_info[element].collect_count_initial;
3503 if (!IS_CUSTOM_ELEMENT(element))
3506 if (collect_count == 0)
3507 player->inventory_infinite_element = element;
3509 for (k = 0; k < collect_count; k++)
3510 if (player->inventory_size < MAX_INVENTORY_SIZE)
3511 player->inventory_element[player->inventory_size++] = element;
3515 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
3516 SnapField(player, 0, 0);
3518 player->LevelSolved = FALSE;
3519 player->GameOver = FALSE;
3521 player->LevelSolved_GameWon = FALSE;
3522 player->LevelSolved_GameEnd = FALSE;
3523 player->LevelSolved_PanelOff = FALSE;
3524 player->LevelSolved_SaveTape = FALSE;
3525 player->LevelSolved_SaveScore = FALSE;
3527 player->LevelSolved_CountingTime = 0;
3528 player->LevelSolved_CountingScore = 0;
3529 player->LevelSolved_CountingHealth = 0;
3531 map_player_action[i] = i;
3534 network_player_action_received = FALSE;
3536 /* initial null action */
3537 if (network_playing)
3538 SendToServer_MovePlayer(MV_NONE);
3546 TimeLeft = level.time;
3549 ScreenMovDir = MV_NONE;
3553 ScrollStepSize = 0; /* will be correctly initialized by ScrollScreen() */
3555 AllPlayersGone = FALSE;
3557 game.no_time_limit = (level.time == 0);
3559 game.yamyam_content_nr = 0;
3560 game.robot_wheel_active = FALSE;
3561 game.magic_wall_active = FALSE;
3562 game.magic_wall_time_left = 0;
3563 game.light_time_left = 0;
3564 game.timegate_time_left = 0;
3565 game.switchgate_pos = 0;
3566 game.wind_direction = level.wind_direction_initial;
3568 game.lenses_time_left = 0;
3569 game.magnify_time_left = 0;
3571 game.ball_state = level.ball_state_initial;
3572 game.ball_content_nr = 0;
3574 game.envelope_active = FALSE;
3576 for (i = 0; i < NUM_BELTS; i++)
3578 game.belt_dir[i] = MV_NONE;
3579 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
3582 for (i = 0; i < MAX_NUM_AMOEBA; i++)
3583 AmoebaCnt[i] = AmoebaCnt2[i] = 0;
3585 #if DEBUG_INIT_PLAYER
3586 DebugPrintPlayerStatus("Player status at level initialization");
3589 SCAN_PLAYFIELD(x, y)
3591 Feld[x][y] = level.field[x][y];
3592 MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
3593 ChangeDelay[x][y] = 0;
3594 ChangePage[x][y] = -1;
3595 CustomValue[x][y] = 0; /* initialized in InitField() */
3596 Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
3598 WasJustMoving[x][y] = 0;
3599 WasJustFalling[x][y] = 0;
3600 CheckCollision[x][y] = 0;
3601 CheckImpact[x][y] = 0;
3603 Pushed[x][y] = FALSE;
3605 ChangeCount[x][y] = 0;
3606 ChangeEvent[x][y] = -1;
3608 ExplodePhase[x][y] = 0;
3609 ExplodeDelay[x][y] = 0;
3610 ExplodeField[x][y] = EX_TYPE_NONE;
3612 RunnerVisit[x][y] = 0;
3613 PlayerVisit[x][y] = 0;
3616 GfxRandom[x][y] = INIT_GFX_RANDOM();
3617 GfxElement[x][y] = EL_UNDEFINED;
3618 GfxAction[x][y] = ACTION_DEFAULT;
3619 GfxDir[x][y] = MV_NONE;
3620 GfxRedraw[x][y] = GFX_REDRAW_NONE;
3623 SCAN_PLAYFIELD(x, y)
3625 if (emulate_bd && !IS_BD_ELEMENT(Feld[x][y]))
3627 if (emulate_sb && !IS_SB_ELEMENT(Feld[x][y]))
3629 if (emulate_sp && !IS_SP_ELEMENT(Feld[x][y]))
3632 InitField(x, y, TRUE);
3634 ResetGfxAnimation(x, y);
3639 for (i = 0; i < MAX_PLAYERS; i++)
3641 struct PlayerInfo *player = &stored_player[i];
3643 /* set number of special actions for bored and sleeping animation */
3644 player->num_special_action_bored =
3645 get_num_special_action(player->artwork_element,
3646 ACTION_BORING_1, ACTION_BORING_LAST);
3647 player->num_special_action_sleeping =
3648 get_num_special_action(player->artwork_element,
3649 ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
3652 game.emulation = (emulate_bd ? EMU_BOULDERDASH :
3653 emulate_sb ? EMU_SOKOBAN :
3654 emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
3656 /* initialize type of slippery elements */
3657 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3659 if (!IS_CUSTOM_ELEMENT(i))
3661 /* default: elements slip down either to the left or right randomly */
3662 element_info[i].slippery_type = SLIPPERY_ANY_RANDOM;
3664 /* SP style elements prefer to slip down on the left side */
3665 if (game.engine_version >= VERSION_IDENT(3,1,1,0) && IS_SP_ELEMENT(i))
3666 element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3668 /* BD style elements prefer to slip down on the left side */
3669 if (game.emulation == EMU_BOULDERDASH)
3670 element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3674 /* initialize explosion and ignition delay */
3675 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3677 if (!IS_CUSTOM_ELEMENT(i))
3680 int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
3681 game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
3682 game.emulation == EMU_SUPAPLEX ? 3 : 2);
3683 int last_phase = (num_phase + 1) * delay;
3684 int half_phase = (num_phase / 2) * delay;
3686 element_info[i].explosion_delay = last_phase - 1;
3687 element_info[i].ignition_delay = half_phase;
3689 if (i == EL_BLACK_ORB)
3690 element_info[i].ignition_delay = 1;
3694 /* correct non-moving belts to start moving left */
3695 for (i = 0; i < NUM_BELTS; i++)
3696 if (game.belt_dir[i] == MV_NONE)
3697 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
3699 #if USE_NEW_PLAYER_ASSIGNMENTS
3700 for (i = 0; i < MAX_PLAYERS; i++)
3702 stored_player[i].connected = FALSE;
3704 /* in network game mode, the local player might not be the first player */
3705 if (stored_player[i].connected_locally)
3706 local_player = &stored_player[i];
3709 if (!network.enabled)
3710 local_player->connected = TRUE;
3714 for (i = 0; i < MAX_PLAYERS; i++)
3715 stored_player[i].connected = tape.player_participates[i];
3717 else if (network.enabled)
3719 /* add team mode players connected over the network (needed for correct
3720 assignment of player figures from level to locally playing players) */
3722 for (i = 0; i < MAX_PLAYERS; i++)
3723 if (stored_player[i].connected_network)
3724 stored_player[i].connected = TRUE;
3726 else if (game.team_mode)
3728 /* try to guess locally connected team mode players (needed for correct
3729 assignment of player figures from level to locally playing players) */
3731 for (i = 0; i < MAX_PLAYERS; i++)
3732 if (setup.input[i].use_joystick ||
3733 setup.input[i].key.left != KSYM_UNDEFINED)
3734 stored_player[i].connected = TRUE;
3737 #if DEBUG_INIT_PLAYER
3738 DebugPrintPlayerStatus("Player status after level initialization");
3741 #if DEBUG_INIT_PLAYER
3743 printf("Reassigning players ...\n");
3746 /* check if any connected player was not found in playfield */
3747 for (i = 0; i < MAX_PLAYERS; i++)
3749 struct PlayerInfo *player = &stored_player[i];
3751 if (player->connected && !player->present)
3753 struct PlayerInfo *field_player = NULL;
3755 #if DEBUG_INIT_PLAYER
3757 printf("- looking for field player for player %d ...\n", i + 1);
3760 /* assign first free player found that is present in the playfield */
3762 /* first try: look for unmapped playfield player that is not connected */
3763 for (j = 0; j < MAX_PLAYERS; j++)
3764 if (field_player == NULL &&
3765 stored_player[j].present &&
3766 !stored_player[j].mapped &&
3767 !stored_player[j].connected)
3768 field_player = &stored_player[j];
3770 /* second try: look for *any* unmapped playfield player */
3771 for (j = 0; j < MAX_PLAYERS; j++)
3772 if (field_player == NULL &&
3773 stored_player[j].present &&
3774 !stored_player[j].mapped)
3775 field_player = &stored_player[j];
3777 if (field_player != NULL)
3779 int jx = field_player->jx, jy = field_player->jy;
3781 #if DEBUG_INIT_PLAYER
3783 printf("- found player %d\n", field_player->index_nr + 1);
3786 player->present = FALSE;
3787 player->active = FALSE;
3789 field_player->present = TRUE;
3790 field_player->active = TRUE;
3793 player->initial_element = field_player->initial_element;
3794 player->artwork_element = field_player->artwork_element;
3796 player->block_last_field = field_player->block_last_field;
3797 player->block_delay_adjustment = field_player->block_delay_adjustment;
3800 StorePlayer[jx][jy] = field_player->element_nr;
3802 field_player->jx = field_player->last_jx = jx;
3803 field_player->jy = field_player->last_jy = jy;
3805 if (local_player == player)
3806 local_player = field_player;
3808 map_player_action[field_player->index_nr] = i;
3810 field_player->mapped = TRUE;
3812 #if DEBUG_INIT_PLAYER
3814 printf("- map_player_action[%d] == %d\n",
3815 field_player->index_nr + 1, i + 1);
3820 if (player->connected && player->present)
3821 player->mapped = TRUE;
3824 #if DEBUG_INIT_PLAYER
3825 DebugPrintPlayerStatus("Player status after player assignment (first stage)");
3830 /* check if any connected player was not found in playfield */
3831 for (i = 0; i < MAX_PLAYERS; i++)
3833 struct PlayerInfo *player = &stored_player[i];
3835 if (player->connected && !player->present)
3837 for (j = 0; j < MAX_PLAYERS; j++)
3839 struct PlayerInfo *field_player = &stored_player[j];
3840 int jx = field_player->jx, jy = field_player->jy;
3842 /* assign first free player found that is present in the playfield */
3843 if (field_player->present && !field_player->connected)
3845 player->present = TRUE;
3846 player->active = TRUE;
3848 field_player->present = FALSE;
3849 field_player->active = FALSE;
3851 player->initial_element = field_player->initial_element;
3852 player->artwork_element = field_player->artwork_element;
3854 player->block_last_field = field_player->block_last_field;
3855 player->block_delay_adjustment = field_player->block_delay_adjustment;
3857 StorePlayer[jx][jy] = player->element_nr;
3859 player->jx = player->last_jx = jx;
3860 player->jy = player->last_jy = jy;
3870 printf("::: local_player->present == %d\n", local_player->present);
3873 /* set focus to local player for network games, else to all players */
3874 game.centered_player_nr = (network_playing ? local_player->index_nr : -1);
3875 game.centered_player_nr_next = game.centered_player_nr;
3876 game.set_centered_player = FALSE;
3878 if (network_playing && tape.recording)
3880 /* store client dependent player focus when recording network games */
3881 tape.centered_player_nr_next = game.centered_player_nr_next;
3882 tape.set_centered_player = TRUE;
3887 /* when playing a tape, eliminate all players who do not participate */
3889 #if USE_NEW_PLAYER_ASSIGNMENTS
3891 if (!game.team_mode)
3893 for (i = 0; i < MAX_PLAYERS; i++)
3895 if (stored_player[i].active &&
3896 !tape.player_participates[map_player_action[i]])
3898 struct PlayerInfo *player = &stored_player[i];
3899 int jx = player->jx, jy = player->jy;
3901 #if DEBUG_INIT_PLAYER
3903 printf("Removing player %d at (%d, %d)\n", i + 1, jx, jy);
3906 player->active = FALSE;
3907 StorePlayer[jx][jy] = 0;
3908 Feld[jx][jy] = EL_EMPTY;
3915 for (i = 0; i < MAX_PLAYERS; i++)
3917 if (stored_player[i].active &&
3918 !tape.player_participates[i])
3920 struct PlayerInfo *player = &stored_player[i];
3921 int jx = player->jx, jy = player->jy;
3923 player->active = FALSE;
3924 StorePlayer[jx][jy] = 0;
3925 Feld[jx][jy] = EL_EMPTY;
3930 else if (!network.enabled && !game.team_mode) /* && !tape.playing */
3932 /* when in single player mode, eliminate all but the local player */
3934 for (i = 0; i < MAX_PLAYERS; i++)
3936 struct PlayerInfo *player = &stored_player[i];
3938 if (player->active && player != local_player)
3940 int jx = player->jx, jy = player->jy;
3942 player->active = FALSE;
3943 player->present = FALSE;
3945 StorePlayer[jx][jy] = 0;
3946 Feld[jx][jy] = EL_EMPTY;
3951 /* when recording the game, store which players take part in the game */
3954 #if USE_NEW_PLAYER_ASSIGNMENTS
3955 for (i = 0; i < MAX_PLAYERS; i++)
3956 if (stored_player[i].connected)
3957 tape.player_participates[i] = TRUE;
3959 for (i = 0; i < MAX_PLAYERS; i++)
3960 if (stored_player[i].active)
3961 tape.player_participates[i] = TRUE;
3965 #if DEBUG_INIT_PLAYER
3966 DebugPrintPlayerStatus("Player status after player assignment (final stage)");
3969 if (BorderElement == EL_EMPTY)
3972 SBX_Right = lev_fieldx - SCR_FIELDX;
3974 SBY_Lower = lev_fieldy - SCR_FIELDY;
3979 SBX_Right = lev_fieldx - SCR_FIELDX + 1;
3981 SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
3984 if (full_lev_fieldx <= SCR_FIELDX)
3985 SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
3986 if (full_lev_fieldy <= SCR_FIELDY)
3987 SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
3989 if (EVEN(SCR_FIELDX) && full_lev_fieldx > SCR_FIELDX)
3991 if (EVEN(SCR_FIELDY) && full_lev_fieldy > SCR_FIELDY)
3994 /* if local player not found, look for custom element that might create
3995 the player (make some assumptions about the right custom element) */
3996 if (!local_player->present)
3998 int start_x = 0, start_y = 0;
3999 int found_rating = 0;
4000 int found_element = EL_UNDEFINED;
4001 int player_nr = local_player->index_nr;
4003 SCAN_PLAYFIELD(x, y)
4005 int element = Feld[x][y];
4010 if (level.use_start_element[player_nr] &&
4011 level.start_element[player_nr] == element &&
4018 found_element = element;
4021 if (!IS_CUSTOM_ELEMENT(element))
4024 if (CAN_CHANGE(element))
4026 for (i = 0; i < element_info[element].num_change_pages; i++)
4028 /* check for player created from custom element as single target */
4029 content = element_info[element].change_page[i].target_element;
4030 is_player = ELEM_IS_PLAYER(content);
4032 if (is_player && (found_rating < 3 ||
4033 (found_rating == 3 && element < found_element)))
4039 found_element = element;
4044 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
4046 /* check for player created from custom element as explosion content */
4047 content = element_info[element].content.e[xx][yy];
4048 is_player = ELEM_IS_PLAYER(content);
4050 if (is_player && (found_rating < 2 ||
4051 (found_rating == 2 && element < found_element)))
4053 start_x = x + xx - 1;
4054 start_y = y + yy - 1;
4057 found_element = element;
4060 if (!CAN_CHANGE(element))
4063 for (i = 0; i < element_info[element].num_change_pages; i++)
4065 /* check for player created from custom element as extended target */
4067 element_info[element].change_page[i].target_content.e[xx][yy];
4069 is_player = ELEM_IS_PLAYER(content);
4071 if (is_player && (found_rating < 1 ||
4072 (found_rating == 1 && element < found_element)))
4074 start_x = x + xx - 1;
4075 start_y = y + yy - 1;
4078 found_element = element;
4084 scroll_x = SCROLL_POSITION_X(start_x);
4085 scroll_y = SCROLL_POSITION_Y(start_y);
4089 scroll_x = SCROLL_POSITION_X(local_player->jx);
4090 scroll_y = SCROLL_POSITION_Y(local_player->jy);
4093 /* !!! FIX THIS (START) !!! */
4094 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
4096 InitGameEngine_EM();
4098 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
4100 InitGameEngine_SP();
4102 else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4104 InitGameEngine_MM();
4108 DrawLevel(REDRAW_FIELD);
4111 /* after drawing the level, correct some elements */
4112 if (game.timegate_time_left == 0)
4113 CloseAllOpenTimegates();
4116 /* blit playfield from scroll buffer to normal back buffer for fading in */
4117 BlitScreenToBitmap(backbuffer);
4118 /* !!! FIX THIS (END) !!! */
4120 DrawMaskedBorder(fade_mask);
4125 // full screen redraw is required at this point in the following cases:
4126 // - special editor door undrawn when game was started from level editor
4127 // - drawing area (playfield) was changed and has to be removed completely
4128 redraw_mask = REDRAW_ALL;
4132 if (!game.restart_level)
4134 /* copy default game door content to main double buffer */
4136 /* !!! CHECK AGAIN !!! */
4137 SetPanelBackground();
4138 // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
4139 DrawBackground(DX, DY, DXSIZE, DYSIZE);
4142 SetPanelBackground();
4143 SetDrawBackgroundMask(REDRAW_DOOR_1);
4145 UpdateAndDisplayGameControlValues();
4147 if (!game.restart_level)
4153 CreateGameButtons();
4158 /* copy actual game door content to door double buffer for OpenDoor() */
4159 BlitBitmap(drawto, bitmap_db_door_1, DX, DY, DXSIZE, DYSIZE, 0, 0);
4161 OpenDoor(DOOR_OPEN_ALL);
4163 KeyboardAutoRepeatOffUnlessAutoplay();
4165 #if DEBUG_INIT_PLAYER
4166 DebugPrintPlayerStatus("Player status (final)");
4175 if (!game.restart_level && !tape.playing)
4177 LevelStats_incPlayed(level_nr);
4179 SaveLevelSetup_SeriesInfo();
4182 game.restart_level = FALSE;
4183 game.restart_game_message = NULL;
4185 if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4186 InitGameActions_MM();
4188 SaveEngineSnapshotToListInitial();
4190 if (!game.restart_level)
4192 PlaySound(SND_GAME_STARTING);
4194 if (setup.sound_music)
4199 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y,
4200 int actual_player_x, int actual_player_y)
4202 /* this is used for non-R'n'D game engines to update certain engine values */
4204 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
4206 actual_player_x = correctLevelPosX_EM(actual_player_x);
4207 actual_player_y = correctLevelPosY_EM(actual_player_y);
4210 /* needed to determine if sounds are played within the visible screen area */
4211 scroll_x = actual_scroll_x;
4212 scroll_y = actual_scroll_y;
4214 /* needed to get player position for "follow finger" playing input method */
4215 local_player->jx = actual_player_x;
4216 local_player->jy = actual_player_y;
4219 void InitMovDir(int x, int y)
4221 int i, element = Feld[x][y];
4222 static int xy[4][2] =
4229 static int direction[3][4] =
4231 { MV_RIGHT, MV_UP, MV_LEFT, MV_DOWN },
4232 { MV_LEFT, MV_DOWN, MV_RIGHT, MV_UP },
4233 { MV_LEFT, MV_RIGHT, MV_UP, MV_DOWN }
4242 Feld[x][y] = EL_BUG;
4243 MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
4246 case EL_SPACESHIP_RIGHT:
4247 case EL_SPACESHIP_UP:
4248 case EL_SPACESHIP_LEFT:
4249 case EL_SPACESHIP_DOWN:
4250 Feld[x][y] = EL_SPACESHIP;
4251 MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
4254 case EL_BD_BUTTERFLY_RIGHT:
4255 case EL_BD_BUTTERFLY_UP:
4256 case EL_BD_BUTTERFLY_LEFT:
4257 case EL_BD_BUTTERFLY_DOWN:
4258 Feld[x][y] = EL_BD_BUTTERFLY;
4259 MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
4262 case EL_BD_FIREFLY_RIGHT:
4263 case EL_BD_FIREFLY_UP:
4264 case EL_BD_FIREFLY_LEFT:
4265 case EL_BD_FIREFLY_DOWN:
4266 Feld[x][y] = EL_BD_FIREFLY;
4267 MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
4270 case EL_PACMAN_RIGHT:
4272 case EL_PACMAN_LEFT:
4273 case EL_PACMAN_DOWN:
4274 Feld[x][y] = EL_PACMAN;
4275 MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
4278 case EL_YAMYAM_LEFT:
4279 case EL_YAMYAM_RIGHT:
4281 case EL_YAMYAM_DOWN:
4282 Feld[x][y] = EL_YAMYAM;
4283 MovDir[x][y] = direction[2][element - EL_YAMYAM_LEFT];
4286 case EL_SP_SNIKSNAK:
4287 MovDir[x][y] = MV_UP;
4290 case EL_SP_ELECTRON:
4291 MovDir[x][y] = MV_LEFT;
4298 Feld[x][y] = EL_MOLE;
4299 MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
4303 if (IS_CUSTOM_ELEMENT(element))
4305 struct ElementInfo *ei = &element_info[element];
4306 int move_direction_initial = ei->move_direction_initial;
4307 int move_pattern = ei->move_pattern;
4309 if (move_direction_initial == MV_START_PREVIOUS)
4311 if (MovDir[x][y] != MV_NONE)
4314 move_direction_initial = MV_START_AUTOMATIC;
4317 if (move_direction_initial == MV_START_RANDOM)
4318 MovDir[x][y] = 1 << RND(4);
4319 else if (move_direction_initial & MV_ANY_DIRECTION)
4320 MovDir[x][y] = move_direction_initial;
4321 else if (move_pattern == MV_ALL_DIRECTIONS ||
4322 move_pattern == MV_TURNING_LEFT ||
4323 move_pattern == MV_TURNING_RIGHT ||
4324 move_pattern == MV_TURNING_LEFT_RIGHT ||
4325 move_pattern == MV_TURNING_RIGHT_LEFT ||
4326 move_pattern == MV_TURNING_RANDOM)
4327 MovDir[x][y] = 1 << RND(4);
4328 else if (move_pattern == MV_HORIZONTAL)
4329 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
4330 else if (move_pattern == MV_VERTICAL)
4331 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
4332 else if (move_pattern & MV_ANY_DIRECTION)
4333 MovDir[x][y] = element_info[element].move_pattern;
4334 else if (move_pattern == MV_ALONG_LEFT_SIDE ||
4335 move_pattern == MV_ALONG_RIGHT_SIDE)
4337 /* use random direction as default start direction */
4338 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
4339 MovDir[x][y] = 1 << RND(4);
4341 for (i = 0; i < NUM_DIRECTIONS; i++)
4343 int x1 = x + xy[i][0];
4344 int y1 = y + xy[i][1];
4346 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4348 if (move_pattern == MV_ALONG_RIGHT_SIDE)
4349 MovDir[x][y] = direction[0][i];
4351 MovDir[x][y] = direction[1][i];
4360 MovDir[x][y] = 1 << RND(4);
4362 if (element != EL_BUG &&
4363 element != EL_SPACESHIP &&
4364 element != EL_BD_BUTTERFLY &&
4365 element != EL_BD_FIREFLY)
4368 for (i = 0; i < NUM_DIRECTIONS; i++)
4370 int x1 = x + xy[i][0];
4371 int y1 = y + xy[i][1];
4373 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4375 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
4377 MovDir[x][y] = direction[0][i];
4380 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
4381 element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4383 MovDir[x][y] = direction[1][i];
4392 GfxDir[x][y] = MovDir[x][y];
4395 void InitAmoebaNr(int x, int y)
4398 int group_nr = AmoebeNachbarNr(x, y);
4402 for (i = 1; i < MAX_NUM_AMOEBA; i++)
4404 if (AmoebaCnt[i] == 0)
4412 AmoebaNr[x][y] = group_nr;
4413 AmoebaCnt[group_nr]++;
4414 AmoebaCnt2[group_nr]++;
4417 static void PlayerWins(struct PlayerInfo *player)
4419 player->LevelSolved = TRUE;
4420 player->GameOver = TRUE;
4422 player->score_final = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
4423 level.native_em_level->lev->score :
4424 level.game_engine_type == GAME_ENGINE_TYPE_MM ?
4427 player->health_final = (level.game_engine_type == GAME_ENGINE_TYPE_MM ?
4428 MM_HEALTH(game_mm.laser_overload_value) :
4431 player->LevelSolved_CountingTime = (game.no_time_limit ? TimePlayed :
4433 player->LevelSolved_CountingScore = player->score_final;
4434 player->LevelSolved_CountingHealth = player->health_final;
4439 static int time_count_steps;
4440 static int time, time_final;
4441 static int score, score_final;
4442 static int health, health_final;
4443 static int game_over_delay_1 = 0;
4444 static int game_over_delay_2 = 0;
4445 static int game_over_delay_3 = 0;
4446 int game_over_delay_value_1 = 50;
4447 int game_over_delay_value_2 = 25;
4448 int game_over_delay_value_3 = 50;
4450 if (!local_player->LevelSolved_GameWon)
4454 /* do not start end game actions before the player stops moving (to exit) */
4455 if (local_player->MovPos)
4458 local_player->LevelSolved_GameWon = TRUE;
4459 local_player->LevelSolved_SaveTape = tape.recording;
4460 local_player->LevelSolved_SaveScore = !tape.playing;
4464 LevelStats_incSolved(level_nr);
4466 SaveLevelSetup_SeriesInfo();
4469 if (tape.auto_play) /* tape might already be stopped here */
4470 tape.auto_play_level_solved = TRUE;
4474 game_over_delay_1 = 0;
4475 game_over_delay_2 = 0;
4476 game_over_delay_3 = game_over_delay_value_3;
4478 time = time_final = (game.no_time_limit ? TimePlayed : TimeLeft);
4479 score = score_final = local_player->score_final;
4480 health = health_final = local_player->health_final;
4482 if (level.score[SC_TIME_BONUS] > 0)
4487 score_final += TimeLeft * level.score[SC_TIME_BONUS];
4489 else if (game.no_time_limit && TimePlayed < 999)
4492 score_final += (999 - TimePlayed) * level.score[SC_TIME_BONUS];
4495 time_count_steps = MAX(1, ABS(time_final - time) / 100);
4497 game_over_delay_1 = game_over_delay_value_1;
4499 if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4502 score_final += health * level.score[SC_TIME_BONUS];
4504 game_over_delay_2 = game_over_delay_value_2;
4507 local_player->score_final = score_final;
4508 local_player->health_final = health_final;
4511 if (level_editor_test_game)
4514 score = score_final;
4516 local_player->LevelSolved_CountingTime = time;
4517 local_player->LevelSolved_CountingScore = score;
4519 game_panel_controls[GAME_PANEL_TIME].value = time;
4520 game_panel_controls[GAME_PANEL_SCORE].value = score;
4522 DisplayGameControlValues();
4525 if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
4527 if (ExitX >= 0 && ExitY >= 0) /* local player has left the level */
4529 /* close exit door after last player */
4530 if ((AllPlayersGone &&
4531 (Feld[ExitX][ExitY] == EL_EXIT_OPEN ||
4532 Feld[ExitX][ExitY] == EL_SP_EXIT_OPEN ||
4533 Feld[ExitX][ExitY] == EL_STEEL_EXIT_OPEN)) ||
4534 Feld[ExitX][ExitY] == EL_EM_EXIT_OPEN ||
4535 Feld[ExitX][ExitY] == EL_EM_STEEL_EXIT_OPEN)
4537 int element = Feld[ExitX][ExitY];
4539 Feld[ExitX][ExitY] =
4540 (element == EL_EXIT_OPEN ? EL_EXIT_CLOSING :
4541 element == EL_EM_EXIT_OPEN ? EL_EM_EXIT_CLOSING :
4542 element == EL_SP_EXIT_OPEN ? EL_SP_EXIT_CLOSING:
4543 element == EL_STEEL_EXIT_OPEN ? EL_STEEL_EXIT_CLOSING:
4544 EL_EM_STEEL_EXIT_CLOSING);
4546 PlayLevelSoundElementAction(ExitX, ExitY, element, ACTION_CLOSING);
4549 /* player disappears */
4550 DrawLevelField(ExitX, ExitY);
4553 for (i = 0; i < MAX_PLAYERS; i++)
4555 struct PlayerInfo *player = &stored_player[i];
4557 if (player->present)
4559 RemovePlayer(player);
4561 /* player disappears */
4562 DrawLevelField(player->jx, player->jy);
4567 PlaySound(SND_GAME_WINNING);
4570 if (game_over_delay_1 > 0)
4572 game_over_delay_1--;
4577 if (time != time_final)
4579 int time_to_go = ABS(time_final - time);
4580 int time_count_dir = (time < time_final ? +1 : -1);
4582 if (time_to_go < time_count_steps)
4583 time_count_steps = 1;
4585 time += time_count_steps * time_count_dir;
4586 score += time_count_steps * level.score[SC_TIME_BONUS];
4588 local_player->LevelSolved_CountingTime = time;
4589 local_player->LevelSolved_CountingScore = score;
4591 game_panel_controls[GAME_PANEL_TIME].value = time;
4592 game_panel_controls[GAME_PANEL_SCORE].value = score;
4594 DisplayGameControlValues();
4596 if (time == time_final)
4597 StopSound(SND_GAME_LEVELTIME_BONUS);
4598 else if (setup.sound_loops)
4599 PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
4601 PlaySound(SND_GAME_LEVELTIME_BONUS);
4606 if (game_over_delay_2 > 0)
4608 game_over_delay_2--;
4613 if (health != health_final)
4615 int health_count_dir = (health < health_final ? +1 : -1);
4617 health += health_count_dir;
4618 score += level.score[SC_TIME_BONUS];
4620 local_player->LevelSolved_CountingHealth = health;
4621 local_player->LevelSolved_CountingScore = score;
4623 game_panel_controls[GAME_PANEL_HEALTH].value = health;
4624 game_panel_controls[GAME_PANEL_SCORE].value = score;
4626 DisplayGameControlValues();
4628 if (health == health_final)
4629 StopSound(SND_GAME_LEVELTIME_BONUS);
4630 else if (setup.sound_loops)
4631 PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
4633 PlaySound(SND_GAME_LEVELTIME_BONUS);
4638 local_player->LevelSolved_PanelOff = TRUE;
4640 if (game_over_delay_3 > 0)
4642 game_over_delay_3--;
4653 int last_level_nr = level_nr;
4655 local_player->LevelSolved_GameEnd = TRUE;
4657 if (local_player->LevelSolved_SaveTape)
4659 /* make sure that request dialog to save tape does not open door again */
4660 if (!global.use_envelope_request)
4661 CloseDoor(DOOR_CLOSE_1);
4663 SaveTapeChecked_LevelSolved(tape.level_nr); /* ask to save tape */
4666 /* if no tape is to be saved, close both doors simultaneously */
4667 CloseDoor(DOOR_CLOSE_ALL);
4669 if (level_editor_test_game)
4671 SetGameStatus(GAME_MODE_MAIN);
4678 if (!local_player->LevelSolved_SaveScore)
4680 SetGameStatus(GAME_MODE_MAIN);
4687 if (level_nr == leveldir_current->handicap_level)
4689 leveldir_current->handicap_level++;
4691 SaveLevelSetup_SeriesInfo();
4694 if (setup.increment_levels &&
4695 level_nr < leveldir_current->last_level)
4697 level_nr++; /* advance to next level */
4698 TapeErase(); /* start with empty tape */
4700 if (setup.auto_play_next_level)
4702 LoadLevel(level_nr);
4704 SaveLevelSetup_SeriesInfo();
4708 hi_pos = NewHiScore(last_level_nr);
4710 if (hi_pos >= 0 && !setup.skip_scores_after_game)
4712 SetGameStatus(GAME_MODE_SCORES);
4714 DrawHallOfFame(last_level_nr, hi_pos);
4716 else if (!setup.auto_play_next_level || !setup.increment_levels)
4718 SetGameStatus(GAME_MODE_MAIN);
4724 StartGameActions(network.enabled, setup.autorecord, level.random_seed);
4728 int NewHiScore(int level_nr)
4732 boolean one_score_entry_per_name = !program.many_scores_per_name;
4734 LoadScore(level_nr);
4736 if (strEqual(setup.player_name, EMPTY_PLAYER_NAME) ||
4737 local_player->score_final < highscore[MAX_SCORE_ENTRIES - 1].Score)
4740 for (k = 0; k < MAX_SCORE_ENTRIES; k++)
4742 if (local_player->score_final > highscore[k].Score)
4744 /* player has made it to the hall of fame */
4746 if (k < MAX_SCORE_ENTRIES - 1)
4748 int m = MAX_SCORE_ENTRIES - 1;
4750 if (one_score_entry_per_name)
4752 for (l = k; l < MAX_SCORE_ENTRIES; l++)
4753 if (strEqual(setup.player_name, highscore[l].Name))
4756 if (m == k) /* player's new highscore overwrites his old one */
4760 for (l = m; l > k; l--)
4762 strcpy(highscore[l].Name, highscore[l - 1].Name);
4763 highscore[l].Score = highscore[l - 1].Score;
4769 strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
4770 highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
4771 highscore[k].Score = local_player->score_final;
4776 else if (one_score_entry_per_name &&
4777 !strncmp(setup.player_name, highscore[k].Name,
4778 MAX_PLAYER_NAME_LEN))
4779 break; /* player already there with a higher score */
4783 SaveScore(level_nr);
4788 inline static int getElementMoveStepsizeExt(int x, int y, int direction)
4790 int element = Feld[x][y];
4791 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
4792 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
4793 int horiz_move = (dx != 0);
4794 int sign = (horiz_move ? dx : dy);
4795 int step = sign * element_info[element].move_stepsize;
4797 /* special values for move stepsize for spring and things on conveyor belt */
4800 if (CAN_FALL(element) &&
4801 y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
4802 step = sign * MOVE_STEPSIZE_NORMAL / 2;
4803 else if (element == EL_SPRING)
4804 step = sign * MOVE_STEPSIZE_NORMAL * 2;
4810 inline static int getElementMoveStepsize(int x, int y)
4812 return getElementMoveStepsizeExt(x, y, MovDir[x][y]);
4815 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
4817 if (player->GfxAction != action || player->GfxDir != dir)
4819 player->GfxAction = action;
4820 player->GfxDir = dir;
4822 player->StepFrame = 0;
4826 static void ResetGfxFrame(int x, int y)
4828 // profiling showed that "autotest" spends 10~20% of its time in this function
4829 if (DrawingDeactivatedField())
4832 int element = Feld[x][y];
4833 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
4835 if (graphic_info[graphic].anim_global_sync)
4836 GfxFrame[x][y] = FrameCounter;
4837 else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
4838 GfxFrame[x][y] = CustomValue[x][y];
4839 else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
4840 GfxFrame[x][y] = element_info[element].collect_score;
4841 else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
4842 GfxFrame[x][y] = ChangeDelay[x][y];
4845 static void ResetGfxAnimation(int x, int y)
4847 GfxAction[x][y] = ACTION_DEFAULT;
4848 GfxDir[x][y] = MovDir[x][y];
4851 ResetGfxFrame(x, y);
4854 static void ResetRandomAnimationValue(int x, int y)
4856 GfxRandom[x][y] = INIT_GFX_RANDOM();
4859 void InitMovingField(int x, int y, int direction)
4861 int element = Feld[x][y];
4862 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
4863 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
4866 boolean is_moving_before, is_moving_after;
4868 /* check if element was/is moving or being moved before/after mode change */
4869 is_moving_before = (WasJustMoving[x][y] != 0);
4870 is_moving_after = (getElementMoveStepsizeExt(x, y, direction) != 0);
4872 /* reset animation only for moving elements which change direction of moving
4873 or which just started or stopped moving
4874 (else CEs with property "can move" / "not moving" are reset each frame) */
4875 if (is_moving_before != is_moving_after ||
4876 direction != MovDir[x][y])
4877 ResetGfxAnimation(x, y);
4879 MovDir[x][y] = direction;
4880 GfxDir[x][y] = direction;
4882 GfxAction[x][y] = (!is_moving_after ? ACTION_WAITING :
4883 direction == MV_DOWN && CAN_FALL(element) ?
4884 ACTION_FALLING : ACTION_MOVING);
4886 /* this is needed for CEs with property "can move" / "not moving" */
4888 if (is_moving_after)
4890 if (Feld[newx][newy] == EL_EMPTY)
4891 Feld[newx][newy] = EL_BLOCKED;
4893 MovDir[newx][newy] = MovDir[x][y];
4895 CustomValue[newx][newy] = CustomValue[x][y];
4897 GfxFrame[newx][newy] = GfxFrame[x][y];
4898 GfxRandom[newx][newy] = GfxRandom[x][y];
4899 GfxAction[newx][newy] = GfxAction[x][y];
4900 GfxDir[newx][newy] = GfxDir[x][y];
4904 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
4906 int direction = MovDir[x][y];
4907 int newx = x + (direction & MV_LEFT ? -1 : direction & MV_RIGHT ? +1 : 0);
4908 int newy = y + (direction & MV_UP ? -1 : direction & MV_DOWN ? +1 : 0);
4914 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
4916 int oldx = x, oldy = y;
4917 int direction = MovDir[x][y];
4919 if (direction == MV_LEFT)
4921 else if (direction == MV_RIGHT)
4923 else if (direction == MV_UP)
4925 else if (direction == MV_DOWN)
4928 *comes_from_x = oldx;
4929 *comes_from_y = oldy;
4932 int MovingOrBlocked2Element(int x, int y)
4934 int element = Feld[x][y];
4936 if (element == EL_BLOCKED)
4940 Blocked2Moving(x, y, &oldx, &oldy);
4941 return Feld[oldx][oldy];
4947 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
4949 /* like MovingOrBlocked2Element(), but if element is moving
4950 and (x,y) is the field the moving element is just leaving,
4951 return EL_BLOCKED instead of the element value */
4952 int element = Feld[x][y];
4954 if (IS_MOVING(x, y))
4956 if (element == EL_BLOCKED)
4960 Blocked2Moving(x, y, &oldx, &oldy);
4961 return Feld[oldx][oldy];
4970 static void RemoveField(int x, int y)
4972 Feld[x][y] = EL_EMPTY;
4978 CustomValue[x][y] = 0;
4981 ChangeDelay[x][y] = 0;
4982 ChangePage[x][y] = -1;
4983 Pushed[x][y] = FALSE;
4985 GfxElement[x][y] = EL_UNDEFINED;
4986 GfxAction[x][y] = ACTION_DEFAULT;
4987 GfxDir[x][y] = MV_NONE;
4990 void RemoveMovingField(int x, int y)
4992 int oldx = x, oldy = y, newx = x, newy = y;
4993 int element = Feld[x][y];
4994 int next_element = EL_UNDEFINED;
4996 if (element != EL_BLOCKED && !IS_MOVING(x, y))
4999 if (IS_MOVING(x, y))
5001 Moving2Blocked(x, y, &newx, &newy);
5003 if (Feld[newx][newy] != EL_BLOCKED)
5005 /* element is moving, but target field is not free (blocked), but
5006 already occupied by something different (example: acid pool);
5007 in this case, only remove the moving field, but not the target */
5009 RemoveField(oldx, oldy);
5011 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5013 TEST_DrawLevelField(oldx, oldy);
5018 else if (element == EL_BLOCKED)
5020 Blocked2Moving(x, y, &oldx, &oldy);
5021 if (!IS_MOVING(oldx, oldy))
5025 if (element == EL_BLOCKED &&
5026 (Feld[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
5027 Feld[oldx][oldy] == EL_QUICKSAND_FAST_EMPTYING ||
5028 Feld[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
5029 Feld[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
5030 Feld[oldx][oldy] == EL_DC_MAGIC_WALL_EMPTYING ||
5031 Feld[oldx][oldy] == EL_AMOEBA_DROPPING))
5032 next_element = get_next_element(Feld[oldx][oldy]);
5034 RemoveField(oldx, oldy);
5035 RemoveField(newx, newy);
5037 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5039 if (next_element != EL_UNDEFINED)
5040 Feld[oldx][oldy] = next_element;
5042 TEST_DrawLevelField(oldx, oldy);
5043 TEST_DrawLevelField(newx, newy);
5046 void DrawDynamite(int x, int y)
5048 int sx = SCREENX(x), sy = SCREENY(y);
5049 int graphic = el2img(Feld[x][y]);
5052 if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
5055 if (IS_WALKABLE_INSIDE(Back[x][y]))
5059 DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
5060 else if (Store[x][y])
5061 DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
5063 frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5065 if (Back[x][y] || Store[x][y])
5066 DrawGraphicThruMask(sx, sy, graphic, frame);
5068 DrawGraphic(sx, sy, graphic, frame);
5071 void CheckDynamite(int x, int y)
5073 if (MovDelay[x][y] != 0) /* dynamite is still waiting to explode */
5077 if (MovDelay[x][y] != 0)
5080 PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5086 StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5091 static void setMinimalPlayerBoundaries(int *sx1, int *sy1, int *sx2, int *sy2)
5093 boolean num_checked_players = 0;
5096 for (i = 0; i < MAX_PLAYERS; i++)
5098 if (stored_player[i].active)
5100 int sx = stored_player[i].jx;
5101 int sy = stored_player[i].jy;
5103 if (num_checked_players == 0)
5110 *sx1 = MIN(*sx1, sx);
5111 *sy1 = MIN(*sy1, sy);
5112 *sx2 = MAX(*sx2, sx);
5113 *sy2 = MAX(*sy2, sy);
5116 num_checked_players++;
5121 static boolean checkIfAllPlayersFitToScreen_RND()
5123 int sx1 = 0, sy1 = 0, sx2 = 0, sy2 = 0;
5125 setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5127 return (sx2 - sx1 < SCR_FIELDX &&
5128 sy2 - sy1 < SCR_FIELDY);
5131 static void setScreenCenteredToAllPlayers(int *sx, int *sy)
5133 int sx1 = scroll_x, sy1 = scroll_y, sx2 = scroll_x, sy2 = scroll_y;
5135 setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5137 *sx = (sx1 + sx2) / 2;
5138 *sy = (sy1 + sy2) / 2;
5141 void DrawRelocateScreen(int old_x, int old_y, int x, int y, int move_dir,
5142 boolean center_screen, boolean quick_relocation)
5144 unsigned int frame_delay_value_old = GetVideoFrameDelay();
5145 boolean ffwd_delay = (tape.playing && tape.fast_forward);
5146 boolean no_delay = (tape.warp_forward);
5147 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5148 int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5149 int new_scroll_x, new_scroll_y;
5151 if (level.lazy_relocation && IN_VIS_FIELD(SCREENX(x), SCREENY(y)))
5153 /* case 1: quick relocation inside visible screen (without scrolling) */
5160 if (!level.shifted_relocation || center_screen)
5162 /* relocation _with_ centering of screen */
5164 new_scroll_x = SCROLL_POSITION_X(x);
5165 new_scroll_y = SCROLL_POSITION_Y(y);
5169 /* relocation _without_ centering of screen */
5171 int center_scroll_x = SCROLL_POSITION_X(old_x);
5172 int center_scroll_y = SCROLL_POSITION_Y(old_y);
5173 int offset_x = x + (scroll_x - center_scroll_x);
5174 int offset_y = y + (scroll_y - center_scroll_y);
5176 /* for new screen position, apply previous offset to center position */
5177 new_scroll_x = SCROLL_POSITION_X(offset_x);
5178 new_scroll_y = SCROLL_POSITION_Y(offset_y);
5181 if (quick_relocation)
5183 /* case 2: quick relocation (redraw without visible scrolling) */
5185 scroll_x = new_scroll_x;
5186 scroll_y = new_scroll_y;
5193 /* case 3: visible relocation (with scrolling to new position) */
5195 ScrollScreen(NULL, SCROLL_GO_ON); /* scroll last frame to full tile */
5197 SetVideoFrameDelay(wait_delay_value);
5199 while (scroll_x != new_scroll_x || scroll_y != new_scroll_y)
5202 int fx = FX, fy = FY;
5204 dx = (new_scroll_x < scroll_x ? +1 : new_scroll_x > scroll_x ? -1 : 0);
5205 dy = (new_scroll_y < scroll_y ? +1 : new_scroll_y > scroll_y ? -1 : 0);
5207 if (dx == 0 && dy == 0) /* no scrolling needed at all */
5213 fx += dx * TILEX / 2;
5214 fy += dy * TILEY / 2;
5216 ScrollLevel(dx, dy);
5219 /* scroll in two steps of half tile size to make things smoother */
5220 BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
5222 /* scroll second step to align at full tile size */
5223 BlitScreenToBitmap(window);
5229 SetVideoFrameDelay(frame_delay_value_old);
5232 void RelocatePlayer(int jx, int jy, int el_player_raw)
5234 int el_player = GET_PLAYER_ELEMENT(el_player_raw);
5235 int player_nr = GET_PLAYER_NR(el_player);
5236 struct PlayerInfo *player = &stored_player[player_nr];
5237 boolean ffwd_delay = (tape.playing && tape.fast_forward);
5238 boolean no_delay = (tape.warp_forward);
5239 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5240 int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5241 int old_jx = player->jx;
5242 int old_jy = player->jy;
5243 int old_element = Feld[old_jx][old_jy];
5244 int element = Feld[jx][jy];
5245 boolean player_relocated = (old_jx != jx || old_jy != jy);
5247 int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
5248 int move_dir_vert = (jy < old_jy ? MV_UP : jy > old_jy ? MV_DOWN : 0);
5249 int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
5250 int enter_side_vert = MV_DIR_OPPOSITE(move_dir_vert);
5251 int leave_side_horiz = move_dir_horiz;
5252 int leave_side_vert = move_dir_vert;
5253 int enter_side = enter_side_horiz | enter_side_vert;
5254 int leave_side = leave_side_horiz | leave_side_vert;
5256 if (player->GameOver) /* do not reanimate dead player */
5259 if (!player_relocated) /* no need to relocate the player */
5262 if (IS_PLAYER(jx, jy)) /* player already placed at new position */
5264 RemoveField(jx, jy); /* temporarily remove newly placed player */
5265 DrawLevelField(jx, jy);
5268 if (player->present)
5270 while (player->MovPos)
5272 ScrollPlayer(player, SCROLL_GO_ON);
5273 ScrollScreen(NULL, SCROLL_GO_ON);
5275 AdvanceFrameAndPlayerCounters(player->index_nr);
5279 BackToFront_WithFrameDelay(wait_delay_value);
5282 DrawPlayer(player); /* needed here only to cleanup last field */
5283 DrawLevelField(player->jx, player->jy); /* remove player graphic */
5285 player->is_moving = FALSE;
5288 if (IS_CUSTOM_ELEMENT(old_element))
5289 CheckElementChangeByPlayer(old_jx, old_jy, old_element,
5291 player->index_bit, leave_side);
5293 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
5295 player->index_bit, leave_side);
5297 Feld[jx][jy] = el_player;
5298 InitPlayerField(jx, jy, el_player, TRUE);
5300 /* "InitPlayerField()" above sets Feld[jx][jy] to EL_EMPTY, but it may be
5301 possible that the relocation target field did not contain a player element,
5302 but a walkable element, to which the new player was relocated -- in this
5303 case, restore that (already initialized!) element on the player field */
5304 if (!ELEM_IS_PLAYER(element)) /* player may be set on walkable element */
5306 Feld[jx][jy] = element; /* restore previously existing element */
5309 /* only visually relocate centered player */
5310 DrawRelocateScreen(old_jx, old_jy, player->jx, player->jy, player->MovDir,
5311 FALSE, level.instant_relocation);
5313 TestIfPlayerTouchesBadThing(jx, jy);
5314 TestIfPlayerTouchesCustomElement(jx, jy);
5316 if (IS_CUSTOM_ELEMENT(element))
5317 CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
5318 player->index_bit, enter_side);
5320 CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
5321 player->index_bit, enter_side);
5323 if (player->is_switching)
5325 /* ensure that relocation while still switching an element does not cause
5326 a new element to be treated as also switched directly after relocation
5327 (this is important for teleporter switches that teleport the player to
5328 a place where another teleporter switch is in the same direction, which
5329 would then incorrectly be treated as immediately switched before the
5330 direction key that caused the switch was released) */
5332 player->switch_x += jx - old_jx;
5333 player->switch_y += jy - old_jy;
5337 void Explode(int ex, int ey, int phase, int mode)
5343 /* !!! eliminate this variable !!! */
5344 int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
5346 if (game.explosions_delayed)
5348 ExplodeField[ex][ey] = mode;
5352 if (phase == EX_PHASE_START) /* initialize 'Store[][]' field */
5354 int center_element = Feld[ex][ey];
5355 int artwork_element, explosion_element; /* set these values later */
5357 /* remove things displayed in background while burning dynamite */
5358 if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
5361 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
5363 /* put moving element to center field (and let it explode there) */
5364 center_element = MovingOrBlocked2Element(ex, ey);
5365 RemoveMovingField(ex, ey);
5366 Feld[ex][ey] = center_element;
5369 /* now "center_element" is finally determined -- set related values now */
5370 artwork_element = center_element; /* for custom player artwork */
5371 explosion_element = center_element; /* for custom player artwork */
5373 if (IS_PLAYER(ex, ey))
5375 int player_nr = GET_PLAYER_NR(StorePlayer[ex][ey]);
5377 artwork_element = stored_player[player_nr].artwork_element;
5379 if (level.use_explosion_element[player_nr])
5381 explosion_element = level.explosion_element[player_nr];
5382 artwork_element = explosion_element;
5386 if (mode == EX_TYPE_NORMAL ||
5387 mode == EX_TYPE_CENTER ||
5388 mode == EX_TYPE_CROSS)
5389 PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
5391 last_phase = element_info[explosion_element].explosion_delay + 1;
5393 for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
5395 int xx = x - ex + 1;
5396 int yy = y - ey + 1;
5399 if (!IN_LEV_FIELD(x, y) ||
5400 (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
5401 (mode == EX_TYPE_CROSS && (x != ex && y != ey)))
5404 element = Feld[x][y];
5406 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
5408 element = MovingOrBlocked2Element(x, y);
5410 if (!IS_EXPLOSION_PROOF(element))
5411 RemoveMovingField(x, y);
5414 /* indestructible elements can only explode in center (but not flames) */
5415 if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
5416 mode == EX_TYPE_BORDER)) ||
5417 element == EL_FLAMES)
5420 /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
5421 behaviour, for example when touching a yamyam that explodes to rocks
5422 with active deadly shield, a rock is created under the player !!! */
5423 /* (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8) */
5425 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
5426 (game.engine_version < VERSION_IDENT(3,1,0,0) ||
5427 (x == ex && y == ey && mode != EX_TYPE_BORDER)))
5429 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
5432 if (IS_ACTIVE_BOMB(element))
5434 /* re-activate things under the bomb like gate or penguin */
5435 Feld[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
5442 /* save walkable background elements while explosion on same tile */
5443 if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
5444 (x != ex || y != ey || mode == EX_TYPE_BORDER))
5445 Back[x][y] = element;
5447 /* ignite explodable elements reached by other explosion */
5448 if (element == EL_EXPLOSION)
5449 element = Store2[x][y];
5451 if (AmoebaNr[x][y] &&
5452 (element == EL_AMOEBA_FULL ||
5453 element == EL_BD_AMOEBA ||
5454 element == EL_AMOEBA_GROWING))
5456 AmoebaCnt[AmoebaNr[x][y]]--;
5457 AmoebaCnt2[AmoebaNr[x][y]]--;
5462 if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
5464 int player_nr = StorePlayer[ex][ey] - EL_PLAYER_1;
5466 Store[x][y] = EL_PLAYER_IS_EXPLODING_1 + player_nr;
5468 if (PLAYERINFO(ex, ey)->use_murphy)
5469 Store[x][y] = EL_EMPTY;
5472 /* !!! check this case -- currently needed for rnd_rado_negundo_v,
5473 !!! levels 015 018 019 020 021 022 023 026 027 028 !!! */
5474 else if (ELEM_IS_PLAYER(center_element))
5475 Store[x][y] = EL_EMPTY;
5476 else if (center_element == EL_YAMYAM)
5477 Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
5478 else if (element_info[center_element].content.e[xx][yy] != EL_EMPTY)
5479 Store[x][y] = element_info[center_element].content.e[xx][yy];
5481 /* needed because EL_BD_BUTTERFLY is not defined as "CAN_EXPLODE"
5482 (killing EL_BD_BUTTERFLY with dynamite would result in BD diamond
5483 otherwise) -- FIX THIS !!! */
5484 else if (!CAN_EXPLODE(element) && element != EL_BD_BUTTERFLY)
5485 Store[x][y] = element_info[element].content.e[1][1];
5487 else if (!CAN_EXPLODE(element))
5488 Store[x][y] = element_info[element].content.e[1][1];
5491 Store[x][y] = EL_EMPTY;
5493 if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
5494 center_element == EL_AMOEBA_TO_DIAMOND)
5495 Store2[x][y] = element;
5497 Feld[x][y] = EL_EXPLOSION;
5498 GfxElement[x][y] = artwork_element;
5500 ExplodePhase[x][y] = 1;
5501 ExplodeDelay[x][y] = last_phase;
5506 if (center_element == EL_YAMYAM)
5507 game.yamyam_content_nr =
5508 (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
5520 GfxFrame[x][y] = 0; /* restart explosion animation */
5522 last_phase = ExplodeDelay[x][y];
5524 ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
5526 /* this can happen if the player leaves an explosion just in time */
5527 if (GfxElement[x][y] == EL_UNDEFINED)
5528 GfxElement[x][y] = EL_EMPTY;
5530 border_element = Store2[x][y];
5531 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5532 border_element = StorePlayer[x][y];
5534 if (phase == element_info[border_element].ignition_delay ||
5535 phase == last_phase)
5537 boolean border_explosion = FALSE;
5539 if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
5540 !PLAYER_EXPLOSION_PROTECTED(x, y))
5542 KillPlayerUnlessExplosionProtected(x, y);
5543 border_explosion = TRUE;
5545 else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
5547 Feld[x][y] = Store2[x][y];
5550 border_explosion = TRUE;
5552 else if (border_element == EL_AMOEBA_TO_DIAMOND)
5554 AmoebeUmwandeln(x, y);
5556 border_explosion = TRUE;
5559 /* if an element just explodes due to another explosion (chain-reaction),
5560 do not immediately end the new explosion when it was the last frame of
5561 the explosion (as it would be done in the following "if"-statement!) */
5562 if (border_explosion && phase == last_phase)
5566 if (phase == last_phase)
5570 element = Feld[x][y] = Store[x][y];
5571 Store[x][y] = Store2[x][y] = 0;
5572 GfxElement[x][y] = EL_UNDEFINED;
5574 /* player can escape from explosions and might therefore be still alive */
5575 if (element >= EL_PLAYER_IS_EXPLODING_1 &&
5576 element <= EL_PLAYER_IS_EXPLODING_4)
5578 int player_nr = element - EL_PLAYER_IS_EXPLODING_1;
5579 int explosion_element = EL_PLAYER_1 + player_nr;
5580 int xx = MIN(MAX(0, x - stored_player[player_nr].jx + 1), 2);
5581 int yy = MIN(MAX(0, y - stored_player[player_nr].jy + 1), 2);
5583 if (level.use_explosion_element[player_nr])
5584 explosion_element = level.explosion_element[player_nr];
5586 Feld[x][y] = (stored_player[player_nr].active ? EL_EMPTY :
5587 element_info[explosion_element].content.e[xx][yy]);
5590 /* restore probably existing indestructible background element */
5591 if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
5592 element = Feld[x][y] = Back[x][y];
5595 MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
5596 GfxDir[x][y] = MV_NONE;
5597 ChangeDelay[x][y] = 0;
5598 ChangePage[x][y] = -1;
5600 CustomValue[x][y] = 0;
5602 InitField_WithBug2(x, y, FALSE);
5604 TEST_DrawLevelField(x, y);
5606 TestIfElementTouchesCustomElement(x, y);
5608 if (GFX_CRUMBLED(element))
5609 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
5611 if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
5612 StorePlayer[x][y] = 0;
5614 if (ELEM_IS_PLAYER(element))
5615 RelocatePlayer(x, y, element);
5617 else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
5619 int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
5620 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5623 TEST_DrawLevelFieldCrumbled(x, y);
5625 if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
5627 DrawLevelElement(x, y, Back[x][y]);
5628 DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
5630 else if (IS_WALKABLE_UNDER(Back[x][y]))
5632 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5633 DrawLevelElementThruMask(x, y, Back[x][y]);
5635 else if (!IS_WALKABLE_INSIDE(Back[x][y]))
5636 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5640 void DynaExplode(int ex, int ey)
5643 int dynabomb_element = Feld[ex][ey];
5644 int dynabomb_size = 1;
5645 boolean dynabomb_xl = FALSE;
5646 struct PlayerInfo *player;
5647 static int xy[4][2] =
5655 if (IS_ACTIVE_BOMB(dynabomb_element))
5657 player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
5658 dynabomb_size = player->dynabomb_size;
5659 dynabomb_xl = player->dynabomb_xl;
5660 player->dynabombs_left++;
5663 Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
5665 for (i = 0; i < NUM_DIRECTIONS; i++)
5667 for (j = 1; j <= dynabomb_size; j++)
5669 int x = ex + j * xy[i][0];
5670 int y = ey + j * xy[i][1];
5673 if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
5676 element = Feld[x][y];
5678 /* do not restart explosions of fields with active bombs */
5679 if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
5682 Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
5684 if (element != EL_EMPTY && element != EL_EXPLOSION &&
5685 !IS_DIGGABLE(element) && !dynabomb_xl)
5691 void Bang(int x, int y)
5693 int element = MovingOrBlocked2Element(x, y);
5694 int explosion_type = EX_TYPE_NORMAL;
5696 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5698 struct PlayerInfo *player = PLAYERINFO(x, y);
5700 element = Feld[x][y] = player->initial_element;
5702 if (level.use_explosion_element[player->index_nr])
5704 int explosion_element = level.explosion_element[player->index_nr];
5706 if (element_info[explosion_element].explosion_type == EXPLODES_CROSS)
5707 explosion_type = EX_TYPE_CROSS;
5708 else if (element_info[explosion_element].explosion_type == EXPLODES_1X1)
5709 explosion_type = EX_TYPE_CENTER;
5717 case EL_BD_BUTTERFLY:
5720 case EL_DARK_YAMYAM:
5724 RaiseScoreElement(element);
5727 case EL_DYNABOMB_PLAYER_1_ACTIVE:
5728 case EL_DYNABOMB_PLAYER_2_ACTIVE:
5729 case EL_DYNABOMB_PLAYER_3_ACTIVE:
5730 case EL_DYNABOMB_PLAYER_4_ACTIVE:
5731 case EL_DYNABOMB_INCREASE_NUMBER:
5732 case EL_DYNABOMB_INCREASE_SIZE:
5733 case EL_DYNABOMB_INCREASE_POWER:
5734 explosion_type = EX_TYPE_DYNA;
5737 case EL_DC_LANDMINE:
5738 explosion_type = EX_TYPE_CENTER;
5743 case EL_LAMP_ACTIVE:
5744 case EL_AMOEBA_TO_DIAMOND:
5745 if (!IS_PLAYER(x, y)) /* penguin and player may be at same field */
5746 explosion_type = EX_TYPE_CENTER;
5750 if (element_info[element].explosion_type == EXPLODES_CROSS)
5751 explosion_type = EX_TYPE_CROSS;
5752 else if (element_info[element].explosion_type == EXPLODES_1X1)
5753 explosion_type = EX_TYPE_CENTER;
5757 if (explosion_type == EX_TYPE_DYNA)
5760 Explode(x, y, EX_PHASE_START, explosion_type);
5762 CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
5765 void SplashAcid(int x, int y)
5767 if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
5768 (!IN_LEV_FIELD(x - 1, y - 2) ||
5769 !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
5770 Feld[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
5772 if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
5773 (!IN_LEV_FIELD(x + 1, y - 2) ||
5774 !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
5775 Feld[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
5777 PlayLevelSound(x, y, SND_ACID_SPLASHING);
5780 static void InitBeltMovement()
5782 static int belt_base_element[4] =
5784 EL_CONVEYOR_BELT_1_LEFT,
5785 EL_CONVEYOR_BELT_2_LEFT,
5786 EL_CONVEYOR_BELT_3_LEFT,
5787 EL_CONVEYOR_BELT_4_LEFT
5789 static int belt_base_active_element[4] =
5791 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
5792 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
5793 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
5794 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
5799 /* set frame order for belt animation graphic according to belt direction */
5800 for (i = 0; i < NUM_BELTS; i++)
5804 for (j = 0; j < NUM_BELT_PARTS; j++)
5806 int element = belt_base_active_element[belt_nr] + j;
5807 int graphic_1 = el2img(element);
5808 int graphic_2 = el2panelimg(element);
5810 if (game.belt_dir[i] == MV_LEFT)
5812 graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
5813 graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
5817 graphic_info[graphic_1].anim_mode |= ANIM_REVERSE;
5818 graphic_info[graphic_2].anim_mode |= ANIM_REVERSE;
5823 SCAN_PLAYFIELD(x, y)
5825 int element = Feld[x][y];
5827 for (i = 0; i < NUM_BELTS; i++)
5829 if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
5831 int e_belt_nr = getBeltNrFromBeltElement(element);
5834 if (e_belt_nr == belt_nr)
5836 int belt_part = Feld[x][y] - belt_base_element[belt_nr];
5838 Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
5845 static void ToggleBeltSwitch(int x, int y)
5847 static int belt_base_element[4] =
5849 EL_CONVEYOR_BELT_1_LEFT,
5850 EL_CONVEYOR_BELT_2_LEFT,
5851 EL_CONVEYOR_BELT_3_LEFT,
5852 EL_CONVEYOR_BELT_4_LEFT
5854 static int belt_base_active_element[4] =
5856 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
5857 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
5858 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
5859 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
5861 static int belt_base_switch_element[4] =
5863 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
5864 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
5865 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
5866 EL_CONVEYOR_BELT_4_SWITCH_LEFT
5868 static int belt_move_dir[4] =
5876 int element = Feld[x][y];
5877 int belt_nr = getBeltNrFromBeltSwitchElement(element);
5878 int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
5879 int belt_dir = belt_move_dir[belt_dir_nr];
5882 if (!IS_BELT_SWITCH(element))
5885 game.belt_dir_nr[belt_nr] = belt_dir_nr;
5886 game.belt_dir[belt_nr] = belt_dir;
5888 if (belt_dir_nr == 3)
5891 /* set frame order for belt animation graphic according to belt direction */
5892 for (i = 0; i < NUM_BELT_PARTS; i++)
5894 int element = belt_base_active_element[belt_nr] + i;
5895 int graphic_1 = el2img(element);
5896 int graphic_2 = el2panelimg(element);
5898 if (belt_dir == MV_LEFT)
5900 graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
5901 graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
5905 graphic_info[graphic_1].anim_mode |= ANIM_REVERSE;
5906 graphic_info[graphic_2].anim_mode |= ANIM_REVERSE;
5910 SCAN_PLAYFIELD(xx, yy)
5912 int element = Feld[xx][yy];
5914 if (IS_BELT_SWITCH(element))
5916 int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
5918 if (e_belt_nr == belt_nr)
5920 Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
5921 TEST_DrawLevelField(xx, yy);
5924 else if (IS_BELT(element) && belt_dir != MV_NONE)
5926 int e_belt_nr = getBeltNrFromBeltElement(element);
5928 if (e_belt_nr == belt_nr)
5930 int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
5932 Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
5933 TEST_DrawLevelField(xx, yy);
5936 else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
5938 int e_belt_nr = getBeltNrFromBeltActiveElement(element);
5940 if (e_belt_nr == belt_nr)
5942 int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
5944 Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
5945 TEST_DrawLevelField(xx, yy);
5951 static void ToggleSwitchgateSwitch(int x, int y)
5955 game.switchgate_pos = !game.switchgate_pos;
5957 SCAN_PLAYFIELD(xx, yy)
5959 int element = Feld[xx][yy];
5961 if (element == EL_SWITCHGATE_SWITCH_UP)
5963 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_DOWN;
5964 TEST_DrawLevelField(xx, yy);
5966 else if (element == EL_SWITCHGATE_SWITCH_DOWN)
5968 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP;
5969 TEST_DrawLevelField(xx, yy);
5971 else if (element == EL_DC_SWITCHGATE_SWITCH_UP)
5973 Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_DOWN;
5974 TEST_DrawLevelField(xx, yy);
5976 else if (element == EL_DC_SWITCHGATE_SWITCH_DOWN)
5978 Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP;
5979 TEST_DrawLevelField(xx, yy);
5981 else if (element == EL_SWITCHGATE_OPEN ||
5982 element == EL_SWITCHGATE_OPENING)
5984 Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
5986 PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
5988 else if (element == EL_SWITCHGATE_CLOSED ||
5989 element == EL_SWITCHGATE_CLOSING)
5991 Feld[xx][yy] = EL_SWITCHGATE_OPENING;
5993 PlayLevelSoundAction(xx, yy, ACTION_OPENING);
5998 static int getInvisibleActiveFromInvisibleElement(int element)
6000 return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
6001 element == EL_INVISIBLE_WALL ? EL_INVISIBLE_WALL_ACTIVE :
6002 element == EL_INVISIBLE_SAND ? EL_INVISIBLE_SAND_ACTIVE :
6006 static int getInvisibleFromInvisibleActiveElement(int element)
6008 return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
6009 element == EL_INVISIBLE_WALL_ACTIVE ? EL_INVISIBLE_WALL :
6010 element == EL_INVISIBLE_SAND_ACTIVE ? EL_INVISIBLE_SAND :
6014 static void RedrawAllLightSwitchesAndInvisibleElements()
6018 SCAN_PLAYFIELD(x, y)
6020 int element = Feld[x][y];
6022 if (element == EL_LIGHT_SWITCH &&
6023 game.light_time_left > 0)
6025 Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
6026 TEST_DrawLevelField(x, y);
6028 else if (element == EL_LIGHT_SWITCH_ACTIVE &&
6029 game.light_time_left == 0)
6031 Feld[x][y] = EL_LIGHT_SWITCH;
6032 TEST_DrawLevelField(x, y);
6034 else if (element == EL_EMC_DRIPPER &&
6035 game.light_time_left > 0)
6037 Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
6038 TEST_DrawLevelField(x, y);
6040 else if (element == EL_EMC_DRIPPER_ACTIVE &&
6041 game.light_time_left == 0)
6043 Feld[x][y] = EL_EMC_DRIPPER;
6044 TEST_DrawLevelField(x, y);
6046 else if (element == EL_INVISIBLE_STEELWALL ||
6047 element == EL_INVISIBLE_WALL ||
6048 element == EL_INVISIBLE_SAND)
6050 if (game.light_time_left > 0)
6051 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
6053 TEST_DrawLevelField(x, y);
6055 /* uncrumble neighbour fields, if needed */
6056 if (element == EL_INVISIBLE_SAND)
6057 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6059 else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6060 element == EL_INVISIBLE_WALL_ACTIVE ||
6061 element == EL_INVISIBLE_SAND_ACTIVE)
6063 if (game.light_time_left == 0)
6064 Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
6066 TEST_DrawLevelField(x, y);
6068 /* re-crumble neighbour fields, if needed */
6069 if (element == EL_INVISIBLE_SAND)
6070 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6075 static void RedrawAllInvisibleElementsForLenses()
6079 SCAN_PLAYFIELD(x, y)
6081 int element = Feld[x][y];
6083 if (element == EL_EMC_DRIPPER &&
6084 game.lenses_time_left > 0)
6086 Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
6087 TEST_DrawLevelField(x, y);
6089 else if (element == EL_EMC_DRIPPER_ACTIVE &&
6090 game.lenses_time_left == 0)
6092 Feld[x][y] = EL_EMC_DRIPPER;
6093 TEST_DrawLevelField(x, y);
6095 else if (element == EL_INVISIBLE_STEELWALL ||
6096 element == EL_INVISIBLE_WALL ||
6097 element == EL_INVISIBLE_SAND)
6099 if (game.lenses_time_left > 0)
6100 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
6102 TEST_DrawLevelField(x, y);
6104 /* uncrumble neighbour fields, if needed */
6105 if (element == EL_INVISIBLE_SAND)
6106 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6108 else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6109 element == EL_INVISIBLE_WALL_ACTIVE ||
6110 element == EL_INVISIBLE_SAND_ACTIVE)
6112 if (game.lenses_time_left == 0)
6113 Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
6115 TEST_DrawLevelField(x, y);
6117 /* re-crumble neighbour fields, if needed */
6118 if (element == EL_INVISIBLE_SAND)
6119 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6124 static void RedrawAllInvisibleElementsForMagnifier()
6128 SCAN_PLAYFIELD(x, y)
6130 int element = Feld[x][y];
6132 if (element == EL_EMC_FAKE_GRASS &&
6133 game.magnify_time_left > 0)
6135 Feld[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
6136 TEST_DrawLevelField(x, y);
6138 else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
6139 game.magnify_time_left == 0)
6141 Feld[x][y] = EL_EMC_FAKE_GRASS;
6142 TEST_DrawLevelField(x, y);
6144 else if (IS_GATE_GRAY(element) &&
6145 game.magnify_time_left > 0)
6147 Feld[x][y] = (IS_RND_GATE_GRAY(element) ?
6148 element - EL_GATE_1_GRAY + EL_GATE_1_GRAY_ACTIVE :
6149 IS_EM_GATE_GRAY(element) ?
6150 element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
6151 IS_EMC_GATE_GRAY(element) ?
6152 element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
6153 IS_DC_GATE_GRAY(element) ?
6154 EL_DC_GATE_WHITE_GRAY_ACTIVE :
6156 TEST_DrawLevelField(x, y);
6158 else if (IS_GATE_GRAY_ACTIVE(element) &&
6159 game.magnify_time_left == 0)
6161 Feld[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ?
6162 element - EL_GATE_1_GRAY_ACTIVE + EL_GATE_1_GRAY :
6163 IS_EM_GATE_GRAY_ACTIVE(element) ?
6164 element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
6165 IS_EMC_GATE_GRAY_ACTIVE(element) ?
6166 element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
6167 IS_DC_GATE_GRAY_ACTIVE(element) ?
6168 EL_DC_GATE_WHITE_GRAY :
6170 TEST_DrawLevelField(x, y);
6175 static void ToggleLightSwitch(int x, int y)
6177 int element = Feld[x][y];
6179 game.light_time_left =
6180 (element == EL_LIGHT_SWITCH ?
6181 level.time_light * FRAMES_PER_SECOND : 0);
6183 RedrawAllLightSwitchesAndInvisibleElements();
6186 static void ActivateTimegateSwitch(int x, int y)
6190 game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
6192 SCAN_PLAYFIELD(xx, yy)
6194 int element = Feld[xx][yy];
6196 if (element == EL_TIMEGATE_CLOSED ||
6197 element == EL_TIMEGATE_CLOSING)
6199 Feld[xx][yy] = EL_TIMEGATE_OPENING;
6200 PlayLevelSound(xx, yy, SND_CLASS_TIMEGATE_OPENING);
6204 else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
6206 Feld[xx][yy] = EL_TIMEGATE_SWITCH;
6207 TEST_DrawLevelField(xx, yy);
6213 Feld[x][y] = (Feld[x][y] == EL_TIMEGATE_SWITCH ? EL_TIMEGATE_SWITCH_ACTIVE :
6214 EL_DC_TIMEGATE_SWITCH_ACTIVE);
6217 void Impact(int x, int y)
6219 boolean last_line = (y == lev_fieldy - 1);
6220 boolean object_hit = FALSE;
6221 boolean impact = (last_line || object_hit);
6222 int element = Feld[x][y];
6223 int smashed = EL_STEELWALL;
6225 if (!last_line) /* check if element below was hit */
6227 if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
6230 object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
6231 MovDir[x][y + 1] != MV_DOWN ||
6232 MovPos[x][y + 1] <= TILEY / 2));
6234 /* do not smash moving elements that left the smashed field in time */
6235 if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
6236 ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
6239 #if USE_QUICKSAND_IMPACT_BUGFIX
6240 if (Feld[x][y + 1] == EL_QUICKSAND_EMPTYING && object_hit == FALSE)
6242 RemoveMovingField(x, y + 1);
6243 Feld[x][y + 1] = EL_QUICKSAND_EMPTY;
6244 Feld[x][y + 2] = EL_ROCK;
6245 TEST_DrawLevelField(x, y + 2);
6250 if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTYING && object_hit == FALSE)
6252 RemoveMovingField(x, y + 1);
6253 Feld[x][y + 1] = EL_QUICKSAND_FAST_EMPTY;
6254 Feld[x][y + 2] = EL_ROCK;
6255 TEST_DrawLevelField(x, y + 2);
6262 smashed = MovingOrBlocked2Element(x, y + 1);
6264 impact = (last_line || object_hit);
6267 if (!last_line && smashed == EL_ACID) /* element falls into acid */
6269 SplashAcid(x, y + 1);
6273 /* !!! not sufficient for all cases -- see EL_PEARL below !!! */
6274 /* only reset graphic animation if graphic really changes after impact */
6276 el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
6278 ResetGfxAnimation(x, y);
6279 TEST_DrawLevelField(x, y);
6282 if (impact && CAN_EXPLODE_IMPACT(element))
6287 else if (impact && element == EL_PEARL &&
6288 smashed != EL_DC_MAGIC_WALL && smashed != EL_DC_MAGIC_WALL_ACTIVE)
6290 ResetGfxAnimation(x, y);
6292 Feld[x][y] = EL_PEARL_BREAKING;
6293 PlayLevelSound(x, y, SND_PEARL_BREAKING);
6296 else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
6298 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6303 if (impact && element == EL_AMOEBA_DROP)
6305 if (object_hit && IS_PLAYER(x, y + 1))
6306 KillPlayerUnlessEnemyProtected(x, y + 1);
6307 else if (object_hit && smashed == EL_PENGUIN)
6311 Feld[x][y] = EL_AMOEBA_GROWING;
6312 Store[x][y] = EL_AMOEBA_WET;
6314 ResetRandomAnimationValue(x, y);
6319 if (object_hit) /* check which object was hit */
6321 if ((CAN_PASS_MAGIC_WALL(element) &&
6322 (smashed == EL_MAGIC_WALL ||
6323 smashed == EL_BD_MAGIC_WALL)) ||
6324 (CAN_PASS_DC_MAGIC_WALL(element) &&
6325 smashed == EL_DC_MAGIC_WALL))
6328 int activated_magic_wall =
6329 (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
6330 smashed == EL_BD_MAGIC_WALL ? EL_BD_MAGIC_WALL_ACTIVE :
6331 EL_DC_MAGIC_WALL_ACTIVE);
6333 /* activate magic wall / mill */
6334 SCAN_PLAYFIELD(xx, yy)
6336 if (Feld[xx][yy] == smashed)
6337 Feld[xx][yy] = activated_magic_wall;
6340 game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
6341 game.magic_wall_active = TRUE;
6343 PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
6344 SND_MAGIC_WALL_ACTIVATING :
6345 smashed == EL_BD_MAGIC_WALL ?
6346 SND_BD_MAGIC_WALL_ACTIVATING :
6347 SND_DC_MAGIC_WALL_ACTIVATING));
6350 if (IS_PLAYER(x, y + 1))
6352 if (CAN_SMASH_PLAYER(element))
6354 KillPlayerUnlessEnemyProtected(x, y + 1);
6358 else if (smashed == EL_PENGUIN)
6360 if (CAN_SMASH_PLAYER(element))
6366 else if (element == EL_BD_DIAMOND)
6368 if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
6374 else if (((element == EL_SP_INFOTRON ||
6375 element == EL_SP_ZONK) &&
6376 (smashed == EL_SP_SNIKSNAK ||
6377 smashed == EL_SP_ELECTRON ||
6378 smashed == EL_SP_DISK_ORANGE)) ||
6379 (element == EL_SP_INFOTRON &&
6380 smashed == EL_SP_DISK_YELLOW))
6385 else if (CAN_SMASH_EVERYTHING(element))
6387 if (IS_CLASSIC_ENEMY(smashed) ||
6388 CAN_EXPLODE_SMASHED(smashed))
6393 else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
6395 if (smashed == EL_LAMP ||
6396 smashed == EL_LAMP_ACTIVE)
6401 else if (smashed == EL_NUT)
6403 Feld[x][y + 1] = EL_NUT_BREAKING;
6404 PlayLevelSound(x, y, SND_NUT_BREAKING);
6405 RaiseScoreElement(EL_NUT);
6408 else if (smashed == EL_PEARL)
6410 ResetGfxAnimation(x, y);
6412 Feld[x][y + 1] = EL_PEARL_BREAKING;
6413 PlayLevelSound(x, y, SND_PEARL_BREAKING);
6416 else if (smashed == EL_DIAMOND)
6418 Feld[x][y + 1] = EL_DIAMOND_BREAKING;
6419 PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
6422 else if (IS_BELT_SWITCH(smashed))
6424 ToggleBeltSwitch(x, y + 1);
6426 else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
6427 smashed == EL_SWITCHGATE_SWITCH_DOWN ||
6428 smashed == EL_DC_SWITCHGATE_SWITCH_UP ||
6429 smashed == EL_DC_SWITCHGATE_SWITCH_DOWN)
6431 ToggleSwitchgateSwitch(x, y + 1);
6433 else if (smashed == EL_LIGHT_SWITCH ||
6434 smashed == EL_LIGHT_SWITCH_ACTIVE)
6436 ToggleLightSwitch(x, y + 1);
6440 CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6442 CheckElementChangeBySide(x, y + 1, smashed, element,
6443 CE_SWITCHED, CH_SIDE_TOP);
6444 CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
6450 CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6455 /* play sound of magic wall / mill */
6457 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
6458 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ||
6459 Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE))
6461 if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
6462 PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
6463 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
6464 PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
6465 else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
6466 PlayLevelSound(x, y, SND_DC_MAGIC_WALL_FILLING);
6471 /* play sound of object that hits the ground */
6472 if (last_line || object_hit)
6473 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6476 inline static void TurnRoundExt(int x, int y)
6488 { 0, 0 }, { 0, 0 }, { 0, 0 },
6493 int left, right, back;
6497 { MV_DOWN, MV_UP, MV_RIGHT },
6498 { MV_UP, MV_DOWN, MV_LEFT },
6500 { MV_LEFT, MV_RIGHT, MV_DOWN },
6504 { MV_RIGHT, MV_LEFT, MV_UP }
6507 int element = Feld[x][y];
6508 int move_pattern = element_info[element].move_pattern;
6510 int old_move_dir = MovDir[x][y];
6511 int left_dir = turn[old_move_dir].left;
6512 int right_dir = turn[old_move_dir].right;
6513 int back_dir = turn[old_move_dir].back;
6515 int left_dx = move_xy[left_dir].dx, left_dy = move_xy[left_dir].dy;
6516 int right_dx = move_xy[right_dir].dx, right_dy = move_xy[right_dir].dy;
6517 int move_dx = move_xy[old_move_dir].dx, move_dy = move_xy[old_move_dir].dy;
6518 int back_dx = move_xy[back_dir].dx, back_dy = move_xy[back_dir].dy;
6520 int left_x = x + left_dx, left_y = y + left_dy;
6521 int right_x = x + right_dx, right_y = y + right_dy;
6522 int move_x = x + move_dx, move_y = y + move_dy;
6526 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
6528 TestIfBadThingTouchesOtherBadThing(x, y);
6530 if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
6531 MovDir[x][y] = right_dir;
6532 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6533 MovDir[x][y] = left_dir;
6535 if (element == EL_BUG && MovDir[x][y] != old_move_dir)
6537 else if (element == EL_BD_BUTTERFLY) /* && MovDir[x][y] == left_dir) */
6540 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
6542 TestIfBadThingTouchesOtherBadThing(x, y);
6544 if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
6545 MovDir[x][y] = left_dir;
6546 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6547 MovDir[x][y] = right_dir;
6549 if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
6551 else if (element == EL_BD_FIREFLY) /* && MovDir[x][y] == right_dir) */
6554 else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
6556 TestIfBadThingTouchesOtherBadThing(x, y);
6558 if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
6559 MovDir[x][y] = left_dir;
6560 else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
6561 MovDir[x][y] = right_dir;
6563 if (MovDir[x][y] != old_move_dir)
6566 else if (element == EL_YAMYAM)
6568 boolean can_turn_left = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
6569 boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
6571 if (can_turn_left && can_turn_right)
6572 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6573 else if (can_turn_left)
6574 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6575 else if (can_turn_right)
6576 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6578 MovDir[x][y] = back_dir;
6580 MovDelay[x][y] = 16 + 16 * RND(3);
6582 else if (element == EL_DARK_YAMYAM)
6584 boolean can_turn_left = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6586 boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6589 if (can_turn_left && can_turn_right)
6590 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6591 else if (can_turn_left)
6592 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6593 else if (can_turn_right)
6594 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6596 MovDir[x][y] = back_dir;
6598 MovDelay[x][y] = 16 + 16 * RND(3);
6600 else if (element == EL_PACMAN)
6602 boolean can_turn_left = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
6603 boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
6605 if (can_turn_left && can_turn_right)
6606 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6607 else if (can_turn_left)
6608 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6609 else if (can_turn_right)
6610 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6612 MovDir[x][y] = back_dir;
6614 MovDelay[x][y] = 6 + RND(40);
6616 else if (element == EL_PIG)
6618 boolean can_turn_left = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
6619 boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
6620 boolean can_move_on = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
6621 boolean should_turn_left, should_turn_right, should_move_on;
6623 int rnd = RND(rnd_value);
6625 should_turn_left = (can_turn_left &&
6627 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
6628 y + back_dy + left_dy)));
6629 should_turn_right = (can_turn_right &&
6631 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
6632 y + back_dy + right_dy)));
6633 should_move_on = (can_move_on &&
6636 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
6637 y + move_dy + left_dy) ||
6638 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
6639 y + move_dy + right_dy)));
6641 if (should_turn_left || should_turn_right || should_move_on)
6643 if (should_turn_left && should_turn_right && should_move_on)
6644 MovDir[x][y] = (rnd < rnd_value / 3 ? left_dir :
6645 rnd < 2 * rnd_value / 3 ? right_dir :
6647 else if (should_turn_left && should_turn_right)
6648 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6649 else if (should_turn_left && should_move_on)
6650 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
6651 else if (should_turn_right && should_move_on)
6652 MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
6653 else if (should_turn_left)
6654 MovDir[x][y] = left_dir;
6655 else if (should_turn_right)
6656 MovDir[x][y] = right_dir;
6657 else if (should_move_on)
6658 MovDir[x][y] = old_move_dir;
6660 else if (can_move_on && rnd > rnd_value / 8)
6661 MovDir[x][y] = old_move_dir;
6662 else if (can_turn_left && can_turn_right)
6663 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6664 else if (can_turn_left && rnd > rnd_value / 8)
6665 MovDir[x][y] = left_dir;
6666 else if (can_turn_right && rnd > rnd_value/8)
6667 MovDir[x][y] = right_dir;
6669 MovDir[x][y] = back_dir;
6671 xx = x + move_xy[MovDir[x][y]].dx;
6672 yy = y + move_xy[MovDir[x][y]].dy;
6674 if (!IN_LEV_FIELD(xx, yy) ||
6675 (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy])))
6676 MovDir[x][y] = old_move_dir;
6680 else if (element == EL_DRAGON)
6682 boolean can_turn_left = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
6683 boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
6684 boolean can_move_on = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
6686 int rnd = RND(rnd_value);
6688 if (can_move_on && rnd > rnd_value / 8)
6689 MovDir[x][y] = old_move_dir;
6690 else if (can_turn_left && can_turn_right)
6691 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6692 else if (can_turn_left && rnd > rnd_value / 8)
6693 MovDir[x][y] = left_dir;
6694 else if (can_turn_right && rnd > rnd_value / 8)
6695 MovDir[x][y] = right_dir;
6697 MovDir[x][y] = back_dir;
6699 xx = x + move_xy[MovDir[x][y]].dx;
6700 yy = y + move_xy[MovDir[x][y]].dy;
6702 if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
6703 MovDir[x][y] = old_move_dir;
6707 else if (element == EL_MOLE)
6709 boolean can_move_on =
6710 (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
6711 IS_AMOEBOID(Feld[move_x][move_y]) ||
6712 Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
6715 boolean can_turn_left =
6716 (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
6717 IS_AMOEBOID(Feld[left_x][left_y])));
6719 boolean can_turn_right =
6720 (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
6721 IS_AMOEBOID(Feld[right_x][right_y])));
6723 if (can_turn_left && can_turn_right)
6724 MovDir[x][y] = (RND(2) ? left_dir : right_dir);
6725 else if (can_turn_left)
6726 MovDir[x][y] = left_dir;
6728 MovDir[x][y] = right_dir;
6731 if (MovDir[x][y] != old_move_dir)
6734 else if (element == EL_BALLOON)
6736 MovDir[x][y] = game.wind_direction;
6739 else if (element == EL_SPRING)
6741 if (MovDir[x][y] & MV_HORIZONTAL)
6743 if (SPRING_CAN_BUMP_FROM_FIELD(move_x, move_y) &&
6744 !SPRING_CAN_ENTER_FIELD(element, x, y + 1))
6746 Feld[move_x][move_y] = EL_EMC_SPRING_BUMPER_ACTIVE;
6747 ResetGfxAnimation(move_x, move_y);
6748 TEST_DrawLevelField(move_x, move_y);
6750 MovDir[x][y] = back_dir;
6752 else if (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
6753 SPRING_CAN_ENTER_FIELD(element, x, y + 1))
6754 MovDir[x][y] = MV_NONE;
6759 else if (element == EL_ROBOT ||
6760 element == EL_SATELLITE ||
6761 element == EL_PENGUIN ||
6762 element == EL_EMC_ANDROID)
6764 int attr_x = -1, attr_y = -1;
6775 for (i = 0; i < MAX_PLAYERS; i++)
6777 struct PlayerInfo *player = &stored_player[i];
6778 int jx = player->jx, jy = player->jy;
6780 if (!player->active)
6784 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
6792 if (element == EL_ROBOT && ZX >= 0 && ZY >= 0 &&
6793 (Feld[ZX][ZY] == EL_ROBOT_WHEEL_ACTIVE ||
6794 game.engine_version < VERSION_IDENT(3,1,0,0)))
6800 if (element == EL_PENGUIN)
6803 static int xy[4][2] =
6811 for (i = 0; i < NUM_DIRECTIONS; i++)
6813 int ex = x + xy[i][0];
6814 int ey = y + xy[i][1];
6816 if (IN_LEV_FIELD(ex, ey) && (Feld[ex][ey] == EL_EXIT_OPEN ||
6817 Feld[ex][ey] == EL_EM_EXIT_OPEN ||
6818 Feld[ex][ey] == EL_STEEL_EXIT_OPEN ||
6819 Feld[ex][ey] == EL_EM_STEEL_EXIT_OPEN))
6828 MovDir[x][y] = MV_NONE;
6830 MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
6831 else if (attr_x > x)
6832 MovDir[x][y] |= (AllPlayersGone ? MV_LEFT : MV_RIGHT);
6834 MovDir[x][y] |= (AllPlayersGone ? MV_DOWN : MV_UP);
6835 else if (attr_y > y)
6836 MovDir[x][y] |= (AllPlayersGone ? MV_UP : MV_DOWN);
6838 if (element == EL_ROBOT)
6842 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6843 MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
6844 Moving2Blocked(x, y, &newx, &newy);
6846 if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
6847 MovDelay[x][y] = 8 + 8 * !RND(3);
6849 MovDelay[x][y] = 16;
6851 else if (element == EL_PENGUIN)
6857 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6859 boolean first_horiz = RND(2);
6860 int new_move_dir = MovDir[x][y];
6863 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6864 Moving2Blocked(x, y, &newx, &newy);
6866 if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
6870 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6871 Moving2Blocked(x, y, &newx, &newy);
6873 if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
6876 MovDir[x][y] = old_move_dir;
6880 else if (element == EL_SATELLITE)
6886 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6888 boolean first_horiz = RND(2);
6889 int new_move_dir = MovDir[x][y];
6892 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6893 Moving2Blocked(x, y, &newx, &newy);
6895 if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
6899 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6900 Moving2Blocked(x, y, &newx, &newy);
6902 if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
6905 MovDir[x][y] = old_move_dir;
6909 else if (element == EL_EMC_ANDROID)
6911 static int check_pos[16] =
6913 -1, /* 0 => (invalid) */
6914 7, /* 1 => MV_LEFT */
6915 3, /* 2 => MV_RIGHT */
6916 -1, /* 3 => (invalid) */
6918 0, /* 5 => MV_LEFT | MV_UP */
6919 2, /* 6 => MV_RIGHT | MV_UP */
6920 -1, /* 7 => (invalid) */
6921 5, /* 8 => MV_DOWN */
6922 6, /* 9 => MV_LEFT | MV_DOWN */
6923 4, /* 10 => MV_RIGHT | MV_DOWN */
6924 -1, /* 11 => (invalid) */
6925 -1, /* 12 => (invalid) */
6926 -1, /* 13 => (invalid) */
6927 -1, /* 14 => (invalid) */
6928 -1, /* 15 => (invalid) */
6936 { -1, -1, MV_LEFT | MV_UP },
6938 { +1, -1, MV_RIGHT | MV_UP },
6939 { +1, 0, MV_RIGHT },
6940 { +1, +1, MV_RIGHT | MV_DOWN },
6942 { -1, +1, MV_LEFT | MV_DOWN },
6945 int start_pos, check_order;
6946 boolean can_clone = FALSE;
6949 /* check if there is any free field around current position */
6950 for (i = 0; i < 8; i++)
6952 int newx = x + check_xy[i].dx;
6953 int newy = y + check_xy[i].dy;
6955 if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
6963 if (can_clone) /* randomly find an element to clone */
6967 start_pos = check_pos[RND(8)];
6968 check_order = (RND(2) ? -1 : +1);
6970 for (i = 0; i < 8; i++)
6972 int pos_raw = start_pos + i * check_order;
6973 int pos = (pos_raw + 8) % 8;
6974 int newx = x + check_xy[pos].dx;
6975 int newy = y + check_xy[pos].dy;
6977 if (ANDROID_CAN_CLONE_FIELD(newx, newy))
6979 element_info[element].move_leave_type = LEAVE_TYPE_LIMITED;
6980 element_info[element].move_leave_element = EL_TRIGGER_ELEMENT;
6982 Store[x][y] = Feld[newx][newy];
6991 if (can_clone) /* randomly find a direction to move */
6995 start_pos = check_pos[RND(8)];
6996 check_order = (RND(2) ? -1 : +1);
6998 for (i = 0; i < 8; i++)
7000 int pos_raw = start_pos + i * check_order;
7001 int pos = (pos_raw + 8) % 8;
7002 int newx = x + check_xy[pos].dx;
7003 int newy = y + check_xy[pos].dy;
7004 int new_move_dir = check_xy[pos].dir;
7006 if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7008 MovDir[x][y] = new_move_dir;
7009 MovDelay[x][y] = level.android_clone_time * 8 + 1;
7018 if (can_clone) /* cloning and moving successful */
7021 /* cannot clone -- try to move towards player */
7023 start_pos = check_pos[MovDir[x][y] & 0x0f];
7024 check_order = (RND(2) ? -1 : +1);
7026 for (i = 0; i < 3; i++)
7028 /* first check start_pos, then previous/next or (next/previous) pos */
7029 int pos_raw = start_pos + (i < 2 ? i : -1) * check_order;
7030 int pos = (pos_raw + 8) % 8;
7031 int newx = x + check_xy[pos].dx;
7032 int newy = y + check_xy[pos].dy;
7033 int new_move_dir = check_xy[pos].dir;
7035 if (IS_PLAYER(newx, newy))
7038 if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7040 MovDir[x][y] = new_move_dir;
7041 MovDelay[x][y] = level.android_move_time * 8 + 1;
7048 else if (move_pattern == MV_TURNING_LEFT ||
7049 move_pattern == MV_TURNING_RIGHT ||
7050 move_pattern == MV_TURNING_LEFT_RIGHT ||
7051 move_pattern == MV_TURNING_RIGHT_LEFT ||
7052 move_pattern == MV_TURNING_RANDOM ||
7053 move_pattern == MV_ALL_DIRECTIONS)
7055 boolean can_turn_left =
7056 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
7057 boolean can_turn_right =
7058 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
7060 if (element_info[element].move_stepsize == 0) /* "not moving" */
7063 if (move_pattern == MV_TURNING_LEFT)
7064 MovDir[x][y] = left_dir;
7065 else if (move_pattern == MV_TURNING_RIGHT)
7066 MovDir[x][y] = right_dir;
7067 else if (move_pattern == MV_TURNING_LEFT_RIGHT)
7068 MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
7069 else if (move_pattern == MV_TURNING_RIGHT_LEFT)
7070 MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
7071 else if (move_pattern == MV_TURNING_RANDOM)
7072 MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
7073 can_turn_right && !can_turn_left ? right_dir :
7074 RND(2) ? left_dir : right_dir);
7075 else if (can_turn_left && can_turn_right)
7076 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7077 else if (can_turn_left)
7078 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7079 else if (can_turn_right)
7080 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7082 MovDir[x][y] = back_dir;
7084 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7086 else if (move_pattern == MV_HORIZONTAL ||
7087 move_pattern == MV_VERTICAL)
7089 if (move_pattern & old_move_dir)
7090 MovDir[x][y] = back_dir;
7091 else if (move_pattern == MV_HORIZONTAL)
7092 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
7093 else if (move_pattern == MV_VERTICAL)
7094 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
7096 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7098 else if (move_pattern & MV_ANY_DIRECTION)
7100 MovDir[x][y] = move_pattern;
7101 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7103 else if (move_pattern & MV_WIND_DIRECTION)
7105 MovDir[x][y] = game.wind_direction;
7106 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7108 else if (move_pattern == MV_ALONG_LEFT_SIDE)
7110 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
7111 MovDir[x][y] = left_dir;
7112 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7113 MovDir[x][y] = right_dir;
7115 if (MovDir[x][y] != old_move_dir)
7116 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7118 else if (move_pattern == MV_ALONG_RIGHT_SIDE)
7120 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
7121 MovDir[x][y] = right_dir;
7122 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7123 MovDir[x][y] = left_dir;
7125 if (MovDir[x][y] != old_move_dir)
7126 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7128 else if (move_pattern == MV_TOWARDS_PLAYER ||
7129 move_pattern == MV_AWAY_FROM_PLAYER)
7131 int attr_x = -1, attr_y = -1;
7133 boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
7144 for (i = 0; i < MAX_PLAYERS; i++)
7146 struct PlayerInfo *player = &stored_player[i];
7147 int jx = player->jx, jy = player->jy;
7149 if (!player->active)
7153 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7161 MovDir[x][y] = MV_NONE;
7163 MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
7164 else if (attr_x > x)
7165 MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
7167 MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
7168 else if (attr_y > y)
7169 MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
7171 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7173 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7175 boolean first_horiz = RND(2);
7176 int new_move_dir = MovDir[x][y];
7178 if (element_info[element].move_stepsize == 0) /* "not moving" */
7180 first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
7181 MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7187 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7188 Moving2Blocked(x, y, &newx, &newy);
7190 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7194 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7195 Moving2Blocked(x, y, &newx, &newy);
7197 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7200 MovDir[x][y] = old_move_dir;
7203 else if (move_pattern == MV_WHEN_PUSHED ||
7204 move_pattern == MV_WHEN_DROPPED)
7206 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7207 MovDir[x][y] = MV_NONE;
7211 else if (move_pattern & MV_MAZE_RUNNER_STYLE)
7213 static int test_xy[7][2] =
7223 static int test_dir[7] =
7233 boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
7234 int move_preference = -1000000; /* start with very low preference */
7235 int new_move_dir = MV_NONE;
7236 int start_test = RND(4);
7239 for (i = 0; i < NUM_DIRECTIONS; i++)
7241 int move_dir = test_dir[start_test + i];
7242 int move_dir_preference;
7244 xx = x + test_xy[start_test + i][0];
7245 yy = y + test_xy[start_test + i][1];
7247 if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
7248 (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
7250 new_move_dir = move_dir;
7255 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
7258 move_dir_preference = -1 * RunnerVisit[xx][yy];
7259 if (hunter_mode && PlayerVisit[xx][yy] > 0)
7260 move_dir_preference = PlayerVisit[xx][yy];
7262 if (move_dir_preference > move_preference)
7264 /* prefer field that has not been visited for the longest time */
7265 move_preference = move_dir_preference;
7266 new_move_dir = move_dir;
7268 else if (move_dir_preference == move_preference &&
7269 move_dir == old_move_dir)
7271 /* prefer last direction when all directions are preferred equally */
7272 move_preference = move_dir_preference;
7273 new_move_dir = move_dir;
7277 MovDir[x][y] = new_move_dir;
7278 if (old_move_dir != new_move_dir)
7279 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7283 static void TurnRound(int x, int y)
7285 int direction = MovDir[x][y];
7289 GfxDir[x][y] = MovDir[x][y];
7291 if (direction != MovDir[x][y])
7295 GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction);
7297 ResetGfxFrame(x, y);
7300 static boolean JustBeingPushed(int x, int y)
7304 for (i = 0; i < MAX_PLAYERS; i++)
7306 struct PlayerInfo *player = &stored_player[i];
7308 if (player->active && player->is_pushing && player->MovPos)
7310 int next_jx = player->jx + (player->jx - player->last_jx);
7311 int next_jy = player->jy + (player->jy - player->last_jy);
7313 if (x == next_jx && y == next_jy)
7321 void StartMoving(int x, int y)
7323 boolean started_moving = FALSE; /* some elements can fall _and_ move */
7324 int element = Feld[x][y];
7329 if (MovDelay[x][y] == 0)
7330 GfxAction[x][y] = ACTION_DEFAULT;
7332 if (CAN_FALL(element) && y < lev_fieldy - 1)
7334 if ((x > 0 && IS_PLAYER(x - 1, y)) ||
7335 (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
7336 if (JustBeingPushed(x, y))
7339 if (element == EL_QUICKSAND_FULL)
7341 if (IS_FREE(x, y + 1))
7343 InitMovingField(x, y, MV_DOWN);
7344 started_moving = TRUE;
7346 Feld[x][y] = EL_QUICKSAND_EMPTYING;
7347 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7348 if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7349 Store[x][y] = EL_ROCK;
7351 Store[x][y] = EL_ROCK;
7354 PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7356 else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7358 if (!MovDelay[x][y])
7360 MovDelay[x][y] = TILEY + 1;
7362 ResetGfxAnimation(x, y);
7363 ResetGfxAnimation(x, y + 1);
7368 DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7369 DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7376 Feld[x][y] = EL_QUICKSAND_EMPTY;
7377 Feld[x][y + 1] = EL_QUICKSAND_FULL;
7378 Store[x][y + 1] = Store[x][y];
7381 PlayLevelSoundAction(x, y, ACTION_FILLING);
7383 else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7385 if (!MovDelay[x][y])
7387 MovDelay[x][y] = TILEY + 1;
7389 ResetGfxAnimation(x, y);
7390 ResetGfxAnimation(x, y + 1);
7395 DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7396 DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7403 Feld[x][y] = EL_QUICKSAND_EMPTY;
7404 Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7405 Store[x][y + 1] = Store[x][y];
7408 PlayLevelSoundAction(x, y, ACTION_FILLING);
7411 else if (element == EL_QUICKSAND_FAST_FULL)
7413 if (IS_FREE(x, y + 1))
7415 InitMovingField(x, y, MV_DOWN);
7416 started_moving = TRUE;
7418 Feld[x][y] = EL_QUICKSAND_FAST_EMPTYING;
7419 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7420 if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7421 Store[x][y] = EL_ROCK;
7423 Store[x][y] = EL_ROCK;
7426 PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7428 else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7430 if (!MovDelay[x][y])
7432 MovDelay[x][y] = TILEY + 1;
7434 ResetGfxAnimation(x, y);
7435 ResetGfxAnimation(x, y + 1);
7440 DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7441 DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7448 Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
7449 Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7450 Store[x][y + 1] = Store[x][y];
7453 PlayLevelSoundAction(x, y, ACTION_FILLING);
7455 else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7457 if (!MovDelay[x][y])
7459 MovDelay[x][y] = TILEY + 1;
7461 ResetGfxAnimation(x, y);
7462 ResetGfxAnimation(x, y + 1);
7467 DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7468 DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7475 Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
7476 Feld[x][y + 1] = EL_QUICKSAND_FULL;
7477 Store[x][y + 1] = Store[x][y];
7480 PlayLevelSoundAction(x, y, ACTION_FILLING);
7483 else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7484 Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7486 InitMovingField(x, y, MV_DOWN);
7487 started_moving = TRUE;
7489 Feld[x][y] = EL_QUICKSAND_FILLING;
7490 Store[x][y] = element;
7492 PlayLevelSoundAction(x, y, ACTION_FILLING);
7494 else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7495 Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7497 InitMovingField(x, y, MV_DOWN);
7498 started_moving = TRUE;
7500 Feld[x][y] = EL_QUICKSAND_FAST_FILLING;
7501 Store[x][y] = element;
7503 PlayLevelSoundAction(x, y, ACTION_FILLING);
7505 else if (element == EL_MAGIC_WALL_FULL)
7507 if (IS_FREE(x, y + 1))
7509 InitMovingField(x, y, MV_DOWN);
7510 started_moving = TRUE;
7512 Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
7513 Store[x][y] = EL_CHANGED(Store[x][y]);
7515 else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
7517 if (!MovDelay[x][y])
7518 MovDelay[x][y] = TILEY / 4 + 1;
7527 Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
7528 Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
7529 Store[x][y + 1] = EL_CHANGED(Store[x][y]);
7533 else if (element == EL_BD_MAGIC_WALL_FULL)
7535 if (IS_FREE(x, y + 1))
7537 InitMovingField(x, y, MV_DOWN);
7538 started_moving = TRUE;
7540 Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
7541 Store[x][y] = EL_CHANGED_BD(Store[x][y]);
7543 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
7545 if (!MovDelay[x][y])
7546 MovDelay[x][y] = TILEY / 4 + 1;
7555 Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
7556 Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
7557 Store[x][y + 1] = EL_CHANGED_BD(Store[x][y]);
7561 else if (element == EL_DC_MAGIC_WALL_FULL)
7563 if (IS_FREE(x, y + 1))
7565 InitMovingField(x, y, MV_DOWN);
7566 started_moving = TRUE;
7568 Feld[x][y] = EL_DC_MAGIC_WALL_EMPTYING;
7569 Store[x][y] = EL_CHANGED_DC(Store[x][y]);
7571 else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
7573 if (!MovDelay[x][y])
7574 MovDelay[x][y] = TILEY / 4 + 1;
7583 Feld[x][y] = EL_DC_MAGIC_WALL_ACTIVE;
7584 Feld[x][y + 1] = EL_DC_MAGIC_WALL_FULL;
7585 Store[x][y + 1] = EL_CHANGED_DC(Store[x][y]);
7589 else if ((CAN_PASS_MAGIC_WALL(element) &&
7590 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
7591 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)) ||
7592 (CAN_PASS_DC_MAGIC_WALL(element) &&
7593 (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)))
7596 InitMovingField(x, y, MV_DOWN);
7597 started_moving = TRUE;
7600 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
7601 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ? EL_BD_MAGIC_WALL_FILLING :
7602 EL_DC_MAGIC_WALL_FILLING);
7603 Store[x][y] = element;
7605 else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
7607 SplashAcid(x, y + 1);
7609 InitMovingField(x, y, MV_DOWN);
7610 started_moving = TRUE;
7612 Store[x][y] = EL_ACID;
7615 (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7616 CheckImpact[x][y] && !IS_FREE(x, y + 1)) ||
7617 (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
7618 CAN_FALL(element) && WasJustFalling[x][y] &&
7619 (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
7621 (game.engine_version < VERSION_IDENT(2,2,0,7) &&
7622 CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
7623 (Feld[x][y + 1] == EL_BLOCKED)))
7625 /* this is needed for a special case not covered by calling "Impact()"
7626 from "ContinueMoving()": if an element moves to a tile directly below
7627 another element which was just falling on that tile (which was empty
7628 in the previous frame), the falling element above would just stop
7629 instead of smashing the element below (in previous version, the above
7630 element was just checked for "moving" instead of "falling", resulting
7631 in incorrect smashes caused by horizontal movement of the above
7632 element; also, the case of the player being the element to smash was
7633 simply not covered here... :-/ ) */
7635 CheckCollision[x][y] = 0;
7636 CheckImpact[x][y] = 0;
7640 else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
7642 if (MovDir[x][y] == MV_NONE)
7644 InitMovingField(x, y, MV_DOWN);
7645 started_moving = TRUE;
7648 else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
7650 if (WasJustFalling[x][y]) /* prevent animation from being restarted */
7651 MovDir[x][y] = MV_DOWN;
7653 InitMovingField(x, y, MV_DOWN);
7654 started_moving = TRUE;
7656 else if (element == EL_AMOEBA_DROP)
7658 Feld[x][y] = EL_AMOEBA_GROWING;
7659 Store[x][y] = EL_AMOEBA_WET;
7661 else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
7662 (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
7663 !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
7664 element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
7666 boolean can_fall_left = (x > 0 && IS_FREE(x - 1, y) &&
7667 (IS_FREE(x - 1, y + 1) ||
7668 Feld[x - 1][y + 1] == EL_ACID));
7669 boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
7670 (IS_FREE(x + 1, y + 1) ||
7671 Feld[x + 1][y + 1] == EL_ACID));
7672 boolean can_fall_any = (can_fall_left || can_fall_right);
7673 boolean can_fall_both = (can_fall_left && can_fall_right);
7674 int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
7676 if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
7678 if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
7679 can_fall_right = FALSE;
7680 else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
7681 can_fall_left = FALSE;
7682 else if (slippery_type == SLIPPERY_ONLY_LEFT)
7683 can_fall_right = FALSE;
7684 else if (slippery_type == SLIPPERY_ONLY_RIGHT)
7685 can_fall_left = FALSE;
7687 can_fall_any = (can_fall_left || can_fall_right);
7688 can_fall_both = FALSE;
7693 if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
7694 can_fall_right = FALSE; /* slip down on left side */
7696 can_fall_left = !(can_fall_right = RND(2));
7698 can_fall_both = FALSE;
7703 /* if not determined otherwise, prefer left side for slipping down */
7704 InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
7705 started_moving = TRUE;
7708 else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
7710 boolean left_is_free = (x > 0 && IS_FREE(x - 1, y));
7711 boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
7712 int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
7713 int belt_dir = game.belt_dir[belt_nr];
7715 if ((belt_dir == MV_LEFT && left_is_free) ||
7716 (belt_dir == MV_RIGHT && right_is_free))
7718 int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
7720 InitMovingField(x, y, belt_dir);
7721 started_moving = TRUE;
7723 Pushed[x][y] = TRUE;
7724 Pushed[nextx][y] = TRUE;
7726 GfxAction[x][y] = ACTION_DEFAULT;
7730 MovDir[x][y] = 0; /* if element was moving, stop it */
7735 /* not "else if" because of elements that can fall and move (EL_SPRING) */
7736 if (CAN_MOVE(element) && !started_moving)
7738 int move_pattern = element_info[element].move_pattern;
7741 Moving2Blocked(x, y, &newx, &newy);
7743 if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
7746 if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7747 CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7749 WasJustMoving[x][y] = 0;
7750 CheckCollision[x][y] = 0;
7752 TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
7754 if (Feld[x][y] != element) /* element has changed */
7758 if (!MovDelay[x][y]) /* start new movement phase */
7760 /* all objects that can change their move direction after each step
7761 (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall */
7763 if (element != EL_YAMYAM &&
7764 element != EL_DARK_YAMYAM &&
7765 element != EL_PACMAN &&
7766 !(move_pattern & MV_ANY_DIRECTION) &&
7767 move_pattern != MV_TURNING_LEFT &&
7768 move_pattern != MV_TURNING_RIGHT &&
7769 move_pattern != MV_TURNING_LEFT_RIGHT &&
7770 move_pattern != MV_TURNING_RIGHT_LEFT &&
7771 move_pattern != MV_TURNING_RANDOM)
7775 if (MovDelay[x][y] && (element == EL_BUG ||
7776 element == EL_SPACESHIP ||
7777 element == EL_SP_SNIKSNAK ||
7778 element == EL_SP_ELECTRON ||
7779 element == EL_MOLE))
7780 TEST_DrawLevelField(x, y);
7784 if (MovDelay[x][y]) /* wait some time before next movement */
7788 if (element == EL_ROBOT ||
7789 element == EL_YAMYAM ||
7790 element == EL_DARK_YAMYAM)
7792 DrawLevelElementAnimationIfNeeded(x, y, element);
7793 PlayLevelSoundAction(x, y, ACTION_WAITING);
7795 else if (element == EL_SP_ELECTRON)
7796 DrawLevelElementAnimationIfNeeded(x, y, element);
7797 else if (element == EL_DRAGON)
7800 int dir = MovDir[x][y];
7801 int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
7802 int dy = (dir == MV_UP ? -1 : dir == MV_DOWN ? +1 : 0);
7803 int graphic = (dir == MV_LEFT ? IMG_FLAMES_1_LEFT :
7804 dir == MV_RIGHT ? IMG_FLAMES_1_RIGHT :
7805 dir == MV_UP ? IMG_FLAMES_1_UP :
7806 dir == MV_DOWN ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
7807 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
7809 GfxAction[x][y] = ACTION_ATTACKING;
7811 if (IS_PLAYER(x, y))
7812 DrawPlayerField(x, y);
7814 TEST_DrawLevelField(x, y);
7816 PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
7818 for (i = 1; i <= 3; i++)
7820 int xx = x + i * dx;
7821 int yy = y + i * dy;
7822 int sx = SCREENX(xx);
7823 int sy = SCREENY(yy);
7824 int flame_graphic = graphic + (i - 1);
7826 if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
7831 int flamed = MovingOrBlocked2Element(xx, yy);
7833 if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
7836 RemoveMovingField(xx, yy);
7838 ChangeDelay[xx][yy] = 0;
7840 Feld[xx][yy] = EL_FLAMES;
7842 if (IN_SCR_FIELD(sx, sy))
7844 TEST_DrawLevelFieldCrumbled(xx, yy);
7845 DrawGraphic(sx, sy, flame_graphic, frame);
7850 if (Feld[xx][yy] == EL_FLAMES)
7851 Feld[xx][yy] = EL_EMPTY;
7852 TEST_DrawLevelField(xx, yy);
7857 if (MovDelay[x][y]) /* element still has to wait some time */
7859 PlayLevelSoundAction(x, y, ACTION_WAITING);
7865 /* now make next step */
7867 Moving2Blocked(x, y, &newx, &newy); /* get next screen position */
7869 if (DONT_COLLIDE_WITH(element) &&
7870 IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
7871 !PLAYER_ENEMY_PROTECTED(newx, newy))
7873 TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
7878 else if (CAN_MOVE_INTO_ACID(element) &&
7879 IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID &&
7880 !IS_MV_DIAGONAL(MovDir[x][y]) &&
7881 (MovDir[x][y] == MV_DOWN ||
7882 game.engine_version >= VERSION_IDENT(3,1,0,0)))
7884 SplashAcid(newx, newy);
7885 Store[x][y] = EL_ACID;
7887 else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
7889 if (Feld[newx][newy] == EL_EXIT_OPEN ||
7890 Feld[newx][newy] == EL_EM_EXIT_OPEN ||
7891 Feld[newx][newy] == EL_STEEL_EXIT_OPEN ||
7892 Feld[newx][newy] == EL_EM_STEEL_EXIT_OPEN)
7895 TEST_DrawLevelField(x, y);
7897 PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
7898 if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
7899 DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
7901 local_player->friends_still_needed--;
7902 if (!local_player->friends_still_needed &&
7903 !local_player->GameOver && AllPlayersGone)
7904 PlayerWins(local_player);
7908 else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
7910 if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MP_MOVING)
7911 TEST_DrawLevelField(newx, newy);
7913 GfxDir[x][y] = MovDir[x][y] = MV_NONE;
7915 else if (!IS_FREE(newx, newy))
7917 GfxAction[x][y] = ACTION_WAITING;
7919 if (IS_PLAYER(x, y))
7920 DrawPlayerField(x, y);
7922 TEST_DrawLevelField(x, y);
7927 else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
7929 if (IS_FOOD_PIG(Feld[newx][newy]))
7931 if (IS_MOVING(newx, newy))
7932 RemoveMovingField(newx, newy);
7935 Feld[newx][newy] = EL_EMPTY;
7936 TEST_DrawLevelField(newx, newy);
7939 PlayLevelSound(x, y, SND_PIG_DIGGING);
7941 else if (!IS_FREE(newx, newy))
7943 if (IS_PLAYER(x, y))
7944 DrawPlayerField(x, y);
7946 TEST_DrawLevelField(x, y);
7951 else if (element == EL_EMC_ANDROID && IN_LEV_FIELD(newx, newy))
7953 if (Store[x][y] != EL_EMPTY)
7955 boolean can_clone = FALSE;
7958 /* check if element to clone is still there */
7959 for (yy = y - 1; yy <= y + 1; yy++) for (xx = x - 1; xx <= x + 1; xx++)
7961 if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == Store[x][y])
7969 /* cannot clone or target field not free anymore -- do not clone */
7970 if (!can_clone || !ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7971 Store[x][y] = EL_EMPTY;
7974 if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7976 if (IS_MV_DIAGONAL(MovDir[x][y]))
7978 int diagonal_move_dir = MovDir[x][y];
7979 int stored = Store[x][y];
7980 int change_delay = 8;
7983 /* android is moving diagonally */
7985 CreateField(x, y, EL_DIAGONAL_SHRINKING);
7987 Store[x][y] = (stored == EL_ACID ? EL_EMPTY : stored);
7988 GfxElement[x][y] = EL_EMC_ANDROID;
7989 GfxAction[x][y] = ACTION_SHRINKING;
7990 GfxDir[x][y] = diagonal_move_dir;
7991 ChangeDelay[x][y] = change_delay;
7993 graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],
7996 DrawLevelGraphicAnimation(x, y, graphic);
7997 PlayLevelSoundAction(x, y, ACTION_SHRINKING);
7999 if (Feld[newx][newy] == EL_ACID)
8001 SplashAcid(newx, newy);
8006 CreateField(newx, newy, EL_DIAGONAL_GROWING);
8008 Store[newx][newy] = EL_EMC_ANDROID;
8009 GfxElement[newx][newy] = EL_EMC_ANDROID;
8010 GfxAction[newx][newy] = ACTION_GROWING;
8011 GfxDir[newx][newy] = diagonal_move_dir;
8012 ChangeDelay[newx][newy] = change_delay;
8014 graphic = el_act_dir2img(GfxElement[newx][newy],
8015 GfxAction[newx][newy], GfxDir[newx][newy]);
8017 DrawLevelGraphicAnimation(newx, newy, graphic);
8018 PlayLevelSoundAction(newx, newy, ACTION_GROWING);
8024 Feld[newx][newy] = EL_EMPTY;
8025 TEST_DrawLevelField(newx, newy);
8027 PlayLevelSoundAction(x, y, ACTION_DIGGING);
8030 else if (!IS_FREE(newx, newy))
8035 else if (IS_CUSTOM_ELEMENT(element) &&
8036 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
8038 if (!DigFieldByCE(newx, newy, element))
8041 if (move_pattern & MV_MAZE_RUNNER_STYLE)
8043 RunnerVisit[x][y] = FrameCounter;
8044 PlayerVisit[x][y] /= 8; /* expire player visit path */
8047 else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
8049 if (!IS_FREE(newx, newy))
8051 if (IS_PLAYER(x, y))
8052 DrawPlayerField(x, y);
8054 TEST_DrawLevelField(x, y);
8060 boolean wanna_flame = !RND(10);
8061 int dx = newx - x, dy = newy - y;
8062 int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
8063 int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
8064 int element1 = (IN_LEV_FIELD(newx1, newy1) ?
8065 MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
8066 int element2 = (IN_LEV_FIELD(newx2, newy2) ?
8067 MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
8070 IS_CLASSIC_ENEMY(element1) ||
8071 IS_CLASSIC_ENEMY(element2)) &&
8072 element1 != EL_DRAGON && element2 != EL_DRAGON &&
8073 element1 != EL_FLAMES && element2 != EL_FLAMES)
8075 ResetGfxAnimation(x, y);
8076 GfxAction[x][y] = ACTION_ATTACKING;
8078 if (IS_PLAYER(x, y))
8079 DrawPlayerField(x, y);
8081 TEST_DrawLevelField(x, y);
8083 PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
8085 MovDelay[x][y] = 50;
8087 Feld[newx][newy] = EL_FLAMES;
8088 if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
8089 Feld[newx1][newy1] = EL_FLAMES;
8090 if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
8091 Feld[newx2][newy2] = EL_FLAMES;
8097 else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8098 Feld[newx][newy] == EL_DIAMOND)
8100 if (IS_MOVING(newx, newy))
8101 RemoveMovingField(newx, newy);
8104 Feld[newx][newy] = EL_EMPTY;
8105 TEST_DrawLevelField(newx, newy);
8108 PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
8110 else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8111 IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
8113 if (AmoebaNr[newx][newy])
8115 AmoebaCnt2[AmoebaNr[newx][newy]]--;
8116 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
8117 Feld[newx][newy] == EL_BD_AMOEBA)
8118 AmoebaCnt[AmoebaNr[newx][newy]]--;
8121 if (IS_MOVING(newx, newy))
8123 RemoveMovingField(newx, newy);
8127 Feld[newx][newy] = EL_EMPTY;
8128 TEST_DrawLevelField(newx, newy);
8131 PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
8133 else if ((element == EL_PACMAN || element == EL_MOLE)
8134 && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
8136 if (AmoebaNr[newx][newy])
8138 AmoebaCnt2[AmoebaNr[newx][newy]]--;
8139 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
8140 Feld[newx][newy] == EL_BD_AMOEBA)
8141 AmoebaCnt[AmoebaNr[newx][newy]]--;
8144 if (element == EL_MOLE)
8146 Feld[newx][newy] = EL_AMOEBA_SHRINKING;
8147 PlayLevelSound(x, y, SND_MOLE_DIGGING);
8149 ResetGfxAnimation(x, y);
8150 GfxAction[x][y] = ACTION_DIGGING;
8151 TEST_DrawLevelField(x, y);
8153 MovDelay[newx][newy] = 0; /* start amoeba shrinking delay */
8155 return; /* wait for shrinking amoeba */
8157 else /* element == EL_PACMAN */
8159 Feld[newx][newy] = EL_EMPTY;
8160 TEST_DrawLevelField(newx, newy);
8161 PlayLevelSound(x, y, SND_PACMAN_DIGGING);
8164 else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
8165 (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
8166 (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
8168 /* wait for shrinking amoeba to completely disappear */
8171 else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
8173 /* object was running against a wall */
8177 if (GFX_ELEMENT(element) != EL_SAND) /* !!! FIX THIS (crumble) !!! */
8178 DrawLevelElementAnimation(x, y, element);
8180 if (DONT_TOUCH(element))
8181 TestIfBadThingTouchesPlayer(x, y);
8186 InitMovingField(x, y, MovDir[x][y]);
8188 PlayLevelSoundAction(x, y, ACTION_MOVING);
8192 ContinueMoving(x, y);
8195 void ContinueMoving(int x, int y)
8197 int element = Feld[x][y];
8198 struct ElementInfo *ei = &element_info[element];
8199 int direction = MovDir[x][y];
8200 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
8201 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
8202 int newx = x + dx, newy = y + dy;
8203 int stored = Store[x][y];
8204 int stored_new = Store[newx][newy];
8205 boolean pushed_by_player = (Pushed[x][y] && IS_PLAYER(x, y));
8206 boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
8207 boolean last_line = (newy == lev_fieldy - 1);
8209 MovPos[x][y] += getElementMoveStepsize(x, y);
8211 if (pushed_by_player) /* special case: moving object pushed by player */
8212 MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
8214 if (ABS(MovPos[x][y]) < TILEX)
8216 TEST_DrawLevelField(x, y);
8218 return; /* element is still moving */
8221 /* element reached destination field */
8223 Feld[x][y] = EL_EMPTY;
8224 Feld[newx][newy] = element;
8225 MovPos[x][y] = 0; /* force "not moving" for "crumbled sand" */
8227 if (Store[x][y] == EL_ACID) /* element is moving into acid pool */
8229 element = Feld[newx][newy] = EL_ACID;
8231 else if (element == EL_MOLE)
8233 Feld[x][y] = EL_SAND;
8235 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8237 else if (element == EL_QUICKSAND_FILLING)
8239 element = Feld[newx][newy] = get_next_element(element);
8240 Store[newx][newy] = Store[x][y];
8242 else if (element == EL_QUICKSAND_EMPTYING)
8244 Feld[x][y] = get_next_element(element);
8245 element = Feld[newx][newy] = Store[x][y];
8247 else if (element == EL_QUICKSAND_FAST_FILLING)
8249 element = Feld[newx][newy] = get_next_element(element);
8250 Store[newx][newy] = Store[x][y];
8252 else if (element == EL_QUICKSAND_FAST_EMPTYING)
8254 Feld[x][y] = get_next_element(element);
8255 element = Feld[newx][newy] = Store[x][y];
8257 else if (element == EL_MAGIC_WALL_FILLING)
8259 element = Feld[newx][newy] = get_next_element(element);
8260 if (!game.magic_wall_active)
8261 element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
8262 Store[newx][newy] = Store[x][y];
8264 else if (element == EL_MAGIC_WALL_EMPTYING)
8266 Feld[x][y] = get_next_element(element);
8267 if (!game.magic_wall_active)
8268 Feld[x][y] = EL_MAGIC_WALL_DEAD;
8269 element = Feld[newx][newy] = Store[x][y];
8271 InitField(newx, newy, FALSE);
8273 else if (element == EL_BD_MAGIC_WALL_FILLING)
8275 element = Feld[newx][newy] = get_next_element(element);
8276 if (!game.magic_wall_active)
8277 element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
8278 Store[newx][newy] = Store[x][y];
8280 else if (element == EL_BD_MAGIC_WALL_EMPTYING)
8282 Feld[x][y] = get_next_element(element);
8283 if (!game.magic_wall_active)
8284 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
8285 element = Feld[newx][newy] = Store[x][y];
8287 InitField(newx, newy, FALSE);
8289 else if (element == EL_DC_MAGIC_WALL_FILLING)
8291 element = Feld[newx][newy] = get_next_element(element);
8292 if (!game.magic_wall_active)
8293 element = Feld[newx][newy] = EL_DC_MAGIC_WALL_DEAD;
8294 Store[newx][newy] = Store[x][y];
8296 else if (element == EL_DC_MAGIC_WALL_EMPTYING)
8298 Feld[x][y] = get_next_element(element);
8299 if (!game.magic_wall_active)
8300 Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
8301 element = Feld[newx][newy] = Store[x][y];
8303 InitField(newx, newy, FALSE);
8305 else if (element == EL_AMOEBA_DROPPING)
8307 Feld[x][y] = get_next_element(element);
8308 element = Feld[newx][newy] = Store[x][y];
8310 else if (element == EL_SOKOBAN_OBJECT)
8313 Feld[x][y] = Back[x][y];
8315 if (Back[newx][newy])
8316 Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
8318 Back[x][y] = Back[newx][newy] = 0;
8321 Store[x][y] = EL_EMPTY;
8326 MovDelay[newx][newy] = 0;
8328 if (CAN_CHANGE_OR_HAS_ACTION(element))
8330 /* copy element change control values to new field */
8331 ChangeDelay[newx][newy] = ChangeDelay[x][y];
8332 ChangePage[newx][newy] = ChangePage[x][y];
8333 ChangeCount[newx][newy] = ChangeCount[x][y];
8334 ChangeEvent[newx][newy] = ChangeEvent[x][y];
8337 CustomValue[newx][newy] = CustomValue[x][y];
8339 ChangeDelay[x][y] = 0;
8340 ChangePage[x][y] = -1;
8341 ChangeCount[x][y] = 0;
8342 ChangeEvent[x][y] = -1;
8344 CustomValue[x][y] = 0;
8346 /* copy animation control values to new field */
8347 GfxFrame[newx][newy] = GfxFrame[x][y];
8348 GfxRandom[newx][newy] = GfxRandom[x][y]; /* keep same random value */
8349 GfxAction[newx][newy] = GfxAction[x][y]; /* keep action one frame */
8350 GfxDir[newx][newy] = GfxDir[x][y]; /* keep element direction */
8352 Pushed[x][y] = Pushed[newx][newy] = FALSE;
8354 /* some elements can leave other elements behind after moving */
8355 if (ei->move_leave_element != EL_EMPTY &&
8356 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
8357 (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
8359 int move_leave_element = ei->move_leave_element;
8361 /* this makes it possible to leave the removed element again */
8362 if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
8363 move_leave_element = (stored == EL_ACID ? EL_EMPTY : stored);
8365 Feld[x][y] = move_leave_element;
8367 if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
8368 MovDir[x][y] = direction;
8370 InitField(x, y, FALSE);
8372 if (GFX_CRUMBLED(Feld[x][y]))
8373 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8375 if (ELEM_IS_PLAYER(move_leave_element))
8376 RelocatePlayer(x, y, move_leave_element);
8379 /* do this after checking for left-behind element */
8380 ResetGfxAnimation(x, y); /* reset animation values for old field */
8382 if (!CAN_MOVE(element) ||
8383 (CAN_FALL(element) && direction == MV_DOWN &&
8384 (element == EL_SPRING ||
8385 element_info[element].move_pattern == MV_WHEN_PUSHED ||
8386 element_info[element].move_pattern == MV_WHEN_DROPPED)))
8387 GfxDir[x][y] = MovDir[newx][newy] = 0;
8389 TEST_DrawLevelField(x, y);
8390 TEST_DrawLevelField(newx, newy);
8392 Stop[newx][newy] = TRUE; /* ignore this element until the next frame */
8394 /* prevent pushed element from moving on in pushed direction */
8395 if (pushed_by_player && CAN_MOVE(element) &&
8396 element_info[element].move_pattern & MV_ANY_DIRECTION &&
8397 !(element_info[element].move_pattern & direction))
8398 TurnRound(newx, newy);
8400 /* prevent elements on conveyor belt from moving on in last direction */
8401 if (pushed_by_conveyor && CAN_FALL(element) &&
8402 direction & MV_HORIZONTAL)
8403 MovDir[newx][newy] = 0;
8405 if (!pushed_by_player)
8407 int nextx = newx + dx, nexty = newy + dy;
8408 boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
8410 WasJustMoving[newx][newy] = CHECK_DELAY_MOVING;
8412 if (CAN_FALL(element) && direction == MV_DOWN)
8413 WasJustFalling[newx][newy] = CHECK_DELAY_FALLING;
8415 if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
8416 CheckCollision[newx][newy] = CHECK_DELAY_COLLISION;
8418 if (CAN_FALL(element) && direction == MV_DOWN && check_collision_again)
8419 CheckImpact[newx][newy] = CHECK_DELAY_IMPACT;
8422 if (DONT_TOUCH(element)) /* object may be nasty to player or others */
8424 TestIfBadThingTouchesPlayer(newx, newy);
8425 TestIfBadThingTouchesFriend(newx, newy);
8427 if (!IS_CUSTOM_ELEMENT(element))
8428 TestIfBadThingTouchesOtherBadThing(newx, newy);
8430 else if (element == EL_PENGUIN)
8431 TestIfFriendTouchesBadThing(newx, newy);
8433 if (DONT_GET_HIT_BY(element))
8435 TestIfGoodThingGetsHitByBadThing(newx, newy, direction);
8438 /* give the player one last chance (one more frame) to move away */
8439 if (CAN_FALL(element) && direction == MV_DOWN &&
8440 (last_line || (!IS_FREE(x, newy + 1) &&
8441 (!IS_PLAYER(x, newy + 1) ||
8442 game.engine_version < VERSION_IDENT(3,1,1,0)))))
8445 if (pushed_by_player && !game.use_change_when_pushing_bug)
8447 int push_side = MV_DIR_OPPOSITE(direction);
8448 struct PlayerInfo *player = PLAYERINFO(x, y);
8450 CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
8451 player->index_bit, push_side);
8452 CheckTriggeredElementChangeByPlayer(newx,newy, element, CE_PLAYER_PUSHES_X,
8453 player->index_bit, push_side);
8456 if (element == EL_EMC_ANDROID && pushed_by_player) /* make another move */
8457 MovDelay[newx][newy] = 1;
8459 CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
8461 TestIfElementTouchesCustomElement(x, y); /* empty or new element */
8462 TestIfElementHitsCustomElement(newx, newy, direction);
8463 TestIfPlayerTouchesCustomElement(newx, newy);
8464 TestIfElementTouchesCustomElement(newx, newy);
8466 if (IS_CUSTOM_ELEMENT(element) && ei->move_enter_element != EL_EMPTY &&
8467 IS_EQUAL_OR_IN_GROUP(stored_new, ei->move_enter_element))
8468 CheckElementChangeBySide(newx, newy, element, stored_new, CE_DIGGING_X,
8469 MV_DIR_OPPOSITE(direction));
8472 int AmoebeNachbarNr(int ax, int ay)
8475 int element = Feld[ax][ay];
8477 static int xy[4][2] =
8485 for (i = 0; i < NUM_DIRECTIONS; i++)
8487 int x = ax + xy[i][0];
8488 int y = ay + xy[i][1];
8490 if (!IN_LEV_FIELD(x, y))
8493 if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
8494 group_nr = AmoebaNr[x][y];
8500 void AmoebenVereinigen(int ax, int ay)
8502 int i, x, y, xx, yy;
8503 int new_group_nr = AmoebaNr[ax][ay];
8504 static int xy[4][2] =
8512 if (new_group_nr == 0)
8515 for (i = 0; i < NUM_DIRECTIONS; i++)
8520 if (!IN_LEV_FIELD(x, y))
8523 if ((Feld[x][y] == EL_AMOEBA_FULL ||
8524 Feld[x][y] == EL_BD_AMOEBA ||
8525 Feld[x][y] == EL_AMOEBA_DEAD) &&
8526 AmoebaNr[x][y] != new_group_nr)
8528 int old_group_nr = AmoebaNr[x][y];
8530 if (old_group_nr == 0)
8533 AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
8534 AmoebaCnt[old_group_nr] = 0;
8535 AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
8536 AmoebaCnt2[old_group_nr] = 0;
8538 SCAN_PLAYFIELD(xx, yy)
8540 if (AmoebaNr[xx][yy] == old_group_nr)
8541 AmoebaNr[xx][yy] = new_group_nr;
8547 void AmoebeUmwandeln(int ax, int ay)
8551 if (Feld[ax][ay] == EL_AMOEBA_DEAD)
8553 int group_nr = AmoebaNr[ax][ay];
8558 printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
8559 printf("AmoebeUmwandeln(): This should never happen!\n");
8564 SCAN_PLAYFIELD(x, y)
8566 if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
8569 Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
8573 PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
8574 SND_AMOEBA_TURNING_TO_GEM :
8575 SND_AMOEBA_TURNING_TO_ROCK));
8580 static int xy[4][2] =
8588 for (i = 0; i < NUM_DIRECTIONS; i++)
8593 if (!IN_LEV_FIELD(x, y))
8596 if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
8598 PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
8599 SND_AMOEBA_TURNING_TO_GEM :
8600 SND_AMOEBA_TURNING_TO_ROCK));
8607 void AmoebeUmwandelnBD(int ax, int ay, int new_element)
8610 int group_nr = AmoebaNr[ax][ay];
8611 boolean done = FALSE;
8616 printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
8617 printf("AmoebeUmwandelnBD(): This should never happen!\n");
8622 SCAN_PLAYFIELD(x, y)
8624 if (AmoebaNr[x][y] == group_nr &&
8625 (Feld[x][y] == EL_AMOEBA_DEAD ||
8626 Feld[x][y] == EL_BD_AMOEBA ||
8627 Feld[x][y] == EL_AMOEBA_GROWING))
8630 Feld[x][y] = new_element;
8631 InitField(x, y, FALSE);
8632 TEST_DrawLevelField(x, y);
8638 PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
8639 SND_BD_AMOEBA_TURNING_TO_ROCK :
8640 SND_BD_AMOEBA_TURNING_TO_GEM));
8643 void AmoebeWaechst(int x, int y)
8645 static unsigned int sound_delay = 0;
8646 static unsigned int sound_delay_value = 0;
8648 if (!MovDelay[x][y]) /* start new growing cycle */
8652 if (DelayReached(&sound_delay, sound_delay_value))
8654 PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
8655 sound_delay_value = 30;
8659 if (MovDelay[x][y]) /* wait some time before growing bigger */
8662 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
8664 int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
8665 6 - MovDelay[x][y]);
8667 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
8670 if (!MovDelay[x][y])
8672 Feld[x][y] = Store[x][y];
8674 TEST_DrawLevelField(x, y);
8679 void AmoebaDisappearing(int x, int y)
8681 static unsigned int sound_delay = 0;
8682 static unsigned int sound_delay_value = 0;
8684 if (!MovDelay[x][y]) /* start new shrinking cycle */
8688 if (DelayReached(&sound_delay, sound_delay_value))
8689 sound_delay_value = 30;
8692 if (MovDelay[x][y]) /* wait some time before shrinking */
8695 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
8697 int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
8698 6 - MovDelay[x][y]);
8700 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
8703 if (!MovDelay[x][y])
8705 Feld[x][y] = EL_EMPTY;
8706 TEST_DrawLevelField(x, y);
8708 /* don't let mole enter this field in this cycle;
8709 (give priority to objects falling to this field from above) */
8715 void AmoebeAbleger(int ax, int ay)
8718 int element = Feld[ax][ay];
8719 int graphic = el2img(element);
8720 int newax = ax, neway = ay;
8721 boolean can_drop = (element == EL_AMOEBA_WET || element == EL_EMC_DRIPPER);
8722 static int xy[4][2] =
8730 if (!level.amoeba_speed && element != EL_EMC_DRIPPER)
8732 Feld[ax][ay] = EL_AMOEBA_DEAD;
8733 TEST_DrawLevelField(ax, ay);
8737 if (IS_ANIMATED(graphic))
8738 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
8740 if (!MovDelay[ax][ay]) /* start making new amoeba field */
8741 MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
8743 if (MovDelay[ax][ay]) /* wait some time before making new amoeba */
8746 if (MovDelay[ax][ay])
8750 if (can_drop) /* EL_AMOEBA_WET or EL_EMC_DRIPPER */
8753 int x = ax + xy[start][0];
8754 int y = ay + xy[start][1];
8756 if (!IN_LEV_FIELD(x, y))
8759 if (IS_FREE(x, y) ||
8760 CAN_GROW_INTO(Feld[x][y]) ||
8761 Feld[x][y] == EL_QUICKSAND_EMPTY ||
8762 Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
8768 if (newax == ax && neway == ay)
8771 else /* normal or "filled" (BD style) amoeba */
8774 boolean waiting_for_player = FALSE;
8776 for (i = 0; i < NUM_DIRECTIONS; i++)
8778 int j = (start + i) % 4;
8779 int x = ax + xy[j][0];
8780 int y = ay + xy[j][1];
8782 if (!IN_LEV_FIELD(x, y))
8785 if (IS_FREE(x, y) ||
8786 CAN_GROW_INTO(Feld[x][y]) ||
8787 Feld[x][y] == EL_QUICKSAND_EMPTY ||
8788 Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
8794 else if (IS_PLAYER(x, y))
8795 waiting_for_player = TRUE;
8798 if (newax == ax && neway == ay) /* amoeba cannot grow */
8800 if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
8802 Feld[ax][ay] = EL_AMOEBA_DEAD;
8803 TEST_DrawLevelField(ax, ay);
8804 AmoebaCnt[AmoebaNr[ax][ay]]--;
8806 if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0) /* amoeba is completely dead */
8808 if (element == EL_AMOEBA_FULL)
8809 AmoebeUmwandeln(ax, ay);
8810 else if (element == EL_BD_AMOEBA)
8811 AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
8816 else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
8818 /* amoeba gets larger by growing in some direction */
8820 int new_group_nr = AmoebaNr[ax][ay];
8823 if (new_group_nr == 0)
8825 printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
8826 printf("AmoebeAbleger(): This should never happen!\n");
8831 AmoebaNr[newax][neway] = new_group_nr;
8832 AmoebaCnt[new_group_nr]++;
8833 AmoebaCnt2[new_group_nr]++;
8835 /* if amoeba touches other amoeba(s) after growing, unify them */
8836 AmoebenVereinigen(newax, neway);
8838 if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
8840 AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
8846 if (!can_drop || neway < ay || !IS_FREE(newax, neway) ||
8847 (neway == lev_fieldy - 1 && newax != ax))
8849 Feld[newax][neway] = EL_AMOEBA_GROWING; /* creation of new amoeba */
8850 Store[newax][neway] = element;
8852 else if (neway == ay || element == EL_EMC_DRIPPER)
8854 Feld[newax][neway] = EL_AMOEBA_DROP; /* drop left/right of amoeba */
8856 PlayLevelSoundAction(newax, neway, ACTION_GROWING);
8860 InitMovingField(ax, ay, MV_DOWN); /* drop dripping from amoeba */
8861 Feld[ax][ay] = EL_AMOEBA_DROPPING;
8862 Store[ax][ay] = EL_AMOEBA_DROP;
8863 ContinueMoving(ax, ay);
8867 TEST_DrawLevelField(newax, neway);
8870 void Life(int ax, int ay)
8874 int element = Feld[ax][ay];
8875 int graphic = el2img(element);
8876 int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
8878 boolean changed = FALSE;
8880 if (IS_ANIMATED(graphic))
8881 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
8886 if (!MovDelay[ax][ay]) /* start new "game of life" cycle */
8887 MovDelay[ax][ay] = life_time;
8889 if (MovDelay[ax][ay]) /* wait some time before next cycle */
8892 if (MovDelay[ax][ay])
8896 for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
8898 int xx = ax+x1, yy = ay+y1;
8901 if (!IN_LEV_FIELD(xx, yy))
8904 for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
8906 int x = xx+x2, y = yy+y2;
8908 if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
8911 if (((Feld[x][y] == element ||
8912 (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y))) &&
8914 (IS_FREE(x, y) && Stop[x][y]))
8918 if (xx == ax && yy == ay) /* field in the middle */
8920 if (nachbarn < life_parameter[0] ||
8921 nachbarn > life_parameter[1])
8923 Feld[xx][yy] = EL_EMPTY;
8925 TEST_DrawLevelField(xx, yy);
8926 Stop[xx][yy] = TRUE;
8930 else if (IS_FREE(xx, yy) || CAN_GROW_INTO(Feld[xx][yy]))
8931 { /* free border field */
8932 if (nachbarn >= life_parameter[2] &&
8933 nachbarn <= life_parameter[3])
8935 Feld[xx][yy] = element;
8936 MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
8938 TEST_DrawLevelField(xx, yy);
8939 Stop[xx][yy] = TRUE;
8946 PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
8947 SND_GAME_OF_LIFE_GROWING);
8950 static void InitRobotWheel(int x, int y)
8952 ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
8955 static void RunRobotWheel(int x, int y)
8957 PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
8960 static void StopRobotWheel(int x, int y)
8962 if (ZX == x && ZY == y)
8966 game.robot_wheel_active = FALSE;
8970 static void InitTimegateWheel(int x, int y)
8972 ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
8975 static void RunTimegateWheel(int x, int y)
8977 PlayLevelSound(x, y, SND_CLASS_TIMEGATE_SWITCH_ACTIVE);
8980 static void InitMagicBallDelay(int x, int y)
8982 ChangeDelay[x][y] = (level.ball_time + 1) * 8 + 1;
8985 static void ActivateMagicBall(int bx, int by)
8989 if (level.ball_random)
8991 int pos_border = RND(8); /* select one of the eight border elements */
8992 int pos_content = (pos_border > 3 ? pos_border + 1 : pos_border);
8993 int xx = pos_content % 3;
8994 int yy = pos_content / 3;
8999 if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
9000 CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9004 for (y = by - 1; y <= by + 1; y++) for (x = bx - 1; x <= bx + 1; x++)
9006 int xx = x - bx + 1;
9007 int yy = y - by + 1;
9009 if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
9010 CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9014 game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
9017 void CheckExit(int x, int y)
9019 if (local_player->gems_still_needed > 0 ||
9020 local_player->sokobanfields_still_needed > 0 ||
9021 local_player->lights_still_needed > 0)
9023 int element = Feld[x][y];
9024 int graphic = el2img(element);
9026 if (IS_ANIMATED(graphic))
9027 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9032 if (AllPlayersGone) /* do not re-open exit door closed after last player */
9035 Feld[x][y] = EL_EXIT_OPENING;
9037 PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
9040 void CheckExitEM(int x, int y)
9042 if (local_player->gems_still_needed > 0 ||
9043 local_player->sokobanfields_still_needed > 0 ||
9044 local_player->lights_still_needed > 0)
9046 int element = Feld[x][y];
9047 int graphic = el2img(element);
9049 if (IS_ANIMATED(graphic))
9050 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9055 if (AllPlayersGone) /* do not re-open exit door closed after last player */
9058 Feld[x][y] = EL_EM_EXIT_OPENING;
9060 PlayLevelSoundNearest(x, y, SND_CLASS_EM_EXIT_OPENING);
9063 void CheckExitSteel(int x, int y)
9065 if (local_player->gems_still_needed > 0 ||
9066 local_player->sokobanfields_still_needed > 0 ||
9067 local_player->lights_still_needed > 0)
9069 int element = Feld[x][y];
9070 int graphic = el2img(element);
9072 if (IS_ANIMATED(graphic))
9073 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9078 if (AllPlayersGone) /* do not re-open exit door closed after last player */
9081 Feld[x][y] = EL_STEEL_EXIT_OPENING;
9083 PlayLevelSoundNearest(x, y, SND_CLASS_STEEL_EXIT_OPENING);
9086 void CheckExitSteelEM(int x, int y)
9088 if (local_player->gems_still_needed > 0 ||
9089 local_player->sokobanfields_still_needed > 0 ||
9090 local_player->lights_still_needed > 0)
9092 int element = Feld[x][y];
9093 int graphic = el2img(element);
9095 if (IS_ANIMATED(graphic))
9096 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9101 if (AllPlayersGone) /* do not re-open exit door closed after last player */
9104 Feld[x][y] = EL_EM_STEEL_EXIT_OPENING;
9106 PlayLevelSoundNearest(x, y, SND_CLASS_EM_STEEL_EXIT_OPENING);
9109 void CheckExitSP(int x, int y)
9111 if (local_player->gems_still_needed > 0)
9113 int element = Feld[x][y];
9114 int graphic = el2img(element);
9116 if (IS_ANIMATED(graphic))
9117 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9122 if (AllPlayersGone) /* do not re-open exit door closed after last player */
9125 Feld[x][y] = EL_SP_EXIT_OPENING;
9127 PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
9130 static void CloseAllOpenTimegates()
9134 SCAN_PLAYFIELD(x, y)
9136 int element = Feld[x][y];
9138 if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
9140 Feld[x][y] = EL_TIMEGATE_CLOSING;
9142 PlayLevelSoundAction(x, y, ACTION_CLOSING);
9147 void DrawTwinkleOnField(int x, int y)
9149 if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
9152 if (Feld[x][y] == EL_BD_DIAMOND)
9155 if (MovDelay[x][y] == 0) /* next animation frame */
9156 MovDelay[x][y] = 11 * !GetSimpleRandom(500);
9158 if (MovDelay[x][y] != 0) /* wait some time before next frame */
9162 DrawLevelElementAnimation(x, y, Feld[x][y]);
9164 if (MovDelay[x][y] != 0)
9166 int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
9167 10 - MovDelay[x][y]);
9169 DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
9174 void MauerWaechst(int x, int y)
9178 if (!MovDelay[x][y]) /* next animation frame */
9179 MovDelay[x][y] = 3 * delay;
9181 if (MovDelay[x][y]) /* wait some time before next frame */
9185 if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9187 int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
9188 int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
9190 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
9193 if (!MovDelay[x][y])
9195 if (MovDir[x][y] == MV_LEFT)
9197 if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
9198 TEST_DrawLevelField(x - 1, y);
9200 else if (MovDir[x][y] == MV_RIGHT)
9202 if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
9203 TEST_DrawLevelField(x + 1, y);
9205 else if (MovDir[x][y] == MV_UP)
9207 if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
9208 TEST_DrawLevelField(x, y - 1);
9212 if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
9213 TEST_DrawLevelField(x, y + 1);
9216 Feld[x][y] = Store[x][y];
9218 GfxDir[x][y] = MovDir[x][y] = MV_NONE;
9219 TEST_DrawLevelField(x, y);
9224 void MauerAbleger(int ax, int ay)
9226 int element = Feld[ax][ay];
9227 int graphic = el2img(element);
9228 boolean oben_frei = FALSE, unten_frei = FALSE;
9229 boolean links_frei = FALSE, rechts_frei = FALSE;
9230 boolean oben_massiv = FALSE, unten_massiv = FALSE;
9231 boolean links_massiv = FALSE, rechts_massiv = FALSE;
9232 boolean new_wall = FALSE;
9234 if (IS_ANIMATED(graphic))
9235 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9237 if (!MovDelay[ax][ay]) /* start building new wall */
9238 MovDelay[ax][ay] = 6;
9240 if (MovDelay[ax][ay]) /* wait some time before building new wall */
9243 if (MovDelay[ax][ay])
9247 if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9249 if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9251 if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9253 if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9256 if (element == EL_EXPANDABLE_WALL_VERTICAL ||
9257 element == EL_EXPANDABLE_WALL_ANY)
9261 Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
9262 Store[ax][ay-1] = element;
9263 GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9264 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9265 DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9266 IMG_EXPANDABLE_WALL_GROWING_UP, 0);
9271 Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
9272 Store[ax][ay+1] = element;
9273 GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9274 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9275 DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9276 IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
9281 if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9282 element == EL_EXPANDABLE_WALL_ANY ||
9283 element == EL_EXPANDABLE_WALL ||
9284 element == EL_BD_EXPANDABLE_WALL)
9288 Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
9289 Store[ax-1][ay] = element;
9290 GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9291 if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9292 DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9293 IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
9299 Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
9300 Store[ax+1][ay] = element;
9301 GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9302 if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9303 DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9304 IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
9309 if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
9310 TEST_DrawLevelField(ax, ay);
9312 if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
9314 if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
9315 unten_massiv = TRUE;
9316 if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
9317 links_massiv = TRUE;
9318 if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
9319 rechts_massiv = TRUE;
9321 if (((oben_massiv && unten_massiv) ||
9322 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9323 element == EL_EXPANDABLE_WALL) &&
9324 ((links_massiv && rechts_massiv) ||
9325 element == EL_EXPANDABLE_WALL_VERTICAL))
9326 Feld[ax][ay] = EL_WALL;
9329 PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9332 void MauerAblegerStahl(int ax, int ay)
9334 int element = Feld[ax][ay];
9335 int graphic = el2img(element);
9336 boolean oben_frei = FALSE, unten_frei = FALSE;
9337 boolean links_frei = FALSE, rechts_frei = FALSE;
9338 boolean oben_massiv = FALSE, unten_massiv = FALSE;
9339 boolean links_massiv = FALSE, rechts_massiv = FALSE;
9340 boolean new_wall = FALSE;
9342 if (IS_ANIMATED(graphic))
9343 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9345 if (!MovDelay[ax][ay]) /* start building new wall */
9346 MovDelay[ax][ay] = 6;
9348 if (MovDelay[ax][ay]) /* wait some time before building new wall */
9351 if (MovDelay[ax][ay])
9355 if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9357 if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9359 if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9361 if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9364 if (element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
9365 element == EL_EXPANDABLE_STEELWALL_ANY)
9369 Feld[ax][ay-1] = EL_EXPANDABLE_STEELWALL_GROWING;
9370 Store[ax][ay-1] = element;
9371 GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9372 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9373 DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9374 IMG_EXPANDABLE_STEELWALL_GROWING_UP, 0);
9379 Feld[ax][ay+1] = EL_EXPANDABLE_STEELWALL_GROWING;
9380 Store[ax][ay+1] = element;
9381 GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9382 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9383 DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9384 IMG_EXPANDABLE_STEELWALL_GROWING_DOWN, 0);
9389 if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
9390 element == EL_EXPANDABLE_STEELWALL_ANY)
9394 Feld[ax-1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9395 Store[ax-1][ay] = element;
9396 GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9397 if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9398 DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9399 IMG_EXPANDABLE_STEELWALL_GROWING_LEFT, 0);
9405 Feld[ax+1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9406 Store[ax+1][ay] = element;
9407 GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9408 if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9409 DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9410 IMG_EXPANDABLE_STEELWALL_GROWING_RIGHT, 0);
9415 if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
9417 if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
9418 unten_massiv = TRUE;
9419 if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
9420 links_massiv = TRUE;
9421 if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
9422 rechts_massiv = TRUE;
9424 if (((oben_massiv && unten_massiv) ||
9425 element == EL_EXPANDABLE_STEELWALL_HORIZONTAL) &&
9426 ((links_massiv && rechts_massiv) ||
9427 element == EL_EXPANDABLE_STEELWALL_VERTICAL))
9428 Feld[ax][ay] = EL_STEELWALL;
9431 PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9434 void CheckForDragon(int x, int y)
9437 boolean dragon_found = FALSE;
9438 static int xy[4][2] =
9446 for (i = 0; i < NUM_DIRECTIONS; i++)
9448 for (j = 0; j < 4; j++)
9450 int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9452 if (IN_LEV_FIELD(xx, yy) &&
9453 (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
9455 if (Feld[xx][yy] == EL_DRAGON)
9456 dragon_found = TRUE;
9465 for (i = 0; i < NUM_DIRECTIONS; i++)
9467 for (j = 0; j < 3; j++)
9469 int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9471 if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
9473 Feld[xx][yy] = EL_EMPTY;
9474 TEST_DrawLevelField(xx, yy);
9483 static void InitBuggyBase(int x, int y)
9485 int element = Feld[x][y];
9486 int activating_delay = FRAMES_PER_SECOND / 4;
9489 (element == EL_SP_BUGGY_BASE ?
9490 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
9491 element == EL_SP_BUGGY_BASE_ACTIVATING ?
9493 element == EL_SP_BUGGY_BASE_ACTIVE ?
9494 1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
9497 static void WarnBuggyBase(int x, int y)
9500 static int xy[4][2] =
9508 for (i = 0; i < NUM_DIRECTIONS; i++)
9510 int xx = x + xy[i][0];
9511 int yy = y + xy[i][1];
9513 if (IN_LEV_FIELD(xx, yy) && IS_PLAYER(xx, yy))
9515 PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
9522 static void InitTrap(int x, int y)
9524 ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
9527 static void ActivateTrap(int x, int y)
9529 PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
9532 static void ChangeActiveTrap(int x, int y)
9534 int graphic = IMG_TRAP_ACTIVE;
9536 /* if new animation frame was drawn, correct crumbled sand border */
9537 if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
9538 TEST_DrawLevelFieldCrumbled(x, y);
9541 static int getSpecialActionElement(int element, int number, int base_element)
9543 return (element != EL_EMPTY ? element :
9544 number != -1 ? base_element + number - 1 :
9548 static int getModifiedActionNumber(int value_old, int operator, int operand,
9549 int value_min, int value_max)
9551 int value_new = (operator == CA_MODE_SET ? operand :
9552 operator == CA_MODE_ADD ? value_old + operand :
9553 operator == CA_MODE_SUBTRACT ? value_old - operand :
9554 operator == CA_MODE_MULTIPLY ? value_old * operand :
9555 operator == CA_MODE_DIVIDE ? value_old / MAX(1, operand) :
9556 operator == CA_MODE_MODULO ? value_old % MAX(1, operand) :
9559 return (value_new < value_min ? value_min :
9560 value_new > value_max ? value_max :
9564 static void ExecuteCustomElementAction(int x, int y, int element, int page)
9566 struct ElementInfo *ei = &element_info[element];
9567 struct ElementChangeInfo *change = &ei->change_page[page];
9568 int target_element = change->target_element;
9569 int action_type = change->action_type;
9570 int action_mode = change->action_mode;
9571 int action_arg = change->action_arg;
9572 int action_element = change->action_element;
9575 if (!change->has_action)
9578 /* ---------- determine action paramater values -------------------------- */
9580 int level_time_value =
9581 (level.time > 0 ? TimeLeft :
9584 int action_arg_element_raw =
9585 (action_arg == CA_ARG_PLAYER_TRIGGER ? change->actual_trigger_player :
9586 action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
9587 action_arg == CA_ARG_ELEMENT_TARGET ? change->target_element :
9588 action_arg == CA_ARG_ELEMENT_ACTION ? change->action_element :
9589 action_arg == CA_ARG_INVENTORY_RM_TRIGGER ? change->actual_trigger_element:
9590 action_arg == CA_ARG_INVENTORY_RM_TARGET ? change->target_element :
9591 action_arg == CA_ARG_INVENTORY_RM_ACTION ? change->action_element :
9593 int action_arg_element = GetElementFromGroupElement(action_arg_element_raw);
9595 int action_arg_direction =
9596 (action_arg >= CA_ARG_DIRECTION_LEFT &&
9597 action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
9598 action_arg == CA_ARG_DIRECTION_TRIGGER ?
9599 change->actual_trigger_side :
9600 action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
9601 MV_DIR_OPPOSITE(change->actual_trigger_side) :
9604 int action_arg_number_min =
9605 (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
9608 int action_arg_number_max =
9609 (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
9610 action_type == CA_SET_LEVEL_GEMS ? 999 :
9611 action_type == CA_SET_LEVEL_TIME ? 9999 :
9612 action_type == CA_SET_LEVEL_SCORE ? 99999 :
9613 action_type == CA_SET_CE_VALUE ? 9999 :
9614 action_type == CA_SET_CE_SCORE ? 9999 :
9617 int action_arg_number_reset =
9618 (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize[0] :
9619 action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
9620 action_type == CA_SET_LEVEL_TIME ? level.time :
9621 action_type == CA_SET_LEVEL_SCORE ? 0 :
9622 action_type == CA_SET_CE_VALUE ? GET_NEW_CE_VALUE(element) :
9623 action_type == CA_SET_CE_SCORE ? 0 :
9626 int action_arg_number =
9627 (action_arg <= CA_ARG_MAX ? action_arg :
9628 action_arg >= CA_ARG_SPEED_NOT_MOVING &&
9629 action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
9630 action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
9631 action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
9632 action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
9633 action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
9634 action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
9635 action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
9636 action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
9637 action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
9638 action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? local_player->gems_still_needed :
9639 action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? local_player->score :
9640 action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CE_VALUE(target_element):
9641 action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
9642 action_arg == CA_ARG_ELEMENT_CV_ACTION ? GET_NEW_CE_VALUE(action_element):
9643 action_arg == CA_ARG_ELEMENT_CS_TARGET ? GET_CE_SCORE(target_element) :
9644 action_arg == CA_ARG_ELEMENT_CS_TRIGGER ? change->actual_trigger_ce_score:
9645 action_arg == CA_ARG_ELEMENT_CS_ACTION ? GET_CE_SCORE(action_element) :
9646 action_arg == CA_ARG_ELEMENT_NR_TARGET ? change->target_element :
9647 action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
9648 action_arg == CA_ARG_ELEMENT_NR_ACTION ? change->action_element :
9651 int action_arg_number_old =
9652 (action_type == CA_SET_LEVEL_GEMS ? local_player->gems_still_needed :
9653 action_type == CA_SET_LEVEL_TIME ? TimeLeft :
9654 action_type == CA_SET_LEVEL_SCORE ? local_player->score :
9655 action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
9656 action_type == CA_SET_CE_SCORE ? ei->collect_score :
9659 int action_arg_number_new =
9660 getModifiedActionNumber(action_arg_number_old,
9661 action_mode, action_arg_number,
9662 action_arg_number_min, action_arg_number_max);
9664 int trigger_player_bits =
9665 (change->actual_trigger_player_bits != CH_PLAYER_NONE ?
9666 change->actual_trigger_player_bits : change->trigger_player);
9668 int action_arg_player_bits =
9669 (action_arg >= CA_ARG_PLAYER_1 &&
9670 action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
9671 action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
9672 action_arg == CA_ARG_PLAYER_ACTION ? 1 << GET_PLAYER_NR(action_element) :
9675 /* ---------- execute action -------------------------------------------- */
9677 switch (action_type)
9684 /* ---------- level actions ------------------------------------------- */
9686 case CA_RESTART_LEVEL:
9688 game.restart_level = TRUE;
9693 case CA_SHOW_ENVELOPE:
9695 int element = getSpecialActionElement(action_arg_element,
9696 action_arg_number, EL_ENVELOPE_1);
9698 if (IS_ENVELOPE(element))
9699 local_player->show_envelope = element;
9704 case CA_SET_LEVEL_TIME:
9706 if (level.time > 0) /* only modify limited time value */
9708 TimeLeft = action_arg_number_new;
9710 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
9712 DisplayGameControlValues();
9714 if (!TimeLeft && setup.time_limit)
9715 for (i = 0; i < MAX_PLAYERS; i++)
9716 KillPlayer(&stored_player[i]);
9722 case CA_SET_LEVEL_SCORE:
9724 local_player->score = action_arg_number_new;
9726 game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
9728 DisplayGameControlValues();
9733 case CA_SET_LEVEL_GEMS:
9735 local_player->gems_still_needed = action_arg_number_new;
9737 game.snapshot.collected_item = TRUE;
9739 game_panel_controls[GAME_PANEL_GEMS].value =
9740 local_player->gems_still_needed;
9742 DisplayGameControlValues();
9747 case CA_SET_LEVEL_WIND:
9749 game.wind_direction = action_arg_direction;
9754 case CA_SET_LEVEL_RANDOM_SEED:
9756 /* ensure that setting a new random seed while playing is predictable */
9757 InitRND(action_arg_number_new ? action_arg_number_new : RND(1000000) + 1);
9762 /* ---------- player actions ------------------------------------------ */
9764 case CA_MOVE_PLAYER:
9766 /* automatically move to the next field in specified direction */
9767 for (i = 0; i < MAX_PLAYERS; i++)
9768 if (trigger_player_bits & (1 << i))
9769 stored_player[i].programmed_action = action_arg_direction;
9774 case CA_EXIT_PLAYER:
9776 for (i = 0; i < MAX_PLAYERS; i++)
9777 if (action_arg_player_bits & (1 << i))
9778 PlayerWins(&stored_player[i]);
9783 case CA_KILL_PLAYER:
9785 for (i = 0; i < MAX_PLAYERS; i++)
9786 if (action_arg_player_bits & (1 << i))
9787 KillPlayer(&stored_player[i]);
9792 case CA_SET_PLAYER_KEYS:
9794 int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
9795 int element = getSpecialActionElement(action_arg_element,
9796 action_arg_number, EL_KEY_1);
9798 if (IS_KEY(element))
9800 for (i = 0; i < MAX_PLAYERS; i++)
9802 if (trigger_player_bits & (1 << i))
9804 stored_player[i].key[KEY_NR(element)] = key_state;
9806 DrawGameDoorValues();
9814 case CA_SET_PLAYER_SPEED:
9816 for (i = 0; i < MAX_PLAYERS; i++)
9818 if (trigger_player_bits & (1 << i))
9820 int move_stepsize = TILEX / stored_player[i].move_delay_value;
9822 if (action_arg == CA_ARG_SPEED_FASTER &&
9823 stored_player[i].cannot_move)
9825 action_arg_number = STEPSIZE_VERY_SLOW;
9827 else if (action_arg == CA_ARG_SPEED_SLOWER ||
9828 action_arg == CA_ARG_SPEED_FASTER)
9830 action_arg_number = 2;
9831 action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
9834 else if (action_arg == CA_ARG_NUMBER_RESET)
9836 action_arg_number = level.initial_player_stepsize[i];
9840 getModifiedActionNumber(move_stepsize,
9843 action_arg_number_min,
9844 action_arg_number_max);
9846 SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
9853 case CA_SET_PLAYER_SHIELD:
9855 for (i = 0; i < MAX_PLAYERS; i++)
9857 if (trigger_player_bits & (1 << i))
9859 if (action_arg == CA_ARG_SHIELD_OFF)
9861 stored_player[i].shield_normal_time_left = 0;
9862 stored_player[i].shield_deadly_time_left = 0;
9864 else if (action_arg == CA_ARG_SHIELD_NORMAL)
9866 stored_player[i].shield_normal_time_left = 999999;
9868 else if (action_arg == CA_ARG_SHIELD_DEADLY)
9870 stored_player[i].shield_normal_time_left = 999999;
9871 stored_player[i].shield_deadly_time_left = 999999;
9879 case CA_SET_PLAYER_GRAVITY:
9881 for (i = 0; i < MAX_PLAYERS; i++)
9883 if (trigger_player_bits & (1 << i))
9885 stored_player[i].gravity =
9886 (action_arg == CA_ARG_GRAVITY_OFF ? FALSE :
9887 action_arg == CA_ARG_GRAVITY_ON ? TRUE :
9888 action_arg == CA_ARG_GRAVITY_TOGGLE ? !stored_player[i].gravity :
9889 stored_player[i].gravity);
9896 case CA_SET_PLAYER_ARTWORK:
9898 for (i = 0; i < MAX_PLAYERS; i++)
9900 if (trigger_player_bits & (1 << i))
9902 int artwork_element = action_arg_element;
9904 if (action_arg == CA_ARG_ELEMENT_RESET)
9906 (level.use_artwork_element[i] ? level.artwork_element[i] :
9907 stored_player[i].element_nr);
9909 if (stored_player[i].artwork_element != artwork_element)
9910 stored_player[i].Frame = 0;
9912 stored_player[i].artwork_element = artwork_element;
9914 SetPlayerWaiting(&stored_player[i], FALSE);
9916 /* set number of special actions for bored and sleeping animation */
9917 stored_player[i].num_special_action_bored =
9918 get_num_special_action(artwork_element,
9919 ACTION_BORING_1, ACTION_BORING_LAST);
9920 stored_player[i].num_special_action_sleeping =
9921 get_num_special_action(artwork_element,
9922 ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
9929 case CA_SET_PLAYER_INVENTORY:
9931 for (i = 0; i < MAX_PLAYERS; i++)
9933 struct PlayerInfo *player = &stored_player[i];
9936 if (trigger_player_bits & (1 << i))
9938 int inventory_element = action_arg_element;
9940 if (action_arg == CA_ARG_ELEMENT_TARGET ||
9941 action_arg == CA_ARG_ELEMENT_TRIGGER ||
9942 action_arg == CA_ARG_ELEMENT_ACTION)
9944 int element = inventory_element;
9945 int collect_count = element_info[element].collect_count_initial;
9947 if (!IS_CUSTOM_ELEMENT(element))
9950 if (collect_count == 0)
9951 player->inventory_infinite_element = element;
9953 for (k = 0; k < collect_count; k++)
9954 if (player->inventory_size < MAX_INVENTORY_SIZE)
9955 player->inventory_element[player->inventory_size++] =
9958 else if (action_arg == CA_ARG_INVENTORY_RM_TARGET ||
9959 action_arg == CA_ARG_INVENTORY_RM_TRIGGER ||
9960 action_arg == CA_ARG_INVENTORY_RM_ACTION)
9962 if (player->inventory_infinite_element != EL_UNDEFINED &&
9963 IS_EQUAL_OR_IN_GROUP(player->inventory_infinite_element,
9964 action_arg_element_raw))
9965 player->inventory_infinite_element = EL_UNDEFINED;
9967 for (k = 0, j = 0; j < player->inventory_size; j++)
9969 if (!IS_EQUAL_OR_IN_GROUP(player->inventory_element[j],
9970 action_arg_element_raw))
9971 player->inventory_element[k++] = player->inventory_element[j];
9974 player->inventory_size = k;
9976 else if (action_arg == CA_ARG_INVENTORY_RM_FIRST)
9978 if (player->inventory_size > 0)
9980 for (j = 0; j < player->inventory_size - 1; j++)
9981 player->inventory_element[j] = player->inventory_element[j + 1];
9983 player->inventory_size--;
9986 else if (action_arg == CA_ARG_INVENTORY_RM_LAST)
9988 if (player->inventory_size > 0)
9989 player->inventory_size--;
9991 else if (action_arg == CA_ARG_INVENTORY_RM_ALL)
9993 player->inventory_infinite_element = EL_UNDEFINED;
9994 player->inventory_size = 0;
9996 else if (action_arg == CA_ARG_INVENTORY_RESET)
9998 player->inventory_infinite_element = EL_UNDEFINED;
9999 player->inventory_size = 0;
10001 if (level.use_initial_inventory[i])
10003 for (j = 0; j < level.initial_inventory_size[i]; j++)
10005 int element = level.initial_inventory_content[i][j];
10006 int collect_count = element_info[element].collect_count_initial;
10008 if (!IS_CUSTOM_ELEMENT(element))
10011 if (collect_count == 0)
10012 player->inventory_infinite_element = element;
10014 for (k = 0; k < collect_count; k++)
10015 if (player->inventory_size < MAX_INVENTORY_SIZE)
10016 player->inventory_element[player->inventory_size++] =
10027 /* ---------- CE actions ---------------------------------------------- */
10029 case CA_SET_CE_VALUE:
10031 int last_ce_value = CustomValue[x][y];
10033 CustomValue[x][y] = action_arg_number_new;
10035 if (CustomValue[x][y] != last_ce_value)
10037 CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_CHANGES);
10038 CheckTriggeredElementChange(x, y, element, CE_VALUE_CHANGES_OF_X);
10040 if (CustomValue[x][y] == 0)
10042 CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
10043 CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
10050 case CA_SET_CE_SCORE:
10052 int last_ce_score = ei->collect_score;
10054 ei->collect_score = action_arg_number_new;
10056 if (ei->collect_score != last_ce_score)
10058 CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_CHANGES);
10059 CheckTriggeredElementChange(x, y, element, CE_SCORE_CHANGES_OF_X);
10061 if (ei->collect_score == 0)
10065 CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_GETS_ZERO);
10066 CheckTriggeredElementChange(x, y, element, CE_SCORE_GETS_ZERO_OF_X);
10069 This is a very special case that seems to be a mixture between
10070 CheckElementChange() and CheckTriggeredElementChange(): while
10071 the first one only affects single elements that are triggered
10072 directly, the second one affects multiple elements in the playfield
10073 that are triggered indirectly by another element. This is a third
10074 case: Changing the CE score always affects multiple identical CEs,
10075 so every affected CE must be checked, not only the single CE for
10076 which the CE score was changed in the first place (as every instance
10077 of that CE shares the same CE score, and therefore also can change)!
10079 SCAN_PLAYFIELD(xx, yy)
10081 if (Feld[xx][yy] == element)
10082 CheckElementChange(xx, yy, element, EL_UNDEFINED,
10083 CE_SCORE_GETS_ZERO);
10091 case CA_SET_CE_ARTWORK:
10093 int artwork_element = action_arg_element;
10094 boolean reset_frame = FALSE;
10097 if (action_arg == CA_ARG_ELEMENT_RESET)
10098 artwork_element = (ei->use_gfx_element ? ei->gfx_element_initial :
10101 if (ei->gfx_element != artwork_element)
10102 reset_frame = TRUE;
10104 ei->gfx_element = artwork_element;
10106 SCAN_PLAYFIELD(xx, yy)
10108 if (Feld[xx][yy] == element)
10112 ResetGfxAnimation(xx, yy);
10113 ResetRandomAnimationValue(xx, yy);
10116 TEST_DrawLevelField(xx, yy);
10123 /* ---------- engine actions ------------------------------------------ */
10125 case CA_SET_ENGINE_SCAN_MODE:
10127 InitPlayfieldScanMode(action_arg);
10137 static void CreateFieldExt(int x, int y, int element, boolean is_change)
10139 int old_element = Feld[x][y];
10140 int new_element = GetElementFromGroupElement(element);
10141 int previous_move_direction = MovDir[x][y];
10142 int last_ce_value = CustomValue[x][y];
10143 boolean player_explosion_protected = PLAYER_EXPLOSION_PROTECTED(x, y);
10144 boolean new_element_is_player = ELEM_IS_PLAYER(new_element);
10145 boolean add_player_onto_element = (new_element_is_player &&
10146 new_element != EL_SOKOBAN_FIELD_PLAYER &&
10147 IS_WALKABLE(old_element));
10149 if (!add_player_onto_element)
10151 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
10152 RemoveMovingField(x, y);
10156 Feld[x][y] = new_element;
10158 if (element_info[new_element].move_direction_initial == MV_START_PREVIOUS)
10159 MovDir[x][y] = previous_move_direction;
10161 if (element_info[new_element].use_last_ce_value)
10162 CustomValue[x][y] = last_ce_value;
10164 InitField_WithBug1(x, y, FALSE);
10166 new_element = Feld[x][y]; /* element may have changed */
10168 ResetGfxAnimation(x, y);
10169 ResetRandomAnimationValue(x, y);
10171 TEST_DrawLevelField(x, y);
10173 if (GFX_CRUMBLED(new_element))
10174 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
10177 /* check if element under the player changes from accessible to unaccessible
10178 (needed for special case of dropping element which then changes) */
10179 /* (must be checked after creating new element for walkable group elements) */
10180 if (IS_PLAYER(x, y) && !player_explosion_protected &&
10181 IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
10188 /* "ChangeCount" not set yet to allow "entered by player" change one time */
10189 if (new_element_is_player)
10190 RelocatePlayer(x, y, new_element);
10193 ChangeCount[x][y]++; /* count number of changes in the same frame */
10195 TestIfBadThingTouchesPlayer(x, y);
10196 TestIfPlayerTouchesCustomElement(x, y);
10197 TestIfElementTouchesCustomElement(x, y);
10200 static void CreateField(int x, int y, int element)
10202 CreateFieldExt(x, y, element, FALSE);
10205 static void CreateElementFromChange(int x, int y, int element)
10207 element = GET_VALID_RUNTIME_ELEMENT(element);
10209 if (game.engine_version >= VERSION_IDENT(3,2,0,7))
10211 int old_element = Feld[x][y];
10213 /* prevent changed element from moving in same engine frame
10214 unless both old and new element can either fall or move */
10215 if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
10216 (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
10220 CreateFieldExt(x, y, element, TRUE);
10223 static boolean ChangeElement(int x, int y, int element, int page)
10225 struct ElementInfo *ei = &element_info[element];
10226 struct ElementChangeInfo *change = &ei->change_page[page];
10227 int ce_value = CustomValue[x][y];
10228 int ce_score = ei->collect_score;
10229 int target_element;
10230 int old_element = Feld[x][y];
10232 /* always use default change event to prevent running into a loop */
10233 if (ChangeEvent[x][y] == -1)
10234 ChangeEvent[x][y] = CE_DELAY;
10236 if (ChangeEvent[x][y] == CE_DELAY)
10238 /* reset actual trigger element, trigger player and action element */
10239 change->actual_trigger_element = EL_EMPTY;
10240 change->actual_trigger_player = EL_EMPTY;
10241 change->actual_trigger_player_bits = CH_PLAYER_NONE;
10242 change->actual_trigger_side = CH_SIDE_NONE;
10243 change->actual_trigger_ce_value = 0;
10244 change->actual_trigger_ce_score = 0;
10247 /* do not change elements more than a specified maximum number of changes */
10248 if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
10251 ChangeCount[x][y]++; /* count number of changes in the same frame */
10253 if (change->explode)
10260 if (change->use_target_content)
10262 boolean complete_replace = TRUE;
10263 boolean can_replace[3][3];
10266 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10269 boolean is_walkable;
10270 boolean is_diggable;
10271 boolean is_collectible;
10272 boolean is_removable;
10273 boolean is_destructible;
10274 int ex = x + xx - 1;
10275 int ey = y + yy - 1;
10276 int content_element = change->target_content.e[xx][yy];
10279 can_replace[xx][yy] = TRUE;
10281 if (ex == x && ey == y) /* do not check changing element itself */
10284 if (content_element == EL_EMPTY_SPACE)
10286 can_replace[xx][yy] = FALSE; /* do not replace border with space */
10291 if (!IN_LEV_FIELD(ex, ey))
10293 can_replace[xx][yy] = FALSE;
10294 complete_replace = FALSE;
10301 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10302 e = MovingOrBlocked2Element(ex, ey);
10304 is_empty = (IS_FREE(ex, ey) ||
10305 (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
10307 is_walkable = (is_empty || IS_WALKABLE(e));
10308 is_diggable = (is_empty || IS_DIGGABLE(e));
10309 is_collectible = (is_empty || IS_COLLECTIBLE(e));
10310 is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
10311 is_removable = (is_diggable || is_collectible);
10313 can_replace[xx][yy] =
10314 (((change->replace_when == CP_WHEN_EMPTY && is_empty) ||
10315 (change->replace_when == CP_WHEN_WALKABLE && is_walkable) ||
10316 (change->replace_when == CP_WHEN_DIGGABLE && is_diggable) ||
10317 (change->replace_when == CP_WHEN_COLLECTIBLE && is_collectible) ||
10318 (change->replace_when == CP_WHEN_REMOVABLE && is_removable) ||
10319 (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
10320 !(IS_PLAYER(ex, ey) && ELEM_IS_PLAYER(content_element)));
10322 if (!can_replace[xx][yy])
10323 complete_replace = FALSE;
10326 if (!change->only_if_complete || complete_replace)
10328 boolean something_has_changed = FALSE;
10330 if (change->only_if_complete && change->use_random_replace &&
10331 RND(100) < change->random_percentage)
10334 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10336 int ex = x + xx - 1;
10337 int ey = y + yy - 1;
10338 int content_element;
10340 if (can_replace[xx][yy] && (!change->use_random_replace ||
10341 RND(100) < change->random_percentage))
10343 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10344 RemoveMovingField(ex, ey);
10346 ChangeEvent[ex][ey] = ChangeEvent[x][y];
10348 content_element = change->target_content.e[xx][yy];
10349 target_element = GET_TARGET_ELEMENT(element, content_element, change,
10350 ce_value, ce_score);
10352 CreateElementFromChange(ex, ey, target_element);
10354 something_has_changed = TRUE;
10356 /* for symmetry reasons, freeze newly created border elements */
10357 if (ex != x || ey != y)
10358 Stop[ex][ey] = TRUE; /* no more moving in this frame */
10362 if (something_has_changed)
10364 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10365 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10371 target_element = GET_TARGET_ELEMENT(element, change->target_element, change,
10372 ce_value, ce_score);
10374 if (element == EL_DIAGONAL_GROWING ||
10375 element == EL_DIAGONAL_SHRINKING)
10377 target_element = Store[x][y];
10379 Store[x][y] = EL_EMPTY;
10382 CreateElementFromChange(x, y, target_element);
10384 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10385 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10388 /* this uses direct change before indirect change */
10389 CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
10394 static void HandleElementChange(int x, int y, int page)
10396 int element = MovingOrBlocked2Element(x, y);
10397 struct ElementInfo *ei = &element_info[element];
10398 struct ElementChangeInfo *change = &ei->change_page[page];
10399 boolean handle_action_before_change = FALSE;
10402 if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
10403 !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
10406 printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
10407 x, y, element, element_info[element].token_name);
10408 printf("HandleElementChange(): This should never happen!\n");
10413 /* this can happen with classic bombs on walkable, changing elements */
10414 if (!CAN_CHANGE_OR_HAS_ACTION(element))
10419 if (ChangeDelay[x][y] == 0) /* initialize element change */
10421 ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
10423 if (change->can_change)
10425 /* !!! not clear why graphic animation should be reset at all here !!! */
10426 /* !!! UPDATE: but is needed for correct Snake Bite tail animation !!! */
10427 /* !!! SOLUTION: do not reset if graphics engine set to 4 or above !!! */
10430 GRAPHICAL BUG ADDRESSED BY CHECKING GRAPHICS ENGINE VERSION:
10432 When using an animation frame delay of 1 (this only happens with
10433 "sp_zonk.moving.left/right" in the classic graphics), the default
10434 (non-moving) animation shows wrong animation frames (while the
10435 moving animation, like "sp_zonk.moving.left/right", is correct,
10436 so this graphical bug never shows up with the classic graphics).
10437 For an animation with 4 frames, this causes wrong frames 0,0,1,2
10438 be drawn instead of the correct frames 0,1,2,3. This is caused by
10439 "GfxFrame[][]" being reset *twice* (in two successive frames) after
10440 an element change: First when the change delay ("ChangeDelay[][]")
10441 counter has reached zero after decrementing, then a second time in
10442 the next frame (after "GfxFrame[][]" was already incremented) when
10443 "ChangeDelay[][]" is reset to the initial delay value again.
10445 This causes frame 0 to be drawn twice, while the last frame won't
10446 be drawn anymore, resulting in the wrong frame sequence 0,0,1,2.
10448 As some animations may already be cleverly designed around this bug
10449 (at least the "Snake Bite" snake tail animation does this), it cannot
10450 simply be fixed here without breaking such existing animations.
10451 Unfortunately, it cannot easily be detected if a graphics set was
10452 designed "before" or "after" the bug was fixed. As a workaround,
10453 a new graphics set option "game.graphics_engine_version" was added
10454 to be able to specify the game's major release version for which the
10455 graphics set was designed, which can then be used to decide if the
10456 bugfix should be used (version 4 and above) or not (version 3 or
10457 below, or if no version was specified at all, as with old sets).
10459 (The wrong/fixed animation frames can be tested with the test level set
10460 "test_gfxframe" and level "000", which contains a specially prepared
10461 custom element at level position (x/y) == (11/9) which uses the zonk
10462 animation mentioned above. Using "game.graphics_engine_version: 4"
10463 fixes the wrong animation frames, showing the correct frames 0,1,2,3.
10464 This can also be seen from the debug output for this test element.)
10467 /* when a custom element is about to change (for example by change delay),
10468 do not reset graphic animation when the custom element is moving */
10469 if (game.graphics_engine_version < 4 &&
10472 ResetGfxAnimation(x, y);
10473 ResetRandomAnimationValue(x, y);
10476 if (change->pre_change_function)
10477 change->pre_change_function(x, y);
10481 ChangeDelay[x][y]--;
10483 if (ChangeDelay[x][y] != 0) /* continue element change */
10485 if (change->can_change)
10487 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
10489 if (IS_ANIMATED(graphic))
10490 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10492 if (change->change_function)
10493 change->change_function(x, y);
10496 else /* finish element change */
10498 if (ChangePage[x][y] != -1) /* remember page from delayed change */
10500 page = ChangePage[x][y];
10501 ChangePage[x][y] = -1;
10503 change = &ei->change_page[page];
10506 if (IS_MOVING(x, y)) /* never change a running system ;-) */
10508 ChangeDelay[x][y] = 1; /* try change after next move step */
10509 ChangePage[x][y] = page; /* remember page to use for change */
10514 /* special case: set new level random seed before changing element */
10515 if (change->has_action && change->action_type == CA_SET_LEVEL_RANDOM_SEED)
10516 handle_action_before_change = TRUE;
10518 if (change->has_action && handle_action_before_change)
10519 ExecuteCustomElementAction(x, y, element, page);
10521 if (change->can_change)
10523 if (ChangeElement(x, y, element, page))
10525 if (change->post_change_function)
10526 change->post_change_function(x, y);
10530 if (change->has_action && !handle_action_before_change)
10531 ExecuteCustomElementAction(x, y, element, page);
10535 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
10536 int trigger_element,
10538 int trigger_player,
10542 boolean change_done_any = FALSE;
10543 int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
10546 if (!(trigger_events[trigger_element][trigger_event]))
10549 RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
10551 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
10553 int element = EL_CUSTOM_START + i;
10554 boolean change_done = FALSE;
10557 if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
10558 !HAS_ANY_CHANGE_EVENT(element, trigger_event))
10561 for (p = 0; p < element_info[element].num_change_pages; p++)
10563 struct ElementChangeInfo *change = &element_info[element].change_page[p];
10565 if (change->can_change_or_has_action &&
10566 change->has_event[trigger_event] &&
10567 change->trigger_side & trigger_side &&
10568 change->trigger_player & trigger_player &&
10569 change->trigger_page & trigger_page_bits &&
10570 IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
10572 change->actual_trigger_element = trigger_element;
10573 change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
10574 change->actual_trigger_player_bits = trigger_player;
10575 change->actual_trigger_side = trigger_side;
10576 change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
10577 change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10579 if ((change->can_change && !change_done) || change->has_action)
10583 SCAN_PLAYFIELD(x, y)
10585 if (Feld[x][y] == element)
10587 if (change->can_change && !change_done)
10589 /* if element already changed in this frame, not only prevent
10590 another element change (checked in ChangeElement()), but
10591 also prevent additional element actions for this element */
10593 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
10594 !level.use_action_after_change_bug)
10597 ChangeDelay[x][y] = 1;
10598 ChangeEvent[x][y] = trigger_event;
10600 HandleElementChange(x, y, p);
10602 else if (change->has_action)
10604 /* if element already changed in this frame, not only prevent
10605 another element change (checked in ChangeElement()), but
10606 also prevent additional element actions for this element */
10608 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
10609 !level.use_action_after_change_bug)
10612 ExecuteCustomElementAction(x, y, element, p);
10613 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
10618 if (change->can_change)
10620 change_done = TRUE;
10621 change_done_any = TRUE;
10628 RECURSION_LOOP_DETECTION_END();
10630 return change_done_any;
10633 static boolean CheckElementChangeExt(int x, int y,
10635 int trigger_element,
10637 int trigger_player,
10640 boolean change_done = FALSE;
10643 if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
10644 !HAS_ANY_CHANGE_EVENT(element, trigger_event))
10647 if (Feld[x][y] == EL_BLOCKED)
10649 Blocked2Moving(x, y, &x, &y);
10650 element = Feld[x][y];
10653 /* check if element has already changed or is about to change after moving */
10654 if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
10655 Feld[x][y] != element) ||
10657 (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
10658 (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
10659 ChangePage[x][y] != -1)))
10662 RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
10664 for (p = 0; p < element_info[element].num_change_pages; p++)
10666 struct ElementChangeInfo *change = &element_info[element].change_page[p];
10668 /* check trigger element for all events where the element that is checked
10669 for changing interacts with a directly adjacent element -- this is
10670 different to element changes that affect other elements to change on the
10671 whole playfield (which is handeld by CheckTriggeredElementChangeExt()) */
10672 boolean check_trigger_element =
10673 (trigger_event == CE_TOUCHING_X ||
10674 trigger_event == CE_HITTING_X ||
10675 trigger_event == CE_HIT_BY_X ||
10676 trigger_event == CE_DIGGING_X); /* this one was forgotten until 3.2.3 */
10678 if (change->can_change_or_has_action &&
10679 change->has_event[trigger_event] &&
10680 change->trigger_side & trigger_side &&
10681 change->trigger_player & trigger_player &&
10682 (!check_trigger_element ||
10683 IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
10685 change->actual_trigger_element = trigger_element;
10686 change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
10687 change->actual_trigger_player_bits = trigger_player;
10688 change->actual_trigger_side = trigger_side;
10689 change->actual_trigger_ce_value = CustomValue[x][y];
10690 change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10692 /* special case: trigger element not at (x,y) position for some events */
10693 if (check_trigger_element)
10705 { 0, 0 }, { 0, 0 }, { 0, 0 },
10709 int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
10710 int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
10712 change->actual_trigger_ce_value = CustomValue[xx][yy];
10713 change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10716 if (change->can_change && !change_done)
10718 ChangeDelay[x][y] = 1;
10719 ChangeEvent[x][y] = trigger_event;
10721 HandleElementChange(x, y, p);
10723 change_done = TRUE;
10725 else if (change->has_action)
10727 ExecuteCustomElementAction(x, y, element, p);
10728 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
10733 RECURSION_LOOP_DETECTION_END();
10735 return change_done;
10738 static void PlayPlayerSound(struct PlayerInfo *player)
10740 int jx = player->jx, jy = player->jy;
10741 int sound_element = player->artwork_element;
10742 int last_action = player->last_action_waiting;
10743 int action = player->action_waiting;
10745 if (player->is_waiting)
10747 if (action != last_action)
10748 PlayLevelSoundElementAction(jx, jy, sound_element, action);
10750 PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
10754 if (action != last_action)
10755 StopSound(element_info[sound_element].sound[last_action]);
10757 if (last_action == ACTION_SLEEPING)
10758 PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
10762 static void PlayAllPlayersSound()
10766 for (i = 0; i < MAX_PLAYERS; i++)
10767 if (stored_player[i].active)
10768 PlayPlayerSound(&stored_player[i]);
10771 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
10773 boolean last_waiting = player->is_waiting;
10774 int move_dir = player->MovDir;
10776 player->dir_waiting = move_dir;
10777 player->last_action_waiting = player->action_waiting;
10781 if (!last_waiting) /* not waiting -> waiting */
10783 player->is_waiting = TRUE;
10785 player->frame_counter_bored =
10787 game.player_boring_delay_fixed +
10788 GetSimpleRandom(game.player_boring_delay_random);
10789 player->frame_counter_sleeping =
10791 game.player_sleeping_delay_fixed +
10792 GetSimpleRandom(game.player_sleeping_delay_random);
10794 InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
10797 if (game.player_sleeping_delay_fixed +
10798 game.player_sleeping_delay_random > 0 &&
10799 player->anim_delay_counter == 0 &&
10800 player->post_delay_counter == 0 &&
10801 FrameCounter >= player->frame_counter_sleeping)
10802 player->is_sleeping = TRUE;
10803 else if (game.player_boring_delay_fixed +
10804 game.player_boring_delay_random > 0 &&
10805 FrameCounter >= player->frame_counter_bored)
10806 player->is_bored = TRUE;
10808 player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
10809 player->is_bored ? ACTION_BORING :
10812 if (player->is_sleeping && player->use_murphy)
10814 /* special case for sleeping Murphy when leaning against non-free tile */
10816 if (!IN_LEV_FIELD(player->jx - 1, player->jy) ||
10817 (Feld[player->jx - 1][player->jy] != EL_EMPTY &&
10818 !IS_MOVING(player->jx - 1, player->jy)))
10819 move_dir = MV_LEFT;
10820 else if (!IN_LEV_FIELD(player->jx + 1, player->jy) ||
10821 (Feld[player->jx + 1][player->jy] != EL_EMPTY &&
10822 !IS_MOVING(player->jx + 1, player->jy)))
10823 move_dir = MV_RIGHT;
10825 player->is_sleeping = FALSE;
10827 player->dir_waiting = move_dir;
10830 if (player->is_sleeping)
10832 if (player->num_special_action_sleeping > 0)
10834 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
10836 int last_special_action = player->special_action_sleeping;
10837 int num_special_action = player->num_special_action_sleeping;
10838 int special_action =
10839 (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
10840 last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
10841 last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
10842 last_special_action + 1 : ACTION_SLEEPING);
10843 int special_graphic =
10844 el_act_dir2img(player->artwork_element, special_action, move_dir);
10846 player->anim_delay_counter =
10847 graphic_info[special_graphic].anim_delay_fixed +
10848 GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
10849 player->post_delay_counter =
10850 graphic_info[special_graphic].post_delay_fixed +
10851 GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
10853 player->special_action_sleeping = special_action;
10856 if (player->anim_delay_counter > 0)
10858 player->action_waiting = player->special_action_sleeping;
10859 player->anim_delay_counter--;
10861 else if (player->post_delay_counter > 0)
10863 player->post_delay_counter--;
10867 else if (player->is_bored)
10869 if (player->num_special_action_bored > 0)
10871 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
10873 int special_action =
10874 ACTION_BORING_1 + GetSimpleRandom(player->num_special_action_bored);
10875 int special_graphic =
10876 el_act_dir2img(player->artwork_element, special_action, move_dir);
10878 player->anim_delay_counter =
10879 graphic_info[special_graphic].anim_delay_fixed +
10880 GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
10881 player->post_delay_counter =
10882 graphic_info[special_graphic].post_delay_fixed +
10883 GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
10885 player->special_action_bored = special_action;
10888 if (player->anim_delay_counter > 0)
10890 player->action_waiting = player->special_action_bored;
10891 player->anim_delay_counter--;
10893 else if (player->post_delay_counter > 0)
10895 player->post_delay_counter--;
10900 else if (last_waiting) /* waiting -> not waiting */
10902 player->is_waiting = FALSE;
10903 player->is_bored = FALSE;
10904 player->is_sleeping = FALSE;
10906 player->frame_counter_bored = -1;
10907 player->frame_counter_sleeping = -1;
10909 player->anim_delay_counter = 0;
10910 player->post_delay_counter = 0;
10912 player->dir_waiting = player->MovDir;
10913 player->action_waiting = ACTION_DEFAULT;
10915 player->special_action_bored = ACTION_DEFAULT;
10916 player->special_action_sleeping = ACTION_DEFAULT;
10920 static void CheckSaveEngineSnapshot(struct PlayerInfo *player)
10922 if ((!player->is_moving && player->was_moving) ||
10923 (player->MovPos == 0 && player->was_moving) ||
10924 (player->is_snapping && !player->was_snapping) ||
10925 (player->is_dropping && !player->was_dropping))
10927 if (!CheckSaveEngineSnapshotToList())
10930 player->was_moving = FALSE;
10931 player->was_snapping = TRUE;
10932 player->was_dropping = TRUE;
10936 if (player->is_moving)
10937 player->was_moving = TRUE;
10939 if (!player->is_snapping)
10940 player->was_snapping = FALSE;
10942 if (!player->is_dropping)
10943 player->was_dropping = FALSE;
10947 static void CheckSingleStepMode(struct PlayerInfo *player)
10949 if (tape.single_step && tape.recording && !tape.pausing)
10951 /* as it is called "single step mode", just return to pause mode when the
10952 player stopped moving after one tile (or never starts moving at all) */
10953 if (!player->is_moving &&
10954 !player->is_pushing &&
10955 !player->is_dropping_pressed)
10957 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
10958 SnapField(player, 0, 0); /* stop snapping */
10962 CheckSaveEngineSnapshot(player);
10965 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
10967 int left = player_action & JOY_LEFT;
10968 int right = player_action & JOY_RIGHT;
10969 int up = player_action & JOY_UP;
10970 int down = player_action & JOY_DOWN;
10971 int button1 = player_action & JOY_BUTTON_1;
10972 int button2 = player_action & JOY_BUTTON_2;
10973 int dx = (left ? -1 : right ? 1 : 0);
10974 int dy = (up ? -1 : down ? 1 : 0);
10976 if (!player->active || tape.pausing)
10982 SnapField(player, dx, dy);
10986 DropElement(player);
10988 MovePlayer(player, dx, dy);
10991 CheckSingleStepMode(player);
10993 SetPlayerWaiting(player, FALSE);
10995 return player_action;
10999 /* no actions for this player (no input at player's configured device) */
11001 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
11002 SnapField(player, 0, 0);
11003 CheckGravityMovementWhenNotMoving(player);
11005 if (player->MovPos == 0)
11006 SetPlayerWaiting(player, TRUE);
11008 if (player->MovPos == 0) /* needed for tape.playing */
11009 player->is_moving = FALSE;
11011 player->is_dropping = FALSE;
11012 player->is_dropping_pressed = FALSE;
11013 player->drop_pressed_delay = 0;
11015 CheckSingleStepMode(player);
11021 static void SetMouseActionFromTapeAction(struct MouseActionInfo *mouse_action,
11024 if (!tape.use_mouse)
11027 mouse_action->lx = tape_action[TAPE_ACTION_LX];
11028 mouse_action->ly = tape_action[TAPE_ACTION_LY];
11029 mouse_action->button = tape_action[TAPE_ACTION_BUTTON];
11032 static void SetTapeActionFromMouseAction(byte *tape_action,
11033 struct MouseActionInfo *mouse_action)
11035 if (!tape.use_mouse)
11038 tape_action[TAPE_ACTION_LX] = mouse_action->lx;
11039 tape_action[TAPE_ACTION_LY] = mouse_action->ly;
11040 tape_action[TAPE_ACTION_BUTTON] = mouse_action->button;
11043 static void CheckLevelTime()
11047 /* !!! SAME CODE AS IN "GameActions()" -- FIX THIS !!! */
11048 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11050 if (level.native_em_level->lev->home == 0) /* all players at home */
11052 PlayerWins(local_player);
11054 AllPlayersGone = TRUE;
11056 level.native_em_level->lev->home = -1;
11059 if (level.native_em_level->ply[0]->alive == 0 &&
11060 level.native_em_level->ply[1]->alive == 0 &&
11061 level.native_em_level->ply[2]->alive == 0 &&
11062 level.native_em_level->ply[3]->alive == 0) /* all dead */
11063 AllPlayersGone = TRUE;
11065 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11067 if (game_sp.LevelSolved &&
11068 !game_sp.GameOver) /* game won */
11070 PlayerWins(local_player);
11072 game_sp.GameOver = TRUE;
11074 AllPlayersGone = TRUE;
11077 if (game_sp.GameOver) /* game lost */
11078 AllPlayersGone = TRUE;
11080 else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
11082 if (game_mm.level_solved &&
11083 !game_mm.game_over) /* game won */
11085 PlayerWins(local_player);
11087 game_mm.game_over = TRUE;
11089 AllPlayersGone = TRUE;
11092 if (game_mm.game_over) /* game lost */
11093 AllPlayersGone = TRUE;
11096 if (TimeFrames >= FRAMES_PER_SECOND)
11101 for (i = 0; i < MAX_PLAYERS; i++)
11103 struct PlayerInfo *player = &stored_player[i];
11105 if (SHIELD_ON(player))
11107 player->shield_normal_time_left--;
11109 if (player->shield_deadly_time_left > 0)
11110 player->shield_deadly_time_left--;
11114 if (!local_player->LevelSolved && !level.use_step_counter)
11122 if (TimeLeft <= 10 && setup.time_limit)
11123 PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
11125 /* this does not make sense: game_panel_controls[GAME_PANEL_TIME].value
11126 is reset from other values in UpdateGameDoorValues() -- FIX THIS */
11128 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
11130 if (!TimeLeft && setup.time_limit)
11132 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11133 level.native_em_level->lev->killed_out_of_time = TRUE;
11135 for (i = 0; i < MAX_PLAYERS; i++)
11136 KillPlayer(&stored_player[i]);
11139 else if (game.no_time_limit && !AllPlayersGone) /* level w/o time limit */
11141 game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
11144 level.native_em_level->lev->time =
11145 (game.no_time_limit ? TimePlayed : TimeLeft);
11148 if (tape.recording || tape.playing)
11149 DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
11152 if (tape.recording || tape.playing)
11153 DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
11155 UpdateAndDisplayGameControlValues();
11158 void AdvanceFrameAndPlayerCounters(int player_nr)
11162 /* advance frame counters (global frame counter and time frame counter) */
11166 /* advance player counters (counters for move delay, move animation etc.) */
11167 for (i = 0; i < MAX_PLAYERS; i++)
11169 boolean advance_player_counters = (player_nr == -1 || player_nr == i);
11170 int move_delay_value = stored_player[i].move_delay_value;
11171 int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
11173 if (!advance_player_counters) /* not all players may be affected */
11176 if (move_frames == 0) /* less than one move per game frame */
11178 int stepsize = TILEX / move_delay_value;
11179 int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
11180 int count = (stored_player[i].is_moving ?
11181 ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
11183 if (count % delay == 0)
11187 stored_player[i].Frame += move_frames;
11189 if (stored_player[i].MovPos != 0)
11190 stored_player[i].StepFrame += move_frames;
11192 if (stored_player[i].move_delay > 0)
11193 stored_player[i].move_delay--;
11195 /* due to bugs in previous versions, counter must count up, not down */
11196 if (stored_player[i].push_delay != -1)
11197 stored_player[i].push_delay++;
11199 if (stored_player[i].drop_delay > 0)
11200 stored_player[i].drop_delay--;
11202 if (stored_player[i].is_dropping_pressed)
11203 stored_player[i].drop_pressed_delay++;
11207 void StartGameActions(boolean init_network_game, boolean record_tape,
11210 unsigned int new_random_seed = InitRND(random_seed);
11213 TapeStartRecording(new_random_seed);
11215 if (init_network_game)
11217 SendToServer_StartPlaying();
11225 void GameActionsExt()
11228 static unsigned int game_frame_delay = 0;
11230 unsigned int game_frame_delay_value;
11231 byte *recorded_player_action;
11232 byte summarized_player_action = 0;
11233 byte tape_action[MAX_PLAYERS];
11236 /* detect endless loops, caused by custom element programming */
11237 if (recursion_loop_detected && recursion_loop_depth == 0)
11239 char *message = getStringCat3("Internal Error! Element ",
11240 EL_NAME(recursion_loop_element),
11241 " caused endless loop! Quit the game?");
11243 Error(ERR_WARN, "element '%s' caused endless loop in game engine",
11244 EL_NAME(recursion_loop_element));
11246 RequestQuitGameExt(FALSE, level_editor_test_game, message);
11248 recursion_loop_detected = FALSE; /* if game should be continued */
11255 if (game.restart_level)
11256 StartGameActions(network.enabled, setup.autorecord, level.random_seed);
11258 /* !!! SAME CODE AS IN "CheckLevelTime()" -- FIX THIS !!! */
11259 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11261 if (level.native_em_level->lev->home == 0) /* all players at home */
11263 PlayerWins(local_player);
11265 AllPlayersGone = TRUE;
11267 level.native_em_level->lev->home = -1;
11270 if (level.native_em_level->ply[0]->alive == 0 &&
11271 level.native_em_level->ply[1]->alive == 0 &&
11272 level.native_em_level->ply[2]->alive == 0 &&
11273 level.native_em_level->ply[3]->alive == 0) /* all dead */
11274 AllPlayersGone = TRUE;
11276 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11278 if (game_sp.LevelSolved &&
11279 !game_sp.GameOver) /* game won */
11281 PlayerWins(local_player);
11283 game_sp.GameOver = TRUE;
11285 AllPlayersGone = TRUE;
11288 if (game_sp.GameOver) /* game lost */
11289 AllPlayersGone = TRUE;
11291 else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
11293 if (game_mm.level_solved &&
11294 !game_mm.game_over) /* game won */
11296 PlayerWins(local_player);
11298 game_mm.game_over = TRUE;
11300 AllPlayersGone = TRUE;
11303 if (game_mm.game_over) /* game lost */
11304 AllPlayersGone = TRUE;
11307 if (local_player->LevelSolved && !local_player->LevelSolved_GameEnd)
11310 if (AllPlayersGone && !TAPE_IS_STOPPED(tape))
11313 if (game_status != GAME_MODE_PLAYING) /* status might have changed */
11316 game_frame_delay_value =
11317 (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
11319 if (tape.playing && tape.warp_forward && !tape.pausing)
11320 game_frame_delay_value = 0;
11322 SetVideoFrameDelay(game_frame_delay_value);
11326 /* ---------- main game synchronization point ---------- */
11328 int skip = WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11330 printf("::: skip == %d\n", skip);
11333 /* ---------- main game synchronization point ---------- */
11335 WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11339 if (network_playing && !network_player_action_received)
11341 /* try to get network player actions in time */
11343 /* last chance to get network player actions without main loop delay */
11344 HandleNetworking();
11346 /* game was quit by network peer */
11347 if (game_status != GAME_MODE_PLAYING)
11350 if (!network_player_action_received)
11351 return; /* failed to get network player actions in time */
11353 /* do not yet reset "network_player_action_received" (for tape.pausing) */
11359 /* at this point we know that we really continue executing the game */
11361 network_player_action_received = FALSE;
11363 /* when playing tape, read previously recorded player input from tape data */
11364 recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
11366 local_player->effective_mouse_action = local_player->mouse_action;
11368 if (recorded_player_action != NULL)
11369 SetMouseActionFromTapeAction(&local_player->effective_mouse_action,
11370 recorded_player_action);
11372 /* TapePlayAction() may return NULL when toggling to "pause before death" */
11376 if (tape.set_centered_player)
11378 game.centered_player_nr_next = tape.centered_player_nr_next;
11379 game.set_centered_player = TRUE;
11382 for (i = 0; i < MAX_PLAYERS; i++)
11384 summarized_player_action |= stored_player[i].action;
11386 if (!network_playing && (game.team_mode || tape.playing))
11387 stored_player[i].effective_action = stored_player[i].action;
11390 if (network_playing)
11391 SendToServer_MovePlayer(summarized_player_action);
11393 // summarize all actions at local players mapped input device position
11394 // (this allows using different input devices in single player mode)
11395 if (!network.enabled && !game.team_mode)
11396 stored_player[map_player_action[local_player->index_nr]].effective_action =
11397 summarized_player_action;
11399 if (tape.recording &&
11401 setup.input_on_focus &&
11402 game.centered_player_nr != -1)
11404 for (i = 0; i < MAX_PLAYERS; i++)
11405 stored_player[i].effective_action =
11406 (i == game.centered_player_nr ? summarized_player_action : 0);
11409 if (recorded_player_action != NULL)
11410 for (i = 0; i < MAX_PLAYERS; i++)
11411 stored_player[i].effective_action = recorded_player_action[i];
11413 for (i = 0; i < MAX_PLAYERS; i++)
11415 tape_action[i] = stored_player[i].effective_action;
11417 /* (this may happen in the RND game engine if a player was not present on
11418 the playfield on level start, but appeared later from a custom element */
11419 if (setup.team_mode &&
11422 !tape.player_participates[i])
11423 tape.player_participates[i] = TRUE;
11426 SetTapeActionFromMouseAction(tape_action,
11427 &local_player->effective_mouse_action);
11429 /* only record actions from input devices, but not programmed actions */
11430 if (tape.recording)
11431 TapeRecordAction(tape_action);
11433 #if USE_NEW_PLAYER_ASSIGNMENTS
11434 // !!! also map player actions in single player mode !!!
11435 // if (game.team_mode)
11438 byte mapped_action[MAX_PLAYERS];
11440 #if DEBUG_PLAYER_ACTIONS
11442 for (i = 0; i < MAX_PLAYERS; i++)
11443 printf(" %d, ", stored_player[i].effective_action);
11446 for (i = 0; i < MAX_PLAYERS; i++)
11447 mapped_action[i] = stored_player[map_player_action[i]].effective_action;
11449 for (i = 0; i < MAX_PLAYERS; i++)
11450 stored_player[i].effective_action = mapped_action[i];
11452 #if DEBUG_PLAYER_ACTIONS
11454 for (i = 0; i < MAX_PLAYERS; i++)
11455 printf(" %d, ", stored_player[i].effective_action);
11459 #if DEBUG_PLAYER_ACTIONS
11463 for (i = 0; i < MAX_PLAYERS; i++)
11464 printf(" %d, ", stored_player[i].effective_action);
11470 for (i = 0; i < MAX_PLAYERS; i++)
11472 // allow engine snapshot in case of changed movement attempt
11473 if ((game.snapshot.last_action[i] & KEY_MOTION) !=
11474 (stored_player[i].effective_action & KEY_MOTION))
11475 game.snapshot.changed_action = TRUE;
11477 // allow engine snapshot in case of snapping/dropping attempt
11478 if ((game.snapshot.last_action[i] & KEY_BUTTON) == 0 &&
11479 (stored_player[i].effective_action & KEY_BUTTON) != 0)
11480 game.snapshot.changed_action = TRUE;
11482 game.snapshot.last_action[i] = stored_player[i].effective_action;
11485 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11487 GameActions_EM_Main();
11489 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11491 GameActions_SP_Main();
11493 else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
11495 GameActions_MM_Main();
11499 GameActions_RND_Main();
11502 BlitScreenToBitmap(backbuffer);
11506 AdvanceFrameAndPlayerCounters(-1); /* advance counters for all players */
11508 if (global.show_frames_per_second)
11510 static unsigned int fps_counter = 0;
11511 static int fps_frames = 0;
11512 unsigned int fps_delay_ms = Counter() - fps_counter;
11516 if (fps_delay_ms >= 500) /* calculate FPS every 0.5 seconds */
11518 global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
11521 fps_counter = Counter();
11523 /* always draw FPS to screen after FPS value was updated */
11524 redraw_mask |= REDRAW_FPS;
11527 /* only draw FPS if no screen areas are deactivated (invisible warp mode) */
11528 if (GetDrawDeactivationMask() == REDRAW_NONE)
11529 redraw_mask |= REDRAW_FPS;
11533 static void GameActions_CheckSaveEngineSnapshot()
11535 if (!game.snapshot.save_snapshot)
11538 // clear flag for saving snapshot _before_ saving snapshot
11539 game.snapshot.save_snapshot = FALSE;
11541 SaveEngineSnapshotToList();
11548 GameActions_CheckSaveEngineSnapshot();
11551 void GameActions_EM_Main()
11553 byte effective_action[MAX_PLAYERS];
11554 boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11557 for (i = 0; i < MAX_PLAYERS; i++)
11558 effective_action[i] = stored_player[i].effective_action;
11560 GameActions_EM(effective_action, warp_mode);
11563 void GameActions_SP_Main()
11565 byte effective_action[MAX_PLAYERS];
11566 boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11569 for (i = 0; i < MAX_PLAYERS; i++)
11570 effective_action[i] = stored_player[i].effective_action;
11572 GameActions_SP(effective_action, warp_mode);
11574 for (i = 0; i < MAX_PLAYERS; i++)
11576 if (stored_player[i].force_dropping)
11577 stored_player[i].action |= KEY_BUTTON_DROP;
11579 stored_player[i].force_dropping = FALSE;
11583 void GameActions_MM_Main()
11585 boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11587 GameActions_MM(local_player->effective_mouse_action, warp_mode);
11590 void GameActions_RND_Main()
11595 void GameActions_RND()
11597 int magic_wall_x = 0, magic_wall_y = 0;
11598 int i, x, y, element, graphic, last_gfx_frame;
11600 InitPlayfieldScanModeVars();
11602 if (game.engine_version >= VERSION_IDENT(3,2,0,7))
11604 SCAN_PLAYFIELD(x, y)
11606 ChangeCount[x][y] = 0;
11607 ChangeEvent[x][y] = -1;
11611 if (game.set_centered_player)
11613 boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
11615 /* switching to "all players" only possible if all players fit to screen */
11616 if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
11618 game.centered_player_nr_next = game.centered_player_nr;
11619 game.set_centered_player = FALSE;
11622 /* do not switch focus to non-existing (or non-active) player */
11623 if (game.centered_player_nr_next >= 0 &&
11624 !stored_player[game.centered_player_nr_next].active)
11626 game.centered_player_nr_next = game.centered_player_nr;
11627 game.set_centered_player = FALSE;
11631 if (game.set_centered_player &&
11632 ScreenMovPos == 0) /* screen currently aligned at tile position */
11636 if (game.centered_player_nr_next == -1)
11638 setScreenCenteredToAllPlayers(&sx, &sy);
11642 sx = stored_player[game.centered_player_nr_next].jx;
11643 sy = stored_player[game.centered_player_nr_next].jy;
11646 game.centered_player_nr = game.centered_player_nr_next;
11647 game.set_centered_player = FALSE;
11649 DrawRelocateScreen(0, 0, sx, sy, MV_NONE, TRUE, setup.quick_switch);
11650 DrawGameDoorValues();
11653 for (i = 0; i < MAX_PLAYERS; i++)
11655 int actual_player_action = stored_player[i].effective_action;
11658 /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
11659 - rnd_equinox_tetrachloride 048
11660 - rnd_equinox_tetrachloride_ii 096
11661 - rnd_emanuel_schmieg 002
11662 - doctor_sloan_ww 001, 020
11664 if (stored_player[i].MovPos == 0)
11665 CheckGravityMovement(&stored_player[i]);
11668 /* overwrite programmed action with tape action */
11669 if (stored_player[i].programmed_action)
11670 actual_player_action = stored_player[i].programmed_action;
11672 PlayerActions(&stored_player[i], actual_player_action);
11674 ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
11677 ScrollScreen(NULL, SCROLL_GO_ON);
11679 /* for backwards compatibility, the following code emulates a fixed bug that
11680 occured when pushing elements (causing elements that just made their last
11681 pushing step to already (if possible) make their first falling step in the
11682 same game frame, which is bad); this code is also needed to use the famous
11683 "spring push bug" which is used in older levels and might be wanted to be
11684 used also in newer levels, but in this case the buggy pushing code is only
11685 affecting the "spring" element and no other elements */
11687 if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
11689 for (i = 0; i < MAX_PLAYERS; i++)
11691 struct PlayerInfo *player = &stored_player[i];
11692 int x = player->jx;
11693 int y = player->jy;
11695 if (player->active && player->is_pushing && player->is_moving &&
11697 (game.engine_version < VERSION_IDENT(2,2,0,7) ||
11698 Feld[x][y] == EL_SPRING))
11700 ContinueMoving(x, y);
11702 /* continue moving after pushing (this is actually a bug) */
11703 if (!IS_MOVING(x, y))
11704 Stop[x][y] = FALSE;
11709 SCAN_PLAYFIELD(x, y)
11711 ChangeCount[x][y] = 0;
11712 ChangeEvent[x][y] = -1;
11714 /* this must be handled before main playfield loop */
11715 if (Feld[x][y] == EL_PLAYER_IS_LEAVING)
11718 if (MovDelay[x][y] <= 0)
11722 if (Feld[x][y] == EL_ELEMENT_SNAPPING)
11725 if (MovDelay[x][y] <= 0)
11728 TEST_DrawLevelField(x, y);
11730 TestIfElementTouchesCustomElement(x, y); /* for empty space */
11735 if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
11737 printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
11738 printf("GameActions(): This should never happen!\n");
11740 ChangePage[x][y] = -1;
11744 Stop[x][y] = FALSE;
11745 if (WasJustMoving[x][y] > 0)
11746 WasJustMoving[x][y]--;
11747 if (WasJustFalling[x][y] > 0)
11748 WasJustFalling[x][y]--;
11749 if (CheckCollision[x][y] > 0)
11750 CheckCollision[x][y]--;
11751 if (CheckImpact[x][y] > 0)
11752 CheckImpact[x][y]--;
11756 /* reset finished pushing action (not done in ContinueMoving() to allow
11757 continuous pushing animation for elements with zero push delay) */
11758 if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
11760 ResetGfxAnimation(x, y);
11761 TEST_DrawLevelField(x, y);
11765 if (IS_BLOCKED(x, y))
11769 Blocked2Moving(x, y, &oldx, &oldy);
11770 if (!IS_MOVING(oldx, oldy))
11772 printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
11773 printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
11774 printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
11775 printf("GameActions(): This should never happen!\n");
11781 SCAN_PLAYFIELD(x, y)
11783 element = Feld[x][y];
11784 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11785 last_gfx_frame = GfxFrame[x][y];
11787 ResetGfxFrame(x, y);
11789 if (GfxFrame[x][y] != last_gfx_frame && !Stop[x][y])
11790 DrawLevelGraphicAnimation(x, y, graphic);
11792 if (ANIM_MODE(graphic) == ANIM_RANDOM &&
11793 IS_NEXT_FRAME(GfxFrame[x][y], graphic))
11794 ResetRandomAnimationValue(x, y);
11796 SetRandomAnimationValue(x, y);
11798 PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
11800 if (IS_INACTIVE(element))
11802 if (IS_ANIMATED(graphic))
11803 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11808 /* this may take place after moving, so 'element' may have changed */
11809 if (IS_CHANGING(x, y) &&
11810 (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
11812 int page = element_info[element].event_page_nr[CE_DELAY];
11814 HandleElementChange(x, y, page);
11816 element = Feld[x][y];
11817 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11820 if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
11824 element = Feld[x][y];
11825 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11827 if (IS_ANIMATED(graphic) &&
11828 !IS_MOVING(x, y) &&
11830 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11832 if (IS_GEM(element) || element == EL_SP_INFOTRON)
11833 TEST_DrawTwinkleOnField(x, y);
11835 else if (element == EL_ACID)
11838 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11840 else if ((element == EL_EXIT_OPEN ||
11841 element == EL_EM_EXIT_OPEN ||
11842 element == EL_SP_EXIT_OPEN ||
11843 element == EL_STEEL_EXIT_OPEN ||
11844 element == EL_EM_STEEL_EXIT_OPEN ||
11845 element == EL_SP_TERMINAL ||
11846 element == EL_SP_TERMINAL_ACTIVE ||
11847 element == EL_EXTRA_TIME ||
11848 element == EL_SHIELD_NORMAL ||
11849 element == EL_SHIELD_DEADLY) &&
11850 IS_ANIMATED(graphic))
11851 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11852 else if (IS_MOVING(x, y))
11853 ContinueMoving(x, y);
11854 else if (IS_ACTIVE_BOMB(element))
11855 CheckDynamite(x, y);
11856 else if (element == EL_AMOEBA_GROWING)
11857 AmoebeWaechst(x, y);
11858 else if (element == EL_AMOEBA_SHRINKING)
11859 AmoebaDisappearing(x, y);
11861 #if !USE_NEW_AMOEBA_CODE
11862 else if (IS_AMOEBALIVE(element))
11863 AmoebeAbleger(x, y);
11866 else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
11868 else if (element == EL_EXIT_CLOSED)
11870 else if (element == EL_EM_EXIT_CLOSED)
11872 else if (element == EL_STEEL_EXIT_CLOSED)
11873 CheckExitSteel(x, y);
11874 else if (element == EL_EM_STEEL_EXIT_CLOSED)
11875 CheckExitSteelEM(x, y);
11876 else if (element == EL_SP_EXIT_CLOSED)
11878 else if (element == EL_EXPANDABLE_WALL_GROWING ||
11879 element == EL_EXPANDABLE_STEELWALL_GROWING)
11880 MauerWaechst(x, y);
11881 else if (element == EL_EXPANDABLE_WALL ||
11882 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
11883 element == EL_EXPANDABLE_WALL_VERTICAL ||
11884 element == EL_EXPANDABLE_WALL_ANY ||
11885 element == EL_BD_EXPANDABLE_WALL)
11886 MauerAbleger(x, y);
11887 else if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
11888 element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
11889 element == EL_EXPANDABLE_STEELWALL_ANY)
11890 MauerAblegerStahl(x, y);
11891 else if (element == EL_FLAMES)
11892 CheckForDragon(x, y);
11893 else if (element == EL_EXPLOSION)
11894 ; /* drawing of correct explosion animation is handled separately */
11895 else if (element == EL_ELEMENT_SNAPPING ||
11896 element == EL_DIAGONAL_SHRINKING ||
11897 element == EL_DIAGONAL_GROWING)
11899 graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
11901 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11903 else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
11904 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11906 if (IS_BELT_ACTIVE(element))
11907 PlayLevelSoundAction(x, y, ACTION_ACTIVE);
11909 if (game.magic_wall_active)
11911 int jx = local_player->jx, jy = local_player->jy;
11913 /* play the element sound at the position nearest to the player */
11914 if ((element == EL_MAGIC_WALL_FULL ||
11915 element == EL_MAGIC_WALL_ACTIVE ||
11916 element == EL_MAGIC_WALL_EMPTYING ||
11917 element == EL_BD_MAGIC_WALL_FULL ||
11918 element == EL_BD_MAGIC_WALL_ACTIVE ||
11919 element == EL_BD_MAGIC_WALL_EMPTYING ||
11920 element == EL_DC_MAGIC_WALL_FULL ||
11921 element == EL_DC_MAGIC_WALL_ACTIVE ||
11922 element == EL_DC_MAGIC_WALL_EMPTYING) &&
11923 ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
11931 #if USE_NEW_AMOEBA_CODE
11932 /* new experimental amoeba growth stuff */
11933 if (!(FrameCounter % 8))
11935 static unsigned int random = 1684108901;
11937 for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
11939 x = RND(lev_fieldx);
11940 y = RND(lev_fieldy);
11941 element = Feld[x][y];
11943 if (!IS_PLAYER(x,y) &&
11944 (element == EL_EMPTY ||
11945 CAN_GROW_INTO(element) ||
11946 element == EL_QUICKSAND_EMPTY ||
11947 element == EL_QUICKSAND_FAST_EMPTY ||
11948 element == EL_ACID_SPLASH_LEFT ||
11949 element == EL_ACID_SPLASH_RIGHT))
11951 if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
11952 (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
11953 (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
11954 (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
11955 Feld[x][y] = EL_AMOEBA_DROP;
11958 random = random * 129 + 1;
11963 game.explosions_delayed = FALSE;
11965 SCAN_PLAYFIELD(x, y)
11967 element = Feld[x][y];
11969 if (ExplodeField[x][y])
11970 Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
11971 else if (element == EL_EXPLOSION)
11972 Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
11974 ExplodeField[x][y] = EX_TYPE_NONE;
11977 game.explosions_delayed = TRUE;
11979 if (game.magic_wall_active)
11981 if (!(game.magic_wall_time_left % 4))
11983 int element = Feld[magic_wall_x][magic_wall_y];
11985 if (element == EL_BD_MAGIC_WALL_FULL ||
11986 element == EL_BD_MAGIC_WALL_ACTIVE ||
11987 element == EL_BD_MAGIC_WALL_EMPTYING)
11988 PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
11989 else if (element == EL_DC_MAGIC_WALL_FULL ||
11990 element == EL_DC_MAGIC_WALL_ACTIVE ||
11991 element == EL_DC_MAGIC_WALL_EMPTYING)
11992 PlayLevelSound(magic_wall_x, magic_wall_y, SND_DC_MAGIC_WALL_ACTIVE);
11994 PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
11997 if (game.magic_wall_time_left > 0)
11999 game.magic_wall_time_left--;
12001 if (!game.magic_wall_time_left)
12003 SCAN_PLAYFIELD(x, y)
12005 element = Feld[x][y];
12007 if (element == EL_MAGIC_WALL_ACTIVE ||
12008 element == EL_MAGIC_WALL_FULL)
12010 Feld[x][y] = EL_MAGIC_WALL_DEAD;
12011 TEST_DrawLevelField(x, y);
12013 else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
12014 element == EL_BD_MAGIC_WALL_FULL)
12016 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
12017 TEST_DrawLevelField(x, y);
12019 else if (element == EL_DC_MAGIC_WALL_ACTIVE ||
12020 element == EL_DC_MAGIC_WALL_FULL)
12022 Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
12023 TEST_DrawLevelField(x, y);
12027 game.magic_wall_active = FALSE;
12032 if (game.light_time_left > 0)
12034 game.light_time_left--;
12036 if (game.light_time_left == 0)
12037 RedrawAllLightSwitchesAndInvisibleElements();
12040 if (game.timegate_time_left > 0)
12042 game.timegate_time_left--;
12044 if (game.timegate_time_left == 0)
12045 CloseAllOpenTimegates();
12048 if (game.lenses_time_left > 0)
12050 game.lenses_time_left--;
12052 if (game.lenses_time_left == 0)
12053 RedrawAllInvisibleElementsForLenses();
12056 if (game.magnify_time_left > 0)
12058 game.magnify_time_left--;
12060 if (game.magnify_time_left == 0)
12061 RedrawAllInvisibleElementsForMagnifier();
12064 for (i = 0; i < MAX_PLAYERS; i++)
12066 struct PlayerInfo *player = &stored_player[i];
12068 if (SHIELD_ON(player))
12070 if (player->shield_deadly_time_left)
12071 PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
12072 else if (player->shield_normal_time_left)
12073 PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
12077 #if USE_DELAYED_GFX_REDRAW
12078 SCAN_PLAYFIELD(x, y)
12080 if (GfxRedraw[x][y] != GFX_REDRAW_NONE)
12082 /* !!! PROBLEM: THIS REDRAWS THE PLAYFIELD _AFTER_ THE SCAN, BUT TILES
12083 !!! MAY HAVE CHANGED AFTER BEING DRAWN DURING PLAYFIELD SCAN !!! */
12085 if (GfxRedraw[x][y] & GFX_REDRAW_TILE)
12086 DrawLevelField(x, y);
12088 if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED)
12089 DrawLevelFieldCrumbled(x, y);
12091 if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS)
12092 DrawLevelFieldCrumbledNeighbours(x, y);
12094 if (GfxRedraw[x][y] & GFX_REDRAW_TILE_TWINKLED)
12095 DrawTwinkleOnField(x, y);
12098 GfxRedraw[x][y] = GFX_REDRAW_NONE;
12103 PlayAllPlayersSound();
12105 if (local_player->show_envelope != 0 && local_player->MovPos == 0)
12107 ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
12109 local_player->show_envelope = 0;
12112 /* use random number generator in every frame to make it less predictable */
12113 if (game.engine_version >= VERSION_IDENT(3,1,1,0))
12117 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
12119 int min_x = x, min_y = y, max_x = x, max_y = y;
12122 for (i = 0; i < MAX_PLAYERS; i++)
12124 int jx = stored_player[i].jx, jy = stored_player[i].jy;
12126 if (!stored_player[i].active || &stored_player[i] == player)
12129 min_x = MIN(min_x, jx);
12130 min_y = MIN(min_y, jy);
12131 max_x = MAX(max_x, jx);
12132 max_y = MAX(max_y, jy);
12135 return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
12138 static boolean AllPlayersInVisibleScreen()
12142 for (i = 0; i < MAX_PLAYERS; i++)
12144 int jx = stored_player[i].jx, jy = stored_player[i].jy;
12146 if (!stored_player[i].active)
12149 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12156 void ScrollLevel(int dx, int dy)
12158 int scroll_offset = 2 * TILEX_VAR;
12161 BlitBitmap(drawto_field, drawto_field,
12162 FX + TILEX_VAR * (dx == -1) - scroll_offset,
12163 FY + TILEY_VAR * (dy == -1) - scroll_offset,
12164 SXSIZE - TILEX_VAR * (dx != 0) + 2 * scroll_offset,
12165 SYSIZE - TILEY_VAR * (dy != 0) + 2 * scroll_offset,
12166 FX + TILEX_VAR * (dx == 1) - scroll_offset,
12167 FY + TILEY_VAR * (dy == 1) - scroll_offset);
12171 x = (dx == 1 ? BX1 : BX2);
12172 for (y = BY1; y <= BY2; y++)
12173 DrawScreenField(x, y);
12178 y = (dy == 1 ? BY1 : BY2);
12179 for (x = BX1; x <= BX2; x++)
12180 DrawScreenField(x, y);
12183 redraw_mask |= REDRAW_FIELD;
12186 static boolean canFallDown(struct PlayerInfo *player)
12188 int jx = player->jx, jy = player->jy;
12190 return (IN_LEV_FIELD(jx, jy + 1) &&
12191 (IS_FREE(jx, jy + 1) ||
12192 (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
12193 IS_WALKABLE_FROM(Feld[jx][jy], MV_DOWN) &&
12194 !IS_WALKABLE_INSIDE(Feld[jx][jy]));
12197 static boolean canPassField(int x, int y, int move_dir)
12199 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
12200 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
12201 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
12202 int nextx = x + dx;
12203 int nexty = y + dy;
12204 int element = Feld[x][y];
12206 return (IS_PASSABLE_FROM(element, opposite_dir) &&
12207 !CAN_MOVE(element) &&
12208 IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
12209 IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
12210 (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
12213 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
12215 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
12216 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
12217 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
12221 return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
12222 IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
12223 (IS_DIGGABLE(Feld[newx][newy]) ||
12224 IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
12225 canPassField(newx, newy, move_dir)));
12228 static void CheckGravityMovement(struct PlayerInfo *player)
12230 if (player->gravity && !player->programmed_action)
12232 int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
12233 int move_dir_vertical = player->effective_action & MV_VERTICAL;
12234 boolean player_is_snapping = (player->effective_action & JOY_BUTTON_1);
12235 int jx = player->jx, jy = player->jy;
12236 boolean player_is_moving_to_valid_field =
12237 (!player_is_snapping &&
12238 (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
12239 canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
12240 boolean player_can_fall_down = canFallDown(player);
12242 if (player_can_fall_down &&
12243 !player_is_moving_to_valid_field)
12244 player->programmed_action = MV_DOWN;
12248 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
12250 return CheckGravityMovement(player);
12252 if (player->gravity && !player->programmed_action)
12254 int jx = player->jx, jy = player->jy;
12255 boolean field_under_player_is_free =
12256 (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
12257 boolean player_is_standing_on_valid_field =
12258 (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
12259 (IS_WALKABLE(Feld[jx][jy]) &&
12260 !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
12262 if (field_under_player_is_free && !player_is_standing_on_valid_field)
12263 player->programmed_action = MV_DOWN;
12268 MovePlayerOneStep()
12269 -----------------------------------------------------------------------------
12270 dx, dy: direction (non-diagonal) to try to move the player to
12271 real_dx, real_dy: direction as read from input device (can be diagonal)
12274 boolean MovePlayerOneStep(struct PlayerInfo *player,
12275 int dx, int dy, int real_dx, int real_dy)
12277 int jx = player->jx, jy = player->jy;
12278 int new_jx = jx + dx, new_jy = jy + dy;
12280 boolean player_can_move = !player->cannot_move;
12282 if (!player->active || (!dx && !dy))
12283 return MP_NO_ACTION;
12285 player->MovDir = (dx < 0 ? MV_LEFT :
12286 dx > 0 ? MV_RIGHT :
12288 dy > 0 ? MV_DOWN : MV_NONE);
12290 if (!IN_LEV_FIELD(new_jx, new_jy))
12291 return MP_NO_ACTION;
12293 if (!player_can_move)
12295 if (player->MovPos == 0)
12297 player->is_moving = FALSE;
12298 player->is_digging = FALSE;
12299 player->is_collecting = FALSE;
12300 player->is_snapping = FALSE;
12301 player->is_pushing = FALSE;
12305 if (!network.enabled && game.centered_player_nr == -1 &&
12306 !AllPlayersInSight(player, new_jx, new_jy))
12307 return MP_NO_ACTION;
12309 can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
12310 if (can_move != MP_MOVING)
12313 /* check if DigField() has caused relocation of the player */
12314 if (player->jx != jx || player->jy != jy)
12315 return MP_NO_ACTION; /* <-- !!! CHECK THIS [-> MP_ACTION ?] !!! */
12317 StorePlayer[jx][jy] = 0;
12318 player->last_jx = jx;
12319 player->last_jy = jy;
12320 player->jx = new_jx;
12321 player->jy = new_jy;
12322 StorePlayer[new_jx][new_jy] = player->element_nr;
12324 if (player->move_delay_value_next != -1)
12326 player->move_delay_value = player->move_delay_value_next;
12327 player->move_delay_value_next = -1;
12331 (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
12333 player->step_counter++;
12335 PlayerVisit[jx][jy] = FrameCounter;
12337 player->is_moving = TRUE;
12340 /* should better be called in MovePlayer(), but this breaks some tapes */
12341 ScrollPlayer(player, SCROLL_INIT);
12347 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
12349 int jx = player->jx, jy = player->jy;
12350 int old_jx = jx, old_jy = jy;
12351 int moved = MP_NO_ACTION;
12353 if (!player->active)
12358 if (player->MovPos == 0)
12360 player->is_moving = FALSE;
12361 player->is_digging = FALSE;
12362 player->is_collecting = FALSE;
12363 player->is_snapping = FALSE;
12364 player->is_pushing = FALSE;
12370 if (player->move_delay > 0)
12373 player->move_delay = -1; /* set to "uninitialized" value */
12375 /* store if player is automatically moved to next field */
12376 player->is_auto_moving = (player->programmed_action != MV_NONE);
12378 /* remove the last programmed player action */
12379 player->programmed_action = 0;
12381 if (player->MovPos)
12383 /* should only happen if pre-1.2 tape recordings are played */
12384 /* this is only for backward compatibility */
12386 int original_move_delay_value = player->move_delay_value;
12389 printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%d]\n",
12393 /* scroll remaining steps with finest movement resolution */
12394 player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
12396 while (player->MovPos)
12398 ScrollPlayer(player, SCROLL_GO_ON);
12399 ScrollScreen(NULL, SCROLL_GO_ON);
12401 AdvanceFrameAndPlayerCounters(player->index_nr);
12404 BackToFront_WithFrameDelay(0);
12407 player->move_delay_value = original_move_delay_value;
12410 player->is_active = FALSE;
12412 if (player->last_move_dir & MV_HORIZONTAL)
12414 if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
12415 moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
12419 if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
12420 moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
12423 if (!moved && !player->is_active)
12425 player->is_moving = FALSE;
12426 player->is_digging = FALSE;
12427 player->is_collecting = FALSE;
12428 player->is_snapping = FALSE;
12429 player->is_pushing = FALSE;
12435 if (moved & MP_MOVING && !ScreenMovPos &&
12436 (player->index_nr == game.centered_player_nr ||
12437 game.centered_player_nr == -1))
12439 int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
12440 int offset = game.scroll_delay_value;
12442 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12444 /* actual player has left the screen -- scroll in that direction */
12445 if (jx != old_jx) /* player has moved horizontally */
12446 scroll_x += (jx - old_jx);
12447 else /* player has moved vertically */
12448 scroll_y += (jy - old_jy);
12452 if (jx != old_jx) /* player has moved horizontally */
12454 if ((player->MovDir == MV_LEFT && scroll_x > jx - MIDPOSX + offset) ||
12455 (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
12456 scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
12458 /* don't scroll over playfield boundaries */
12459 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
12460 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
12462 /* don't scroll more than one field at a time */
12463 scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
12465 /* don't scroll against the player's moving direction */
12466 if ((player->MovDir == MV_LEFT && scroll_x > old_scroll_x) ||
12467 (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
12468 scroll_x = old_scroll_x;
12470 else /* player has moved vertically */
12472 if ((player->MovDir == MV_UP && scroll_y > jy - MIDPOSY + offset) ||
12473 (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
12474 scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
12476 /* don't scroll over playfield boundaries */
12477 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
12478 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
12480 /* don't scroll more than one field at a time */
12481 scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
12483 /* don't scroll against the player's moving direction */
12484 if ((player->MovDir == MV_UP && scroll_y > old_scroll_y) ||
12485 (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
12486 scroll_y = old_scroll_y;
12490 if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
12492 if (!network.enabled && game.centered_player_nr == -1 &&
12493 !AllPlayersInVisibleScreen())
12495 scroll_x = old_scroll_x;
12496 scroll_y = old_scroll_y;
12500 ScrollScreen(player, SCROLL_INIT);
12501 ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
12506 player->StepFrame = 0;
12508 if (moved & MP_MOVING)
12510 if (old_jx != jx && old_jy == jy)
12511 player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
12512 else if (old_jx == jx && old_jy != jy)
12513 player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
12515 TEST_DrawLevelField(jx, jy); /* for "crumbled sand" */
12517 player->last_move_dir = player->MovDir;
12518 player->is_moving = TRUE;
12519 player->is_snapping = FALSE;
12520 player->is_switching = FALSE;
12521 player->is_dropping = FALSE;
12522 player->is_dropping_pressed = FALSE;
12523 player->drop_pressed_delay = 0;
12526 /* should better be called here than above, but this breaks some tapes */
12527 ScrollPlayer(player, SCROLL_INIT);
12532 CheckGravityMovementWhenNotMoving(player);
12534 player->is_moving = FALSE;
12536 /* at this point, the player is allowed to move, but cannot move right now
12537 (e.g. because of something blocking the way) -- ensure that the player
12538 is also allowed to move in the next frame (in old versions before 3.1.1,
12539 the player was forced to wait again for eight frames before next try) */
12541 if (game.engine_version >= VERSION_IDENT(3,1,1,0))
12542 player->move_delay = 0; /* allow direct movement in the next frame */
12545 if (player->move_delay == -1) /* not yet initialized by DigField() */
12546 player->move_delay = player->move_delay_value;
12548 if (game.engine_version < VERSION_IDENT(3,0,7,0))
12550 TestIfPlayerTouchesBadThing(jx, jy);
12551 TestIfPlayerTouchesCustomElement(jx, jy);
12554 if (!player->active)
12555 RemovePlayer(player);
12560 void ScrollPlayer(struct PlayerInfo *player, int mode)
12562 int jx = player->jx, jy = player->jy;
12563 int last_jx = player->last_jx, last_jy = player->last_jy;
12564 int move_stepsize = TILEX / player->move_delay_value;
12566 if (!player->active)
12569 if (player->MovPos == 0 && mode == SCROLL_GO_ON) /* player not moving */
12572 if (mode == SCROLL_INIT)
12574 player->actual_frame_counter = FrameCounter;
12575 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
12577 if ((player->block_last_field || player->block_delay_adjustment > 0) &&
12578 Feld[last_jx][last_jy] == EL_EMPTY)
12580 int last_field_block_delay = 0; /* start with no blocking at all */
12581 int block_delay_adjustment = player->block_delay_adjustment;
12583 /* if player blocks last field, add delay for exactly one move */
12584 if (player->block_last_field)
12586 last_field_block_delay += player->move_delay_value;
12588 /* when blocking enabled, prevent moving up despite gravity */
12589 if (player->gravity && player->MovDir == MV_UP)
12590 block_delay_adjustment = -1;
12593 /* add block delay adjustment (also possible when not blocking) */
12594 last_field_block_delay += block_delay_adjustment;
12596 Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
12597 MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
12600 if (player->MovPos != 0) /* player has not yet reached destination */
12603 else if (!FrameReached(&player->actual_frame_counter, 1))
12606 if (player->MovPos != 0)
12608 player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
12609 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
12611 /* before DrawPlayer() to draw correct player graphic for this case */
12612 if (player->MovPos == 0)
12613 CheckGravityMovement(player);
12616 if (player->MovPos == 0) /* player reached destination field */
12618 if (player->move_delay_reset_counter > 0)
12620 player->move_delay_reset_counter--;
12622 if (player->move_delay_reset_counter == 0)
12624 /* continue with normal speed after quickly moving through gate */
12625 HALVE_PLAYER_SPEED(player);
12627 /* be able to make the next move without delay */
12628 player->move_delay = 0;
12632 player->last_jx = jx;
12633 player->last_jy = jy;
12635 if (Feld[jx][jy] == EL_EXIT_OPEN ||
12636 Feld[jx][jy] == EL_EM_EXIT_OPEN ||
12637 Feld[jx][jy] == EL_EM_EXIT_OPENING ||
12638 Feld[jx][jy] == EL_STEEL_EXIT_OPEN ||
12639 Feld[jx][jy] == EL_EM_STEEL_EXIT_OPEN ||
12640 Feld[jx][jy] == EL_EM_STEEL_EXIT_OPENING ||
12641 Feld[jx][jy] == EL_SP_EXIT_OPEN ||
12642 Feld[jx][jy] == EL_SP_EXIT_OPENING) /* <-- special case */
12644 DrawPlayer(player); /* needed here only to cleanup last field */
12645 RemovePlayer(player);
12647 if (local_player->friends_still_needed == 0 ||
12648 IS_SP_ELEMENT(Feld[jx][jy]))
12649 PlayerWins(player);
12652 /* this breaks one level: "machine", level 000 */
12654 int move_direction = player->MovDir;
12655 int enter_side = MV_DIR_OPPOSITE(move_direction);
12656 int leave_side = move_direction;
12657 int old_jx = last_jx;
12658 int old_jy = last_jy;
12659 int old_element = Feld[old_jx][old_jy];
12660 int new_element = Feld[jx][jy];
12662 if (IS_CUSTOM_ELEMENT(old_element))
12663 CheckElementChangeByPlayer(old_jx, old_jy, old_element,
12665 player->index_bit, leave_side);
12667 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
12668 CE_PLAYER_LEAVES_X,
12669 player->index_bit, leave_side);
12671 if (IS_CUSTOM_ELEMENT(new_element))
12672 CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
12673 player->index_bit, enter_side);
12675 CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
12676 CE_PLAYER_ENTERS_X,
12677 player->index_bit, enter_side);
12679 CheckTriggeredElementChangeBySide(jx, jy, player->initial_element,
12680 CE_MOVE_OF_X, move_direction);
12683 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
12685 TestIfPlayerTouchesBadThing(jx, jy);
12686 TestIfPlayerTouchesCustomElement(jx, jy);
12688 /* needed because pushed element has not yet reached its destination,
12689 so it would trigger a change event at its previous field location */
12690 if (!player->is_pushing)
12691 TestIfElementTouchesCustomElement(jx, jy); /* for empty space */
12693 if (!player->active)
12694 RemovePlayer(player);
12697 if (!local_player->LevelSolved && level.use_step_counter)
12707 if (TimeLeft <= 10 && setup.time_limit)
12708 PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
12710 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
12712 DisplayGameControlValues();
12714 if (!TimeLeft && setup.time_limit)
12715 for (i = 0; i < MAX_PLAYERS; i++)
12716 KillPlayer(&stored_player[i]);
12718 else if (game.no_time_limit && !AllPlayersGone) /* level w/o time limit */
12720 game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
12722 DisplayGameControlValues();
12726 if (tape.single_step && tape.recording && !tape.pausing &&
12727 !player->programmed_action)
12728 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
12730 if (!player->programmed_action)
12731 CheckSaveEngineSnapshot(player);
12735 void ScrollScreen(struct PlayerInfo *player, int mode)
12737 static unsigned int screen_frame_counter = 0;
12739 if (mode == SCROLL_INIT)
12741 /* set scrolling step size according to actual player's moving speed */
12742 ScrollStepSize = TILEX / player->move_delay_value;
12744 screen_frame_counter = FrameCounter;
12745 ScreenMovDir = player->MovDir;
12746 ScreenMovPos = player->MovPos;
12747 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
12750 else if (!FrameReached(&screen_frame_counter, 1))
12755 ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
12756 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
12757 redraw_mask |= REDRAW_FIELD;
12760 ScreenMovDir = MV_NONE;
12763 void TestIfPlayerTouchesCustomElement(int x, int y)
12765 static int xy[4][2] =
12772 static int trigger_sides[4][2] =
12774 /* center side border side */
12775 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
12776 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
12777 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
12778 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
12780 static int touch_dir[4] =
12782 MV_LEFT | MV_RIGHT,
12787 int center_element = Feld[x][y]; /* should always be non-moving! */
12790 for (i = 0; i < NUM_DIRECTIONS; i++)
12792 int xx = x + xy[i][0];
12793 int yy = y + xy[i][1];
12794 int center_side = trigger_sides[i][0];
12795 int border_side = trigger_sides[i][1];
12796 int border_element;
12798 if (!IN_LEV_FIELD(xx, yy))
12801 if (IS_PLAYER(x, y)) /* player found at center element */
12803 struct PlayerInfo *player = PLAYERINFO(x, y);
12805 if (game.engine_version < VERSION_IDENT(3,0,7,0))
12806 border_element = Feld[xx][yy]; /* may be moving! */
12807 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
12808 border_element = Feld[xx][yy];
12809 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
12810 border_element = MovingOrBlocked2Element(xx, yy);
12812 continue; /* center and border element do not touch */
12814 CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
12815 player->index_bit, border_side);
12816 CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
12817 CE_PLAYER_TOUCHES_X,
12818 player->index_bit, border_side);
12821 /* use player element that is initially defined in the level playfield,
12822 not the player element that corresponds to the runtime player number
12823 (example: a level that contains EL_PLAYER_3 as the only player would
12824 incorrectly give EL_PLAYER_1 for "player->element_nr") */
12825 int player_element = PLAYERINFO(x, y)->initial_element;
12827 CheckElementChangeBySide(xx, yy, border_element, player_element,
12828 CE_TOUCHING_X, border_side);
12831 else if (IS_PLAYER(xx, yy)) /* player found at border element */
12833 struct PlayerInfo *player = PLAYERINFO(xx, yy);
12835 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
12837 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
12838 continue; /* center and border element do not touch */
12841 CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
12842 player->index_bit, center_side);
12843 CheckTriggeredElementChangeByPlayer(x, y, center_element,
12844 CE_PLAYER_TOUCHES_X,
12845 player->index_bit, center_side);
12848 /* use player element that is initially defined in the level playfield,
12849 not the player element that corresponds to the runtime player number
12850 (example: a level that contains EL_PLAYER_3 as the only player would
12851 incorrectly give EL_PLAYER_1 for "player->element_nr") */
12852 int player_element = PLAYERINFO(xx, yy)->initial_element;
12854 CheckElementChangeBySide(x, y, center_element, player_element,
12855 CE_TOUCHING_X, center_side);
12863 void TestIfElementTouchesCustomElement(int x, int y)
12865 static int xy[4][2] =
12872 static int trigger_sides[4][2] =
12874 /* center side border side */
12875 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
12876 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
12877 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
12878 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
12880 static int touch_dir[4] =
12882 MV_LEFT | MV_RIGHT,
12887 boolean change_center_element = FALSE;
12888 int center_element = Feld[x][y]; /* should always be non-moving! */
12889 int border_element_old[NUM_DIRECTIONS];
12892 for (i = 0; i < NUM_DIRECTIONS; i++)
12894 int xx = x + xy[i][0];
12895 int yy = y + xy[i][1];
12896 int border_element;
12898 border_element_old[i] = -1;
12900 if (!IN_LEV_FIELD(xx, yy))
12903 if (game.engine_version < VERSION_IDENT(3,0,7,0))
12904 border_element = Feld[xx][yy]; /* may be moving! */
12905 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
12906 border_element = Feld[xx][yy];
12907 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
12908 border_element = MovingOrBlocked2Element(xx, yy);
12910 continue; /* center and border element do not touch */
12912 border_element_old[i] = border_element;
12915 for (i = 0; i < NUM_DIRECTIONS; i++)
12917 int xx = x + xy[i][0];
12918 int yy = y + xy[i][1];
12919 int center_side = trigger_sides[i][0];
12920 int border_element = border_element_old[i];
12922 if (border_element == -1)
12925 /* check for change of border element */
12926 CheckElementChangeBySide(xx, yy, border_element, center_element,
12927 CE_TOUCHING_X, center_side);
12929 /* (center element cannot be player, so we dont have to check this here) */
12932 for (i = 0; i < NUM_DIRECTIONS; i++)
12934 int xx = x + xy[i][0];
12935 int yy = y + xy[i][1];
12936 int border_side = trigger_sides[i][1];
12937 int border_element = border_element_old[i];
12939 if (border_element == -1)
12942 /* check for change of center element (but change it only once) */
12943 if (!change_center_element)
12944 change_center_element =
12945 CheckElementChangeBySide(x, y, center_element, border_element,
12946 CE_TOUCHING_X, border_side);
12948 if (IS_PLAYER(xx, yy))
12950 /* use player element that is initially defined in the level playfield,
12951 not the player element that corresponds to the runtime player number
12952 (example: a level that contains EL_PLAYER_3 as the only player would
12953 incorrectly give EL_PLAYER_1 for "player->element_nr") */
12954 int player_element = PLAYERINFO(xx, yy)->initial_element;
12956 CheckElementChangeBySide(x, y, center_element, player_element,
12957 CE_TOUCHING_X, border_side);
12962 void TestIfElementHitsCustomElement(int x, int y, int direction)
12964 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
12965 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
12966 int hitx = x + dx, hity = y + dy;
12967 int hitting_element = Feld[x][y];
12968 int touched_element;
12970 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
12973 touched_element = (IN_LEV_FIELD(hitx, hity) ?
12974 MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
12976 if (IN_LEV_FIELD(hitx, hity))
12978 int opposite_direction = MV_DIR_OPPOSITE(direction);
12979 int hitting_side = direction;
12980 int touched_side = opposite_direction;
12981 boolean object_hit = (!IS_MOVING(hitx, hity) ||
12982 MovDir[hitx][hity] != direction ||
12983 ABS(MovPos[hitx][hity]) <= TILEY / 2);
12989 CheckElementChangeBySide(x, y, hitting_element, touched_element,
12990 CE_HITTING_X, touched_side);
12992 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
12993 CE_HIT_BY_X, hitting_side);
12995 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
12996 CE_HIT_BY_SOMETHING, opposite_direction);
12998 if (IS_PLAYER(hitx, hity))
13000 /* use player element that is initially defined in the level playfield,
13001 not the player element that corresponds to the runtime player number
13002 (example: a level that contains EL_PLAYER_3 as the only player would
13003 incorrectly give EL_PLAYER_1 for "player->element_nr") */
13004 int player_element = PLAYERINFO(hitx, hity)->initial_element;
13006 CheckElementChangeBySide(x, y, hitting_element, player_element,
13007 CE_HITTING_X, touched_side);
13012 /* "hitting something" is also true when hitting the playfield border */
13013 CheckElementChangeBySide(x, y, hitting_element, touched_element,
13014 CE_HITTING_SOMETHING, direction);
13017 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
13019 int i, kill_x = -1, kill_y = -1;
13021 int bad_element = -1;
13022 static int test_xy[4][2] =
13029 static int test_dir[4] =
13037 for (i = 0; i < NUM_DIRECTIONS; i++)
13039 int test_x, test_y, test_move_dir, test_element;
13041 test_x = good_x + test_xy[i][0];
13042 test_y = good_y + test_xy[i][1];
13044 if (!IN_LEV_FIELD(test_x, test_y))
13048 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13050 test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
13052 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
13053 2nd case: DONT_TOUCH style bad thing does not move away from good thing
13055 if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
13056 (DONT_TOUCH(test_element) && test_move_dir != test_dir[i]))
13060 bad_element = test_element;
13066 if (kill_x != -1 || kill_y != -1)
13068 if (IS_PLAYER(good_x, good_y))
13070 struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
13072 if (player->shield_deadly_time_left > 0 &&
13073 !IS_INDESTRUCTIBLE(bad_element))
13074 Bang(kill_x, kill_y);
13075 else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
13076 KillPlayer(player);
13079 Bang(good_x, good_y);
13083 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
13085 int i, kill_x = -1, kill_y = -1;
13086 int bad_element = Feld[bad_x][bad_y];
13087 static int test_xy[4][2] =
13094 static int touch_dir[4] =
13096 MV_LEFT | MV_RIGHT,
13101 static int test_dir[4] =
13109 if (bad_element == EL_EXPLOSION) /* skip just exploding bad things */
13112 for (i = 0; i < NUM_DIRECTIONS; i++)
13114 int test_x, test_y, test_move_dir, test_element;
13116 test_x = bad_x + test_xy[i][0];
13117 test_y = bad_y + test_xy[i][1];
13119 if (!IN_LEV_FIELD(test_x, test_y))
13123 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13125 test_element = Feld[test_x][test_y];
13127 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
13128 2nd case: DONT_TOUCH style bad thing does not move away from good thing
13130 if ((DONT_RUN_INTO(bad_element) && bad_move_dir == test_dir[i]) ||
13131 (DONT_TOUCH(bad_element) && test_move_dir != test_dir[i]))
13133 /* good thing is player or penguin that does not move away */
13134 if (IS_PLAYER(test_x, test_y))
13136 struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
13138 if (bad_element == EL_ROBOT && player->is_moving)
13139 continue; /* robot does not kill player if he is moving */
13141 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13143 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
13144 continue; /* center and border element do not touch */
13152 else if (test_element == EL_PENGUIN)
13162 if (kill_x != -1 || kill_y != -1)
13164 if (IS_PLAYER(kill_x, kill_y))
13166 struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
13168 if (player->shield_deadly_time_left > 0 &&
13169 !IS_INDESTRUCTIBLE(bad_element))
13170 Bang(bad_x, bad_y);
13171 else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
13172 KillPlayer(player);
13175 Bang(kill_x, kill_y);
13179 void TestIfGoodThingGetsHitByBadThing(int bad_x, int bad_y, int bad_move_dir)
13181 int bad_element = Feld[bad_x][bad_y];
13182 int dx = (bad_move_dir == MV_LEFT ? -1 : bad_move_dir == MV_RIGHT ? +1 : 0);
13183 int dy = (bad_move_dir == MV_UP ? -1 : bad_move_dir == MV_DOWN ? +1 : 0);
13184 int test_x = bad_x + dx, test_y = bad_y + dy;
13185 int test_move_dir, test_element;
13186 int kill_x = -1, kill_y = -1;
13188 if (!IN_LEV_FIELD(test_x, test_y))
13192 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13194 test_element = Feld[test_x][test_y];
13196 if (test_move_dir != bad_move_dir)
13198 /* good thing can be player or penguin that does not move away */
13199 if (IS_PLAYER(test_x, test_y))
13201 struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
13203 /* (note: in comparison to DONT_RUN_TO and DONT_TOUCH, also handle the
13204 player as being hit when he is moving towards the bad thing, because
13205 the "get hit by" condition would be lost after the player stops) */
13206 if (player->MovPos != 0 && player->MovDir == bad_move_dir)
13207 return; /* player moves away from bad thing */
13212 else if (test_element == EL_PENGUIN)
13219 if (kill_x != -1 || kill_y != -1)
13221 if (IS_PLAYER(kill_x, kill_y))
13223 struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
13225 if (player->shield_deadly_time_left > 0 &&
13226 !IS_INDESTRUCTIBLE(bad_element))
13227 Bang(bad_x, bad_y);
13228 else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
13229 KillPlayer(player);
13232 Bang(kill_x, kill_y);
13236 void TestIfPlayerTouchesBadThing(int x, int y)
13238 TestIfGoodThingHitsBadThing(x, y, MV_NONE);
13241 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
13243 TestIfGoodThingHitsBadThing(x, y, move_dir);
13246 void TestIfBadThingTouchesPlayer(int x, int y)
13248 TestIfBadThingHitsGoodThing(x, y, MV_NONE);
13251 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
13253 TestIfBadThingHitsGoodThing(x, y, move_dir);
13256 void TestIfFriendTouchesBadThing(int x, int y)
13258 TestIfGoodThingHitsBadThing(x, y, MV_NONE);
13261 void TestIfBadThingTouchesFriend(int x, int y)
13263 TestIfBadThingHitsGoodThing(x, y, MV_NONE);
13266 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
13268 int i, kill_x = bad_x, kill_y = bad_y;
13269 static int xy[4][2] =
13277 for (i = 0; i < NUM_DIRECTIONS; i++)
13281 x = bad_x + xy[i][0];
13282 y = bad_y + xy[i][1];
13283 if (!IN_LEV_FIELD(x, y))
13286 element = Feld[x][y];
13287 if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
13288 element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
13296 if (kill_x != bad_x || kill_y != bad_y)
13297 Bang(bad_x, bad_y);
13300 void KillPlayer(struct PlayerInfo *player)
13302 int jx = player->jx, jy = player->jy;
13304 if (!player->active)
13308 printf("::: 0: killed == %d, active == %d, reanimated == %d\n",
13309 player->killed, player->active, player->reanimated);
13312 /* the following code was introduced to prevent an infinite loop when calling
13314 -> CheckTriggeredElementChangeExt()
13315 -> ExecuteCustomElementAction()
13317 -> (infinitely repeating the above sequence of function calls)
13318 which occurs when killing the player while having a CE with the setting
13319 "kill player X when explosion of <player X>"; the solution using a new
13320 field "player->killed" was chosen for backwards compatibility, although
13321 clever use of the fields "player->active" etc. would probably also work */
13323 if (player->killed)
13327 player->killed = TRUE;
13329 /* remove accessible field at the player's position */
13330 Feld[jx][jy] = EL_EMPTY;
13332 /* deactivate shield (else Bang()/Explode() would not work right) */
13333 player->shield_normal_time_left = 0;
13334 player->shield_deadly_time_left = 0;
13337 printf("::: 1: killed == %d, active == %d, reanimated == %d\n",
13338 player->killed, player->active, player->reanimated);
13344 printf("::: 2: killed == %d, active == %d, reanimated == %d\n",
13345 player->killed, player->active, player->reanimated);
13348 if (player->reanimated) /* killed player may have been reanimated */
13349 player->killed = player->reanimated = FALSE;
13351 BuryPlayer(player);
13354 static void KillPlayerUnlessEnemyProtected(int x, int y)
13356 if (!PLAYER_ENEMY_PROTECTED(x, y))
13357 KillPlayer(PLAYERINFO(x, y));
13360 static void KillPlayerUnlessExplosionProtected(int x, int y)
13362 if (!PLAYER_EXPLOSION_PROTECTED(x, y))
13363 KillPlayer(PLAYERINFO(x, y));
13366 void BuryPlayer(struct PlayerInfo *player)
13368 int jx = player->jx, jy = player->jy;
13370 if (!player->active)
13373 PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
13374 PlayLevelSound(jx, jy, SND_GAME_LOSING);
13376 player->GameOver = TRUE;
13377 RemovePlayer(player);
13380 void RemovePlayer(struct PlayerInfo *player)
13382 int jx = player->jx, jy = player->jy;
13383 int i, found = FALSE;
13385 player->present = FALSE;
13386 player->active = FALSE;
13388 if (!ExplodeField[jx][jy])
13389 StorePlayer[jx][jy] = 0;
13391 if (player->is_moving)
13392 TEST_DrawLevelField(player->last_jx, player->last_jy);
13394 for (i = 0; i < MAX_PLAYERS; i++)
13395 if (stored_player[i].active)
13399 AllPlayersGone = TRUE;
13405 static void setFieldForSnapping(int x, int y, int element, int direction)
13407 struct ElementInfo *ei = &element_info[element];
13408 int direction_bit = MV_DIR_TO_BIT(direction);
13409 int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
13410 int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
13411 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
13413 Feld[x][y] = EL_ELEMENT_SNAPPING;
13414 MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
13416 ResetGfxAnimation(x, y);
13418 GfxElement[x][y] = element;
13419 GfxAction[x][y] = action;
13420 GfxDir[x][y] = direction;
13421 GfxFrame[x][y] = -1;
13425 =============================================================================
13426 checkDiagonalPushing()
13427 -----------------------------------------------------------------------------
13428 check if diagonal input device direction results in pushing of object
13429 (by checking if the alternative direction is walkable, diggable, ...)
13430 =============================================================================
13433 static boolean checkDiagonalPushing(struct PlayerInfo *player,
13434 int x, int y, int real_dx, int real_dy)
13436 int jx, jy, dx, dy, xx, yy;
13438 if (real_dx == 0 || real_dy == 0) /* no diagonal direction => push */
13441 /* diagonal direction: check alternative direction */
13446 xx = jx + (dx == 0 ? real_dx : 0);
13447 yy = jy + (dy == 0 ? real_dy : 0);
13449 return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
13453 =============================================================================
13455 -----------------------------------------------------------------------------
13456 x, y: field next to player (non-diagonal) to try to dig to
13457 real_dx, real_dy: direction as read from input device (can be diagonal)
13458 =============================================================================
13461 static int DigField(struct PlayerInfo *player,
13462 int oldx, int oldy, int x, int y,
13463 int real_dx, int real_dy, int mode)
13465 boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
13466 boolean player_was_pushing = player->is_pushing;
13467 boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
13468 boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
13469 int jx = oldx, jy = oldy;
13470 int dx = x - jx, dy = y - jy;
13471 int nextx = x + dx, nexty = y + dy;
13472 int move_direction = (dx == -1 ? MV_LEFT :
13473 dx == +1 ? MV_RIGHT :
13475 dy == +1 ? MV_DOWN : MV_NONE);
13476 int opposite_direction = MV_DIR_OPPOSITE(move_direction);
13477 int dig_side = MV_DIR_OPPOSITE(move_direction);
13478 int old_element = Feld[jx][jy];
13479 int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
13482 if (is_player) /* function can also be called by EL_PENGUIN */
13484 if (player->MovPos == 0)
13486 player->is_digging = FALSE;
13487 player->is_collecting = FALSE;
13490 if (player->MovPos == 0) /* last pushing move finished */
13491 player->is_pushing = FALSE;
13493 if (mode == DF_NO_PUSH) /* player just stopped pushing */
13495 player->is_switching = FALSE;
13496 player->push_delay = -1;
13498 return MP_NO_ACTION;
13502 if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
13503 old_element = Back[jx][jy];
13505 /* in case of element dropped at player position, check background */
13506 else if (Back[jx][jy] != EL_EMPTY &&
13507 game.engine_version >= VERSION_IDENT(2,2,0,0))
13508 old_element = Back[jx][jy];
13510 if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
13511 return MP_NO_ACTION; /* field has no opening in this direction */
13513 if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
13514 return MP_NO_ACTION; /* field has no opening in this direction */
13516 if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
13520 Feld[jx][jy] = player->artwork_element;
13521 InitMovingField(jx, jy, MV_DOWN);
13522 Store[jx][jy] = EL_ACID;
13523 ContinueMoving(jx, jy);
13524 BuryPlayer(player);
13526 return MP_DONT_RUN_INTO;
13529 if (player_can_move && DONT_RUN_INTO(element))
13531 TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
13533 return MP_DONT_RUN_INTO;
13536 if (IS_MOVING(x, y) || IS_PLAYER(x, y))
13537 return MP_NO_ACTION;
13539 collect_count = element_info[element].collect_count_initial;
13541 if (!is_player && !IS_COLLECTIBLE(element)) /* penguin cannot collect it */
13542 return MP_NO_ACTION;
13544 if (game.engine_version < VERSION_IDENT(2,2,0,0))
13545 player_can_move = player_can_move_or_snap;
13547 if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
13548 game.engine_version >= VERSION_IDENT(2,2,0,0))
13550 CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
13551 player->index_bit, dig_side);
13552 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13553 player->index_bit, dig_side);
13555 if (element == EL_DC_LANDMINE)
13558 if (Feld[x][y] != element) /* field changed by snapping */
13561 return MP_NO_ACTION;
13564 if (player->gravity && is_player && !player->is_auto_moving &&
13565 canFallDown(player) && move_direction != MV_DOWN &&
13566 !canMoveToValidFieldWithGravity(jx, jy, move_direction))
13567 return MP_NO_ACTION; /* player cannot walk here due to gravity */
13569 if (player_can_move &&
13570 IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
13572 int sound_element = SND_ELEMENT(element);
13573 int sound_action = ACTION_WALKING;
13575 if (IS_RND_GATE(element))
13577 if (!player->key[RND_GATE_NR(element)])
13578 return MP_NO_ACTION;
13580 else if (IS_RND_GATE_GRAY(element))
13582 if (!player->key[RND_GATE_GRAY_NR(element)])
13583 return MP_NO_ACTION;
13585 else if (IS_RND_GATE_GRAY_ACTIVE(element))
13587 if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
13588 return MP_NO_ACTION;
13590 else if (element == EL_EXIT_OPEN ||
13591 element == EL_EM_EXIT_OPEN ||
13592 element == EL_EM_EXIT_OPENING ||
13593 element == EL_STEEL_EXIT_OPEN ||
13594 element == EL_EM_STEEL_EXIT_OPEN ||
13595 element == EL_EM_STEEL_EXIT_OPENING ||
13596 element == EL_SP_EXIT_OPEN ||
13597 element == EL_SP_EXIT_OPENING)
13599 sound_action = ACTION_PASSING; /* player is passing exit */
13601 else if (element == EL_EMPTY)
13603 sound_action = ACTION_MOVING; /* nothing to walk on */
13606 /* play sound from background or player, whatever is available */
13607 if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
13608 PlayLevelSoundElementAction(x, y, sound_element, sound_action);
13610 PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
13612 else if (player_can_move &&
13613 IS_PASSABLE(element) && canPassField(x, y, move_direction))
13615 if (!ACCESS_FROM(element, opposite_direction))
13616 return MP_NO_ACTION; /* field not accessible from this direction */
13618 if (CAN_MOVE(element)) /* only fixed elements can be passed! */
13619 return MP_NO_ACTION;
13621 if (IS_EM_GATE(element))
13623 if (!player->key[EM_GATE_NR(element)])
13624 return MP_NO_ACTION;
13626 else if (IS_EM_GATE_GRAY(element))
13628 if (!player->key[EM_GATE_GRAY_NR(element)])
13629 return MP_NO_ACTION;
13631 else if (IS_EM_GATE_GRAY_ACTIVE(element))
13633 if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
13634 return MP_NO_ACTION;
13636 else if (IS_EMC_GATE(element))
13638 if (!player->key[EMC_GATE_NR(element)])
13639 return MP_NO_ACTION;
13641 else if (IS_EMC_GATE_GRAY(element))
13643 if (!player->key[EMC_GATE_GRAY_NR(element)])
13644 return MP_NO_ACTION;
13646 else if (IS_EMC_GATE_GRAY_ACTIVE(element))
13648 if (!player->key[EMC_GATE_GRAY_ACTIVE_NR(element)])
13649 return MP_NO_ACTION;
13651 else if (element == EL_DC_GATE_WHITE ||
13652 element == EL_DC_GATE_WHITE_GRAY ||
13653 element == EL_DC_GATE_WHITE_GRAY_ACTIVE)
13655 if (player->num_white_keys == 0)
13656 return MP_NO_ACTION;
13658 player->num_white_keys--;
13660 else if (IS_SP_PORT(element))
13662 if (element == EL_SP_GRAVITY_PORT_LEFT ||
13663 element == EL_SP_GRAVITY_PORT_RIGHT ||
13664 element == EL_SP_GRAVITY_PORT_UP ||
13665 element == EL_SP_GRAVITY_PORT_DOWN)
13666 player->gravity = !player->gravity;
13667 else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
13668 element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
13669 element == EL_SP_GRAVITY_ON_PORT_UP ||
13670 element == EL_SP_GRAVITY_ON_PORT_DOWN)
13671 player->gravity = TRUE;
13672 else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
13673 element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
13674 element == EL_SP_GRAVITY_OFF_PORT_UP ||
13675 element == EL_SP_GRAVITY_OFF_PORT_DOWN)
13676 player->gravity = FALSE;
13679 /* automatically move to the next field with double speed */
13680 player->programmed_action = move_direction;
13682 if (player->move_delay_reset_counter == 0)
13684 player->move_delay_reset_counter = 2; /* two double speed steps */
13686 DOUBLE_PLAYER_SPEED(player);
13689 PlayLevelSoundAction(x, y, ACTION_PASSING);
13691 else if (player_can_move_or_snap && IS_DIGGABLE(element))
13695 if (mode != DF_SNAP)
13697 GfxElement[x][y] = GFX_ELEMENT(element);
13698 player->is_digging = TRUE;
13701 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
13703 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
13704 player->index_bit, dig_side);
13706 if (mode == DF_SNAP)
13708 if (level.block_snap_field)
13709 setFieldForSnapping(x, y, element, move_direction);
13711 TestIfElementTouchesCustomElement(x, y); /* for empty space */
13713 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13714 player->index_bit, dig_side);
13717 else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
13721 if (is_player && mode != DF_SNAP)
13723 GfxElement[x][y] = element;
13724 player->is_collecting = TRUE;
13727 if (element == EL_SPEED_PILL)
13729 player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
13731 else if (element == EL_EXTRA_TIME && level.time > 0)
13733 TimeLeft += level.extra_time;
13735 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
13737 DisplayGameControlValues();
13739 else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
13741 player->shield_normal_time_left += level.shield_normal_time;
13742 if (element == EL_SHIELD_DEADLY)
13743 player->shield_deadly_time_left += level.shield_deadly_time;
13745 else if (element == EL_DYNAMITE ||
13746 element == EL_EM_DYNAMITE ||
13747 element == EL_SP_DISK_RED)
13749 if (player->inventory_size < MAX_INVENTORY_SIZE)
13750 player->inventory_element[player->inventory_size++] = element;
13752 DrawGameDoorValues();
13754 else if (element == EL_DYNABOMB_INCREASE_NUMBER)
13756 player->dynabomb_count++;
13757 player->dynabombs_left++;
13759 else if (element == EL_DYNABOMB_INCREASE_SIZE)
13761 player->dynabomb_size++;
13763 else if (element == EL_DYNABOMB_INCREASE_POWER)
13765 player->dynabomb_xl = TRUE;
13767 else if (IS_KEY(element))
13769 player->key[KEY_NR(element)] = TRUE;
13771 DrawGameDoorValues();
13773 else if (element == EL_DC_KEY_WHITE)
13775 player->num_white_keys++;
13777 /* display white keys? */
13778 /* DrawGameDoorValues(); */
13780 else if (IS_ENVELOPE(element))
13782 player->show_envelope = element;
13784 else if (element == EL_EMC_LENSES)
13786 game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
13788 RedrawAllInvisibleElementsForLenses();
13790 else if (element == EL_EMC_MAGNIFIER)
13792 game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
13794 RedrawAllInvisibleElementsForMagnifier();
13796 else if (IS_DROPPABLE(element) ||
13797 IS_THROWABLE(element)) /* can be collected and dropped */
13801 if (collect_count == 0)
13802 player->inventory_infinite_element = element;
13804 for (i = 0; i < collect_count; i++)
13805 if (player->inventory_size < MAX_INVENTORY_SIZE)
13806 player->inventory_element[player->inventory_size++] = element;
13808 DrawGameDoorValues();
13810 else if (collect_count > 0)
13812 local_player->gems_still_needed -= collect_count;
13813 if (local_player->gems_still_needed < 0)
13814 local_player->gems_still_needed = 0;
13816 game.snapshot.collected_item = TRUE;
13818 game_panel_controls[GAME_PANEL_GEMS].value = local_player->gems_still_needed;
13820 DisplayGameControlValues();
13823 RaiseScoreElement(element);
13824 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
13827 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
13828 player->index_bit, dig_side);
13830 if (mode == DF_SNAP)
13832 if (level.block_snap_field)
13833 setFieldForSnapping(x, y, element, move_direction);
13835 TestIfElementTouchesCustomElement(x, y); /* for empty space */
13837 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13838 player->index_bit, dig_side);
13841 else if (player_can_move_or_snap && IS_PUSHABLE(element))
13843 if (mode == DF_SNAP && element != EL_BD_ROCK)
13844 return MP_NO_ACTION;
13846 if (CAN_FALL(element) && dy)
13847 return MP_NO_ACTION;
13849 if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
13850 !(element == EL_SPRING && level.use_spring_bug))
13851 return MP_NO_ACTION;
13853 if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
13854 ((move_direction & MV_VERTICAL &&
13855 ((element_info[element].move_pattern & MV_LEFT &&
13856 IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
13857 (element_info[element].move_pattern & MV_RIGHT &&
13858 IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
13859 (move_direction & MV_HORIZONTAL &&
13860 ((element_info[element].move_pattern & MV_UP &&
13861 IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
13862 (element_info[element].move_pattern & MV_DOWN &&
13863 IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
13864 return MP_NO_ACTION;
13866 /* do not push elements already moving away faster than player */
13867 if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
13868 ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
13869 return MP_NO_ACTION;
13871 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
13873 if (player->push_delay_value == -1 || !player_was_pushing)
13874 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13876 else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
13878 if (player->push_delay_value == -1)
13879 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13881 else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
13883 if (!player->is_pushing)
13884 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13887 player->is_pushing = TRUE;
13888 player->is_active = TRUE;
13890 if (!(IN_LEV_FIELD(nextx, nexty) &&
13891 (IS_FREE(nextx, nexty) ||
13892 (IS_SB_ELEMENT(element) &&
13893 Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY) ||
13894 (IS_CUSTOM_ELEMENT(element) &&
13895 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty)))))
13896 return MP_NO_ACTION;
13898 if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
13899 return MP_NO_ACTION;
13901 if (player->push_delay == -1) /* new pushing; restart delay */
13902 player->push_delay = 0;
13904 if (player->push_delay < player->push_delay_value &&
13905 !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
13906 element != EL_SPRING && element != EL_BALLOON)
13908 /* make sure that there is no move delay before next try to push */
13909 if (game.engine_version >= VERSION_IDENT(3,0,7,1))
13910 player->move_delay = 0;
13912 return MP_NO_ACTION;
13915 if (IS_CUSTOM_ELEMENT(element) &&
13916 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty))
13918 if (!DigFieldByCE(nextx, nexty, element))
13919 return MP_NO_ACTION;
13922 if (IS_SB_ELEMENT(element))
13924 if (element == EL_SOKOBAN_FIELD_FULL)
13926 Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
13927 local_player->sokobanfields_still_needed++;
13930 if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
13932 Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
13933 local_player->sokobanfields_still_needed--;
13936 Feld[x][y] = EL_SOKOBAN_OBJECT;
13938 if (Back[x][y] == Back[nextx][nexty])
13939 PlayLevelSoundAction(x, y, ACTION_PUSHING);
13940 else if (Back[x][y] != 0)
13941 PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
13944 PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
13947 if (local_player->sokobanfields_still_needed == 0 &&
13948 (game.emulation == EMU_SOKOBAN || level.auto_exit_sokoban))
13950 PlayerWins(player);
13952 PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
13956 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
13958 InitMovingField(x, y, move_direction);
13959 GfxAction[x][y] = ACTION_PUSHING;
13961 if (mode == DF_SNAP)
13962 ContinueMoving(x, y);
13964 MovPos[x][y] = (dx != 0 ? dx : dy);
13966 Pushed[x][y] = TRUE;
13967 Pushed[nextx][nexty] = TRUE;
13969 if (game.engine_version < VERSION_IDENT(2,2,0,7))
13970 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13972 player->push_delay_value = -1; /* get new value later */
13974 /* check for element change _after_ element has been pushed */
13975 if (game.use_change_when_pushing_bug)
13977 CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
13978 player->index_bit, dig_side);
13979 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
13980 player->index_bit, dig_side);
13983 else if (IS_SWITCHABLE(element))
13985 if (PLAYER_SWITCHING(player, x, y))
13987 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
13988 player->index_bit, dig_side);
13993 player->is_switching = TRUE;
13994 player->switch_x = x;
13995 player->switch_y = y;
13997 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
13999 if (element == EL_ROBOT_WHEEL)
14001 Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
14005 game.robot_wheel_active = TRUE;
14007 TEST_DrawLevelField(x, y);
14009 else if (element == EL_SP_TERMINAL)
14013 SCAN_PLAYFIELD(xx, yy)
14015 if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
14019 else if (Feld[xx][yy] == EL_SP_TERMINAL)
14021 Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
14023 ResetGfxAnimation(xx, yy);
14024 TEST_DrawLevelField(xx, yy);
14028 else if (IS_BELT_SWITCH(element))
14030 ToggleBeltSwitch(x, y);
14032 else if (element == EL_SWITCHGATE_SWITCH_UP ||
14033 element == EL_SWITCHGATE_SWITCH_DOWN ||
14034 element == EL_DC_SWITCHGATE_SWITCH_UP ||
14035 element == EL_DC_SWITCHGATE_SWITCH_DOWN)
14037 ToggleSwitchgateSwitch(x, y);
14039 else if (element == EL_LIGHT_SWITCH ||
14040 element == EL_LIGHT_SWITCH_ACTIVE)
14042 ToggleLightSwitch(x, y);
14044 else if (element == EL_TIMEGATE_SWITCH ||
14045 element == EL_DC_TIMEGATE_SWITCH)
14047 ActivateTimegateSwitch(x, y);
14049 else if (element == EL_BALLOON_SWITCH_LEFT ||
14050 element == EL_BALLOON_SWITCH_RIGHT ||
14051 element == EL_BALLOON_SWITCH_UP ||
14052 element == EL_BALLOON_SWITCH_DOWN ||
14053 element == EL_BALLOON_SWITCH_NONE ||
14054 element == EL_BALLOON_SWITCH_ANY)
14056 game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT ? MV_LEFT :
14057 element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
14058 element == EL_BALLOON_SWITCH_UP ? MV_UP :
14059 element == EL_BALLOON_SWITCH_DOWN ? MV_DOWN :
14060 element == EL_BALLOON_SWITCH_NONE ? MV_NONE :
14063 else if (element == EL_LAMP)
14065 Feld[x][y] = EL_LAMP_ACTIVE;
14066 local_player->lights_still_needed--;
14068 ResetGfxAnimation(x, y);
14069 TEST_DrawLevelField(x, y);
14071 else if (element == EL_TIME_ORB_FULL)
14073 Feld[x][y] = EL_TIME_ORB_EMPTY;
14075 if (level.time > 0 || level.use_time_orb_bug)
14077 TimeLeft += level.time_orb_time;
14078 game.no_time_limit = FALSE;
14080 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
14082 DisplayGameControlValues();
14085 ResetGfxAnimation(x, y);
14086 TEST_DrawLevelField(x, y);
14088 else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
14089 element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
14093 game.ball_state = !game.ball_state;
14095 SCAN_PLAYFIELD(xx, yy)
14097 int e = Feld[xx][yy];
14099 if (game.ball_state)
14101 if (e == EL_EMC_MAGIC_BALL)
14102 CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
14103 else if (e == EL_EMC_MAGIC_BALL_SWITCH)
14104 CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
14108 if (e == EL_EMC_MAGIC_BALL_ACTIVE)
14109 CreateField(xx, yy, EL_EMC_MAGIC_BALL);
14110 else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
14111 CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
14116 CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
14117 player->index_bit, dig_side);
14119 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
14120 player->index_bit, dig_side);
14122 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14123 player->index_bit, dig_side);
14129 if (!PLAYER_SWITCHING(player, x, y))
14131 player->is_switching = TRUE;
14132 player->switch_x = x;
14133 player->switch_y = y;
14135 CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
14136 player->index_bit, dig_side);
14137 CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
14138 player->index_bit, dig_side);
14140 CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
14141 player->index_bit, dig_side);
14142 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
14143 player->index_bit, dig_side);
14146 CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
14147 player->index_bit, dig_side);
14148 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14149 player->index_bit, dig_side);
14151 return MP_NO_ACTION;
14154 player->push_delay = -1;
14156 if (is_player) /* function can also be called by EL_PENGUIN */
14158 if (Feld[x][y] != element) /* really digged/collected something */
14160 player->is_collecting = !player->is_digging;
14161 player->is_active = TRUE;
14168 static boolean DigFieldByCE(int x, int y, int digging_element)
14170 int element = Feld[x][y];
14172 if (!IS_FREE(x, y))
14174 int action = (IS_DIGGABLE(element) ? ACTION_DIGGING :
14175 IS_COLLECTIBLE(element) ? ACTION_COLLECTING :
14178 /* no element can dig solid indestructible elements */
14179 if (IS_INDESTRUCTIBLE(element) &&
14180 !IS_DIGGABLE(element) &&
14181 !IS_COLLECTIBLE(element))
14184 if (AmoebaNr[x][y] &&
14185 (element == EL_AMOEBA_FULL ||
14186 element == EL_BD_AMOEBA ||
14187 element == EL_AMOEBA_GROWING))
14189 AmoebaCnt[AmoebaNr[x][y]]--;
14190 AmoebaCnt2[AmoebaNr[x][y]]--;
14193 if (IS_MOVING(x, y))
14194 RemoveMovingField(x, y);
14198 TEST_DrawLevelField(x, y);
14201 /* if digged element was about to explode, prevent the explosion */
14202 ExplodeField[x][y] = EX_TYPE_NONE;
14204 PlayLevelSoundAction(x, y, action);
14207 Store[x][y] = EL_EMPTY;
14209 /* this makes it possible to leave the removed element again */
14210 if (IS_EQUAL_OR_IN_GROUP(element, MOVE_ENTER_EL(digging_element)))
14211 Store[x][y] = element;
14216 static boolean SnapField(struct PlayerInfo *player, int dx, int dy)
14218 int jx = player->jx, jy = player->jy;
14219 int x = jx + dx, y = jy + dy;
14220 int snap_direction = (dx == -1 ? MV_LEFT :
14221 dx == +1 ? MV_RIGHT :
14223 dy == +1 ? MV_DOWN : MV_NONE);
14224 boolean can_continue_snapping = (level.continuous_snapping &&
14225 WasJustFalling[x][y] < CHECK_DELAY_FALLING);
14227 if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
14230 if (!player->active || !IN_LEV_FIELD(x, y))
14238 if (player->MovPos == 0)
14239 player->is_pushing = FALSE;
14241 player->is_snapping = FALSE;
14243 if (player->MovPos == 0)
14245 player->is_moving = FALSE;
14246 player->is_digging = FALSE;
14247 player->is_collecting = FALSE;
14253 /* prevent snapping with already pressed snap key when not allowed */
14254 if (player->is_snapping && !can_continue_snapping)
14257 player->MovDir = snap_direction;
14259 if (player->MovPos == 0)
14261 player->is_moving = FALSE;
14262 player->is_digging = FALSE;
14263 player->is_collecting = FALSE;
14266 player->is_dropping = FALSE;
14267 player->is_dropping_pressed = FALSE;
14268 player->drop_pressed_delay = 0;
14270 if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
14273 player->is_snapping = TRUE;
14274 player->is_active = TRUE;
14276 if (player->MovPos == 0)
14278 player->is_moving = FALSE;
14279 player->is_digging = FALSE;
14280 player->is_collecting = FALSE;
14283 if (player->MovPos != 0) /* prevent graphic bugs in versions < 2.2.0 */
14284 TEST_DrawLevelField(player->last_jx, player->last_jy);
14286 TEST_DrawLevelField(x, y);
14291 static boolean DropElement(struct PlayerInfo *player)
14293 int old_element, new_element;
14294 int dropx = player->jx, dropy = player->jy;
14295 int drop_direction = player->MovDir;
14296 int drop_side = drop_direction;
14297 int drop_element = get_next_dropped_element(player);
14299 /* do not drop an element on top of another element; when holding drop key
14300 pressed without moving, dropped element must move away before the next
14301 element can be dropped (this is especially important if the next element
14302 is dynamite, which can be placed on background for historical reasons) */
14303 if (PLAYER_DROPPING(player, dropx, dropy) && Feld[dropx][dropy] != EL_EMPTY)
14306 if (IS_THROWABLE(drop_element))
14308 dropx += GET_DX_FROM_DIR(drop_direction);
14309 dropy += GET_DY_FROM_DIR(drop_direction);
14311 if (!IN_LEV_FIELD(dropx, dropy))
14315 old_element = Feld[dropx][dropy]; /* old element at dropping position */
14316 new_element = drop_element; /* default: no change when dropping */
14318 /* check if player is active, not moving and ready to drop */
14319 if (!player->active || player->MovPos || player->drop_delay > 0)
14322 /* check if player has anything that can be dropped */
14323 if (new_element == EL_UNDEFINED)
14326 /* only set if player has anything that can be dropped */
14327 player->is_dropping_pressed = TRUE;
14329 /* check if drop key was pressed long enough for EM style dynamite */
14330 if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
14333 /* check if anything can be dropped at the current position */
14334 if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
14337 /* collected custom elements can only be dropped on empty fields */
14338 if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
14341 if (old_element != EL_EMPTY)
14342 Back[dropx][dropy] = old_element; /* store old element on this field */
14344 ResetGfxAnimation(dropx, dropy);
14345 ResetRandomAnimationValue(dropx, dropy);
14347 if (player->inventory_size > 0 ||
14348 player->inventory_infinite_element != EL_UNDEFINED)
14350 if (player->inventory_size > 0)
14352 player->inventory_size--;
14354 DrawGameDoorValues();
14356 if (new_element == EL_DYNAMITE)
14357 new_element = EL_DYNAMITE_ACTIVE;
14358 else if (new_element == EL_EM_DYNAMITE)
14359 new_element = EL_EM_DYNAMITE_ACTIVE;
14360 else if (new_element == EL_SP_DISK_RED)
14361 new_element = EL_SP_DISK_RED_ACTIVE;
14364 Feld[dropx][dropy] = new_element;
14366 if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
14367 DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
14368 el2img(Feld[dropx][dropy]), 0);
14370 PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
14372 /* needed if previous element just changed to "empty" in the last frame */
14373 ChangeCount[dropx][dropy] = 0; /* allow at least one more change */
14375 CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
14376 player->index_bit, drop_side);
14377 CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
14379 player->index_bit, drop_side);
14381 TestIfElementTouchesCustomElement(dropx, dropy);
14383 else /* player is dropping a dyna bomb */
14385 player->dynabombs_left--;
14387 Feld[dropx][dropy] = new_element;
14389 if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
14390 DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
14391 el2img(Feld[dropx][dropy]), 0);
14393 PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
14396 if (Feld[dropx][dropy] == new_element) /* uninitialized unless CE change */
14397 InitField_WithBug1(dropx, dropy, FALSE);
14399 new_element = Feld[dropx][dropy]; /* element might have changed */
14401 if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
14402 element_info[new_element].move_pattern == MV_WHEN_DROPPED)
14404 if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
14405 MovDir[dropx][dropy] = drop_direction;
14407 ChangeCount[dropx][dropy] = 0; /* allow at least one more change */
14409 /* do not cause impact style collision by dropping elements that can fall */
14410 CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
14413 player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
14414 player->is_dropping = TRUE;
14416 player->drop_pressed_delay = 0;
14417 player->is_dropping_pressed = FALSE;
14419 player->drop_x = dropx;
14420 player->drop_y = dropy;
14425 /* ------------------------------------------------------------------------- */
14426 /* game sound playing functions */
14427 /* ------------------------------------------------------------------------- */
14429 static int *loop_sound_frame = NULL;
14430 static int *loop_sound_volume = NULL;
14432 void InitPlayLevelSound()
14434 int num_sounds = getSoundListSize();
14436 checked_free(loop_sound_frame);
14437 checked_free(loop_sound_volume);
14439 loop_sound_frame = checked_calloc(num_sounds * sizeof(int));
14440 loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
14443 static void PlayLevelSound(int x, int y, int nr)
14445 int sx = SCREENX(x), sy = SCREENY(y);
14446 int volume, stereo_position;
14447 int max_distance = 8;
14448 int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
14450 if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
14451 (!setup.sound_loops && IS_LOOP_SOUND(nr)))
14454 if (!IN_LEV_FIELD(x, y) ||
14455 sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
14456 sy < -max_distance || sy >= SCR_FIELDY + max_distance)
14459 volume = SOUND_MAX_VOLUME;
14461 if (!IN_SCR_FIELD(sx, sy))
14463 int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
14464 int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
14466 volume -= volume * (dx > dy ? dx : dy) / max_distance;
14469 stereo_position = (SOUND_MAX_LEFT +
14470 (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
14471 (SCR_FIELDX + 2 * max_distance));
14473 if (IS_LOOP_SOUND(nr))
14475 /* This assures that quieter loop sounds do not overwrite louder ones,
14476 while restarting sound volume comparison with each new game frame. */
14478 if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
14481 loop_sound_volume[nr] = volume;
14482 loop_sound_frame[nr] = FrameCounter;
14485 PlaySoundExt(nr, volume, stereo_position, type);
14488 static void PlayLevelSoundNearest(int x, int y, int sound_action)
14490 PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
14491 x > LEVELX(BX2) ? LEVELX(BX2) : x,
14492 y < LEVELY(BY1) ? LEVELY(BY1) :
14493 y > LEVELY(BY2) ? LEVELY(BY2) : y,
14497 static void PlayLevelSoundAction(int x, int y, int action)
14499 PlayLevelSoundElementAction(x, y, Feld[x][y], action);
14502 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
14504 int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
14506 if (sound_effect != SND_UNDEFINED)
14507 PlayLevelSound(x, y, sound_effect);
14510 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
14513 int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
14515 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14516 PlayLevelSound(x, y, sound_effect);
14519 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
14521 int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
14523 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14524 PlayLevelSound(x, y, sound_effect);
14527 static void StopLevelSoundActionIfLoop(int x, int y, int action)
14529 int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
14531 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14532 StopSound(sound_effect);
14535 static int getLevelMusicNr()
14537 if (levelset.music[level_nr] != MUS_UNDEFINED)
14538 return levelset.music[level_nr]; /* from config file */
14540 return MAP_NOCONF_MUSIC(level_nr); /* from music dir */
14543 static void FadeLevelSounds()
14548 static void FadeLevelMusic()
14550 int music_nr = getLevelMusicNr();
14551 char *curr_music = getCurrentlyPlayingMusicFilename();
14552 char *next_music = getMusicInfoEntryFilename(music_nr);
14554 if (!strEqual(curr_music, next_music))
14558 void FadeLevelSoundsAndMusic()
14564 static void PlayLevelMusic()
14566 int music_nr = getLevelMusicNr();
14567 char *curr_music = getCurrentlyPlayingMusicFilename();
14568 char *next_music = getMusicInfoEntryFilename(music_nr);
14570 if (!strEqual(curr_music, next_music))
14571 PlayMusic(music_nr);
14574 void PlayLevelSound_EM(int xx, int yy, int element_em, int sample)
14576 int element = (element_em > -1 ? map_element_EM_to_RND(element_em) : 0);
14577 int offset = (BorderElement == EL_STEELWALL ? 1 : 0);
14578 int x = xx - 1 - offset;
14579 int y = yy - 1 - offset;
14584 PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
14588 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14592 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14596 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14600 PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
14604 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14608 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14611 case SAMPLE_android_clone:
14612 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14615 case SAMPLE_android_move:
14616 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14619 case SAMPLE_spring:
14620 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14624 PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
14628 PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
14631 case SAMPLE_eater_eat:
14632 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14636 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14639 case SAMPLE_collect:
14640 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
14643 case SAMPLE_diamond:
14644 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14647 case SAMPLE_squash:
14648 /* !!! CHECK THIS !!! */
14650 PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
14652 PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
14656 case SAMPLE_wonderfall:
14657 PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
14661 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14665 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14669 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14673 PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
14677 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14681 PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
14684 case SAMPLE_wonder:
14685 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14689 PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
14692 case SAMPLE_exit_open:
14693 PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
14696 case SAMPLE_exit_leave:
14697 PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
14700 case SAMPLE_dynamite:
14701 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14705 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14709 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
14713 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14717 PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
14721 PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
14725 PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
14729 PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
14734 void PlayLevelSound_SP(int xx, int yy, int element_sp, int action_sp)
14736 int element = map_element_SP_to_RND(element_sp);
14737 int action = map_action_SP_to_RND(action_sp);
14738 int offset = (setup.sp_show_border_elements ? 0 : 1);
14739 int x = xx - offset;
14740 int y = yy - offset;
14742 PlayLevelSoundElementAction(x, y, element, action);
14745 void PlayLevelSound_MM(int xx, int yy, int element_mm, int action_mm)
14747 int element = map_element_MM_to_RND(element_mm);
14748 int action = map_action_MM_to_RND(action_mm);
14750 int x = xx - offset;
14751 int y = yy - offset;
14753 if (!IS_MM_ELEMENT(element))
14754 element = EL_MM_DEFAULT;
14756 PlayLevelSoundElementAction(x, y, element, action);
14759 void PlaySound_MM(int sound_mm)
14761 int sound = map_sound_MM_to_RND(sound_mm);
14763 if (sound == SND_UNDEFINED)
14769 void PlaySoundLoop_MM(int sound_mm)
14771 int sound = map_sound_MM_to_RND(sound_mm);
14773 if (sound == SND_UNDEFINED)
14776 PlaySoundLoop(sound);
14779 void StopSound_MM(int sound_mm)
14781 int sound = map_sound_MM_to_RND(sound_mm);
14783 if (sound == SND_UNDEFINED)
14789 void RaiseScore(int value)
14791 local_player->score += value;
14793 game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
14795 DisplayGameControlValues();
14798 void RaiseScoreElement(int element)
14803 case EL_BD_DIAMOND:
14804 case EL_EMERALD_YELLOW:
14805 case EL_EMERALD_RED:
14806 case EL_EMERALD_PURPLE:
14807 case EL_SP_INFOTRON:
14808 RaiseScore(level.score[SC_EMERALD]);
14811 RaiseScore(level.score[SC_DIAMOND]);
14814 RaiseScore(level.score[SC_CRYSTAL]);
14817 RaiseScore(level.score[SC_PEARL]);
14820 case EL_BD_BUTTERFLY:
14821 case EL_SP_ELECTRON:
14822 RaiseScore(level.score[SC_BUG]);
14825 case EL_BD_FIREFLY:
14826 case EL_SP_SNIKSNAK:
14827 RaiseScore(level.score[SC_SPACESHIP]);
14830 case EL_DARK_YAMYAM:
14831 RaiseScore(level.score[SC_YAMYAM]);
14834 RaiseScore(level.score[SC_ROBOT]);
14837 RaiseScore(level.score[SC_PACMAN]);
14840 RaiseScore(level.score[SC_NUT]);
14843 case EL_EM_DYNAMITE:
14844 case EL_SP_DISK_RED:
14845 case EL_DYNABOMB_INCREASE_NUMBER:
14846 case EL_DYNABOMB_INCREASE_SIZE:
14847 case EL_DYNABOMB_INCREASE_POWER:
14848 RaiseScore(level.score[SC_DYNAMITE]);
14850 case EL_SHIELD_NORMAL:
14851 case EL_SHIELD_DEADLY:
14852 RaiseScore(level.score[SC_SHIELD]);
14854 case EL_EXTRA_TIME:
14855 RaiseScore(level.extra_time_score);
14869 case EL_DC_KEY_WHITE:
14870 RaiseScore(level.score[SC_KEY]);
14873 RaiseScore(element_info[element].collect_score);
14878 void RequestQuitGameExt(boolean skip_request, boolean quick_quit, char *message)
14880 if (skip_request || Request(message, REQ_ASK | REQ_STAY_CLOSED))
14882 /* closing door required in case of envelope style request dialogs */
14884 CloseDoor(DOOR_CLOSE_1);
14886 if (network.enabled)
14887 SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
14891 FadeSkipNextFadeIn();
14893 SetGameStatus(GAME_MODE_MAIN);
14898 else /* continue playing the game */
14900 if (tape.playing && tape.deactivate_display)
14901 TapeDeactivateDisplayOff(TRUE);
14903 OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
14905 if (tape.playing && tape.deactivate_display)
14906 TapeDeactivateDisplayOn();
14910 void RequestQuitGame(boolean ask_if_really_quit)
14912 boolean quick_quit = (!ask_if_really_quit || level_editor_test_game);
14913 boolean skip_request = AllPlayersGone || quick_quit;
14915 RequestQuitGameExt(skip_request, quick_quit,
14916 "Do you really want to quit the game?");
14919 void RequestRestartGame(char *message)
14921 game.restart_game_message = NULL;
14923 if (Request(message, REQ_ASK | REQ_STAY_CLOSED))
14925 StartGameActions(network.enabled, setup.autorecord, level.random_seed);
14929 SetGameStatus(GAME_MODE_MAIN);
14936 /* ------------------------------------------------------------------------- */
14937 /* random generator functions */
14938 /* ------------------------------------------------------------------------- */
14940 unsigned int InitEngineRandom_RND(int seed)
14942 game.num_random_calls = 0;
14944 return InitEngineRandom(seed);
14947 unsigned int RND(int max)
14951 game.num_random_calls++;
14953 return GetEngineRandom(max);
14960 /* ------------------------------------------------------------------------- */
14961 /* game engine snapshot handling functions */
14962 /* ------------------------------------------------------------------------- */
14964 struct EngineSnapshotInfo
14966 /* runtime values for custom element collect score */
14967 int collect_score[NUM_CUSTOM_ELEMENTS];
14969 /* runtime values for group element choice position */
14970 int choice_pos[NUM_GROUP_ELEMENTS];
14972 /* runtime values for belt position animations */
14973 int belt_graphic[4][NUM_BELT_PARTS];
14974 int belt_anim_mode[4][NUM_BELT_PARTS];
14977 static struct EngineSnapshotInfo engine_snapshot_rnd;
14978 static char *snapshot_level_identifier = NULL;
14979 static int snapshot_level_nr = -1;
14981 static void SaveEngineSnapshotValues_RND()
14983 static int belt_base_active_element[4] =
14985 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
14986 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
14987 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
14988 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
14992 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
14994 int element = EL_CUSTOM_START + i;
14996 engine_snapshot_rnd.collect_score[i] = element_info[element].collect_score;
14999 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
15001 int element = EL_GROUP_START + i;
15003 engine_snapshot_rnd.choice_pos[i] = element_info[element].group->choice_pos;
15006 for (i = 0; i < 4; i++)
15008 for (j = 0; j < NUM_BELT_PARTS; j++)
15010 int element = belt_base_active_element[i] + j;
15011 int graphic = el2img(element);
15012 int anim_mode = graphic_info[graphic].anim_mode;
15014 engine_snapshot_rnd.belt_graphic[i][j] = graphic;
15015 engine_snapshot_rnd.belt_anim_mode[i][j] = anim_mode;
15020 static void LoadEngineSnapshotValues_RND()
15022 unsigned int num_random_calls = game.num_random_calls;
15025 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
15027 int element = EL_CUSTOM_START + i;
15029 element_info[element].collect_score = engine_snapshot_rnd.collect_score[i];
15032 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
15034 int element = EL_GROUP_START + i;
15036 element_info[element].group->choice_pos = engine_snapshot_rnd.choice_pos[i];
15039 for (i = 0; i < 4; i++)
15041 for (j = 0; j < NUM_BELT_PARTS; j++)
15043 int graphic = engine_snapshot_rnd.belt_graphic[i][j];
15044 int anim_mode = engine_snapshot_rnd.belt_anim_mode[i][j];
15046 graphic_info[graphic].anim_mode = anim_mode;
15050 if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15052 InitRND(tape.random_seed);
15053 for (i = 0; i < num_random_calls; i++)
15057 if (game.num_random_calls != num_random_calls)
15059 Error(ERR_INFO, "number of random calls out of sync");
15060 Error(ERR_INFO, "number of random calls should be %d", num_random_calls);
15061 Error(ERR_INFO, "number of random calls is %d", game.num_random_calls);
15062 Error(ERR_EXIT, "this should not happen -- please debug");
15066 void FreeEngineSnapshotSingle()
15068 FreeSnapshotSingle();
15070 setString(&snapshot_level_identifier, NULL);
15071 snapshot_level_nr = -1;
15074 void FreeEngineSnapshotList()
15076 FreeSnapshotList();
15079 ListNode *SaveEngineSnapshotBuffers()
15081 ListNode *buffers = NULL;
15083 /* copy some special values to a structure better suited for the snapshot */
15085 if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15086 SaveEngineSnapshotValues_RND();
15087 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15088 SaveEngineSnapshotValues_EM();
15089 if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15090 SaveEngineSnapshotValues_SP(&buffers);
15091 if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15092 SaveEngineSnapshotValues_MM(&buffers);
15094 /* save values stored in special snapshot structure */
15096 if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15097 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_rnd));
15098 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15099 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em));
15100 if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15101 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_sp));
15102 if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15103 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_mm));
15105 /* save further RND engine values */
15107 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(stored_player));
15108 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(game));
15109 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(tape));
15111 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ZX));
15112 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ZY));
15113 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExitX));
15114 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExitY));
15116 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(FrameCounter));
15117 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeFrames));
15118 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimePlayed));
15119 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeLeft));
15120 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TapeTime));
15122 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovDir));
15123 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovPos));
15124 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenGfxPos));
15126 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScrollStepSize));
15128 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AllPlayersGone));
15130 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt));
15131 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt2));
15133 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Feld));
15134 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovPos));
15135 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDir));
15136 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDelay));
15137 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeDelay));
15138 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangePage));
15139 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CustomValue));
15140 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store));
15141 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store2));
15142 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(StorePlayer));
15143 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Back));
15144 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaNr));
15145 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustMoving));
15146 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustFalling));
15147 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckCollision));
15148 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckImpact));
15149 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Stop));
15150 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Pushed));
15152 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeCount));
15153 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeEvent));
15155 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodePhase));
15156 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeDelay));
15157 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeField));
15159 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(RunnerVisit));
15160 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(PlayerVisit));
15162 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxFrame));
15163 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxRandom));
15164 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxElement));
15165 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxAction));
15166 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxDir));
15168 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_x));
15169 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_y));
15172 ListNode *node = engine_snapshot_list_rnd;
15175 while (node != NULL)
15177 num_bytes += ((struct EngineSnapshotNodeInfo *)node->content)->size;
15182 printf("::: size of engine snapshot: %d bytes\n", num_bytes);
15188 void SaveEngineSnapshotSingle()
15190 ListNode *buffers = SaveEngineSnapshotBuffers();
15192 /* finally save all snapshot buffers to single snapshot */
15193 SaveSnapshotSingle(buffers);
15195 /* save level identification information */
15196 setString(&snapshot_level_identifier, leveldir_current->identifier);
15197 snapshot_level_nr = level_nr;
15200 boolean CheckSaveEngineSnapshotToList()
15202 boolean save_snapshot =
15203 ((game.snapshot.mode == SNAPSHOT_MODE_EVERY_STEP) ||
15204 (game.snapshot.mode == SNAPSHOT_MODE_EVERY_MOVE &&
15205 game.snapshot.changed_action) ||
15206 (game.snapshot.mode == SNAPSHOT_MODE_EVERY_COLLECT &&
15207 game.snapshot.collected_item));
15209 game.snapshot.changed_action = FALSE;
15210 game.snapshot.collected_item = FALSE;
15211 game.snapshot.save_snapshot = save_snapshot;
15213 return save_snapshot;
15216 void SaveEngineSnapshotToList()
15218 if (game.snapshot.mode == SNAPSHOT_MODE_OFF ||
15222 ListNode *buffers = SaveEngineSnapshotBuffers();
15224 /* finally save all snapshot buffers to snapshot list */
15225 SaveSnapshotToList(buffers);
15228 void SaveEngineSnapshotToListInitial()
15230 FreeEngineSnapshotList();
15232 SaveEngineSnapshotToList();
15235 void LoadEngineSnapshotValues()
15237 /* restore special values from snapshot structure */
15239 if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15240 LoadEngineSnapshotValues_RND();
15241 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15242 LoadEngineSnapshotValues_EM();
15243 if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15244 LoadEngineSnapshotValues_SP();
15245 if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15246 LoadEngineSnapshotValues_MM();
15249 void LoadEngineSnapshotSingle()
15251 LoadSnapshotSingle();
15253 LoadEngineSnapshotValues();
15256 void LoadEngineSnapshot_Undo(int steps)
15258 LoadSnapshotFromList_Older(steps);
15260 LoadEngineSnapshotValues();
15263 void LoadEngineSnapshot_Redo(int steps)
15265 LoadSnapshotFromList_Newer(steps);
15267 LoadEngineSnapshotValues();
15270 boolean CheckEngineSnapshotSingle()
15272 return (strEqual(snapshot_level_identifier, leveldir_current->identifier) &&
15273 snapshot_level_nr == level_nr);
15276 boolean CheckEngineSnapshotList()
15278 return CheckSnapshotList();
15282 /* ---------- new game button stuff ---------------------------------------- */
15289 boolean *setup_value;
15290 boolean allowed_on_tape;
15292 } gamebutton_info[NUM_GAME_BUTTONS] =
15295 IMG_GFX_GAME_BUTTON_STOP, &game.button.stop,
15296 GAME_CTRL_ID_STOP, NULL,
15300 IMG_GFX_GAME_BUTTON_PAUSE, &game.button.pause,
15301 GAME_CTRL_ID_PAUSE, NULL,
15305 IMG_GFX_GAME_BUTTON_PLAY, &game.button.play,
15306 GAME_CTRL_ID_PLAY, NULL,
15310 IMG_GFX_GAME_BUTTON_UNDO, &game.button.undo,
15311 GAME_CTRL_ID_UNDO, NULL,
15315 IMG_GFX_GAME_BUTTON_REDO, &game.button.redo,
15316 GAME_CTRL_ID_REDO, NULL,
15320 IMG_GFX_GAME_BUTTON_SAVE, &game.button.save,
15321 GAME_CTRL_ID_SAVE, NULL,
15325 IMG_GFX_GAME_BUTTON_PAUSE2, &game.button.pause2,
15326 GAME_CTRL_ID_PAUSE2, NULL,
15330 IMG_GFX_GAME_BUTTON_LOAD, &game.button.load,
15331 GAME_CTRL_ID_LOAD, NULL,
15335 IMG_GFX_GAME_BUTTON_PANEL_STOP, &game.button.panel_stop,
15336 GAME_CTRL_ID_PANEL_STOP, NULL,
15340 IMG_GFX_GAME_BUTTON_PANEL_PAUSE, &game.button.panel_pause,
15341 GAME_CTRL_ID_PANEL_PAUSE, NULL,
15342 FALSE, "pause game"
15345 IMG_GFX_GAME_BUTTON_PANEL_PLAY, &game.button.panel_play,
15346 GAME_CTRL_ID_PANEL_PLAY, NULL,
15350 IMG_GFX_GAME_BUTTON_SOUND_MUSIC, &game.button.sound_music,
15351 SOUND_CTRL_ID_MUSIC, &setup.sound_music,
15352 TRUE, "background music on/off"
15355 IMG_GFX_GAME_BUTTON_SOUND_LOOPS, &game.button.sound_loops,
15356 SOUND_CTRL_ID_LOOPS, &setup.sound_loops,
15357 TRUE, "sound loops on/off"
15360 IMG_GFX_GAME_BUTTON_SOUND_SIMPLE, &game.button.sound_simple,
15361 SOUND_CTRL_ID_SIMPLE, &setup.sound_simple,
15362 TRUE, "normal sounds on/off"
15365 IMG_GFX_GAME_BUTTON_PANEL_SOUND_MUSIC, &game.button.panel_sound_music,
15366 SOUND_CTRL_ID_PANEL_MUSIC, &setup.sound_music,
15367 FALSE, "background music on/off"
15370 IMG_GFX_GAME_BUTTON_PANEL_SOUND_LOOPS, &game.button.panel_sound_loops,
15371 SOUND_CTRL_ID_PANEL_LOOPS, &setup.sound_loops,
15372 FALSE, "sound loops on/off"
15375 IMG_GFX_GAME_BUTTON_PANEL_SOUND_SIMPLE, &game.button.panel_sound_simple,
15376 SOUND_CTRL_ID_PANEL_SIMPLE, &setup.sound_simple,
15377 FALSE, "normal sounds on/off"
15381 void CreateGameButtons()
15385 for (i = 0; i < NUM_GAME_BUTTONS; i++)
15387 int graphic = gamebutton_info[i].graphic;
15388 struct GraphicInfo *gfx = &graphic_info[graphic];
15389 struct XY *pos = gamebutton_info[i].pos;
15390 struct GadgetInfo *gi;
15393 unsigned int event_mask;
15394 boolean allowed_on_tape = gamebutton_info[i].allowed_on_tape;
15395 boolean on_tape = (tape.show_game_buttons && allowed_on_tape);
15396 int base_x = (on_tape ? VX : DX);
15397 int base_y = (on_tape ? VY : DY);
15398 int gd_x = gfx->src_x;
15399 int gd_y = gfx->src_y;
15400 int gd_xp = gfx->src_x + gfx->pressed_xoffset;
15401 int gd_yp = gfx->src_y + gfx->pressed_yoffset;
15402 int gd_xa = gfx->src_x + gfx->active_xoffset;
15403 int gd_ya = gfx->src_y + gfx->active_yoffset;
15404 int gd_xap = gfx->src_x + gfx->active_xoffset + gfx->pressed_xoffset;
15405 int gd_yap = gfx->src_y + gfx->active_yoffset + gfx->pressed_yoffset;
15408 if (gfx->bitmap == NULL)
15410 game_gadget[id] = NULL;
15415 if (id == GAME_CTRL_ID_STOP ||
15416 id == GAME_CTRL_ID_PANEL_STOP ||
15417 id == GAME_CTRL_ID_PLAY ||
15418 id == GAME_CTRL_ID_PANEL_PLAY ||
15419 id == GAME_CTRL_ID_SAVE ||
15420 id == GAME_CTRL_ID_LOAD)
15422 button_type = GD_TYPE_NORMAL_BUTTON;
15424 event_mask = GD_EVENT_RELEASED;
15426 else if (id == GAME_CTRL_ID_UNDO ||
15427 id == GAME_CTRL_ID_REDO)
15429 button_type = GD_TYPE_NORMAL_BUTTON;
15431 event_mask = GD_EVENT_PRESSED | GD_EVENT_REPEATED;
15435 button_type = GD_TYPE_CHECK_BUTTON;
15436 checked = (gamebutton_info[i].setup_value != NULL ?
15437 *gamebutton_info[i].setup_value : FALSE);
15438 event_mask = GD_EVENT_PRESSED;
15441 gi = CreateGadget(GDI_CUSTOM_ID, id,
15442 GDI_IMAGE_ID, graphic,
15443 GDI_INFO_TEXT, gamebutton_info[i].infotext,
15444 GDI_X, base_x + GDI_ACTIVE_POS(pos->x),
15445 GDI_Y, base_y + GDI_ACTIVE_POS(pos->y),
15446 GDI_WIDTH, gfx->width,
15447 GDI_HEIGHT, gfx->height,
15448 GDI_TYPE, button_type,
15449 GDI_STATE, GD_BUTTON_UNPRESSED,
15450 GDI_CHECKED, checked,
15451 GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
15452 GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
15453 GDI_ALT_DESIGN_UNPRESSED, gfx->bitmap, gd_xa, gd_ya,
15454 GDI_ALT_DESIGN_PRESSED, gfx->bitmap, gd_xap, gd_yap,
15455 GDI_DIRECT_DRAW, FALSE,
15456 GDI_EVENT_MASK, event_mask,
15457 GDI_CALLBACK_ACTION, HandleGameButtons,
15461 Error(ERR_EXIT, "cannot create gadget");
15463 game_gadget[id] = gi;
15467 void FreeGameButtons()
15471 for (i = 0; i < NUM_GAME_BUTTONS; i++)
15472 FreeGadget(game_gadget[i]);
15475 static void UnmapGameButtonsAtSamePosition(int id)
15479 for (i = 0; i < NUM_GAME_BUTTONS; i++)
15481 gamebutton_info[i].pos->x == gamebutton_info[id].pos->x &&
15482 gamebutton_info[i].pos->y == gamebutton_info[id].pos->y)
15483 UnmapGadget(game_gadget[i]);
15486 static void UnmapGameButtonsAtSamePosition_All()
15488 if (setup.show_snapshot_buttons)
15490 UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_SAVE);
15491 UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE2);
15492 UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_LOAD);
15496 UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_STOP);
15497 UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE);
15498 UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PLAY);
15500 UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_STOP);
15501 UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_PAUSE);
15502 UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_PLAY);
15506 static void MapGameButtonsAtSamePosition(int id)
15510 for (i = 0; i < NUM_GAME_BUTTONS; i++)
15512 gamebutton_info[i].pos->x == gamebutton_info[id].pos->x &&
15513 gamebutton_info[i].pos->y == gamebutton_info[id].pos->y)
15514 MapGadget(game_gadget[i]);
15516 UnmapGameButtonsAtSamePosition_All();
15519 void MapUndoRedoButtons()
15521 UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
15522 UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
15524 MapGadget(game_gadget[GAME_CTRL_ID_UNDO]);
15525 MapGadget(game_gadget[GAME_CTRL_ID_REDO]);
15527 ModifyGadget(game_gadget[GAME_CTRL_ID_PAUSE2], GDI_CHECKED, TRUE, GDI_END);
15530 void UnmapUndoRedoButtons()
15532 UnmapGadget(game_gadget[GAME_CTRL_ID_UNDO]);
15533 UnmapGadget(game_gadget[GAME_CTRL_ID_REDO]);
15535 MapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
15536 MapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
15538 ModifyGadget(game_gadget[GAME_CTRL_ID_PAUSE2], GDI_CHECKED, FALSE, GDI_END);
15541 void MapGameButtonsExt(boolean on_tape)
15545 for (i = 0; i < NUM_GAME_BUTTONS; i++)
15546 if ((!on_tape || gamebutton_info[i].allowed_on_tape) &&
15547 i != GAME_CTRL_ID_UNDO &&
15548 i != GAME_CTRL_ID_REDO)
15549 MapGadget(game_gadget[i]);
15551 UnmapGameButtonsAtSamePosition_All();
15553 RedrawGameButtons();
15556 void UnmapGameButtonsExt(boolean on_tape)
15560 for (i = 0; i < NUM_GAME_BUTTONS; i++)
15561 if (!on_tape || gamebutton_info[i].allowed_on_tape)
15562 UnmapGadget(game_gadget[i]);
15565 void RedrawGameButtonsExt(boolean on_tape)
15569 for (i = 0; i < NUM_GAME_BUTTONS; i++)
15570 if (!on_tape || gamebutton_info[i].allowed_on_tape)
15571 RedrawGadget(game_gadget[i]);
15573 // RedrawGadget() may have set REDRAW_ALL if buttons are defined off-area
15574 redraw_mask &= ~REDRAW_ALL;
15577 void SetGadgetState(struct GadgetInfo *gi, boolean state)
15582 gi->checked = state;
15585 void RedrawSoundButtonGadget(int id)
15587 int id2 = (id == SOUND_CTRL_ID_MUSIC ? SOUND_CTRL_ID_PANEL_MUSIC :
15588 id == SOUND_CTRL_ID_LOOPS ? SOUND_CTRL_ID_PANEL_LOOPS :
15589 id == SOUND_CTRL_ID_SIMPLE ? SOUND_CTRL_ID_PANEL_SIMPLE :
15590 id == SOUND_CTRL_ID_PANEL_MUSIC ? SOUND_CTRL_ID_MUSIC :
15591 id == SOUND_CTRL_ID_PANEL_LOOPS ? SOUND_CTRL_ID_LOOPS :
15592 id == SOUND_CTRL_ID_PANEL_SIMPLE ? SOUND_CTRL_ID_SIMPLE :
15595 SetGadgetState(game_gadget[id2], *gamebutton_info[id2].setup_value);
15596 RedrawGadget(game_gadget[id2]);
15599 void MapGameButtons()
15601 MapGameButtonsExt(FALSE);
15604 void UnmapGameButtons()
15606 UnmapGameButtonsExt(FALSE);
15609 void RedrawGameButtons()
15611 RedrawGameButtonsExt(FALSE);
15614 void MapGameButtonsOnTape()
15616 MapGameButtonsExt(TRUE);
15619 void UnmapGameButtonsOnTape()
15621 UnmapGameButtonsExt(TRUE);
15624 void RedrawGameButtonsOnTape()
15626 RedrawGameButtonsExt(TRUE);
15629 void GameUndoRedoExt()
15631 ClearPlayerAction();
15633 tape.pausing = TRUE;
15636 UpdateAndDisplayGameControlValues();
15638 DrawCompleteVideoDisplay();
15639 DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
15640 DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
15641 DrawVideoDisplay(VIDEO_STATE_1STEP(tape.single_step), 0);
15646 void GameUndo(int steps)
15648 if (!CheckEngineSnapshotList())
15651 LoadEngineSnapshot_Undo(steps);
15656 void GameRedo(int steps)
15658 if (!CheckEngineSnapshotList())
15661 LoadEngineSnapshot_Redo(steps);
15666 static void HandleGameButtonsExt(int id, int button)
15668 static boolean game_undo_executed = FALSE;
15669 int steps = BUTTON_STEPSIZE(button);
15670 boolean handle_game_buttons =
15671 (game_status == GAME_MODE_PLAYING ||
15672 (game_status == GAME_MODE_MAIN && tape.show_game_buttons));
15674 if (!handle_game_buttons)
15679 case GAME_CTRL_ID_STOP:
15680 case GAME_CTRL_ID_PANEL_STOP:
15681 if (game_status == GAME_MODE_MAIN)
15687 RequestQuitGame(TRUE);
15691 case GAME_CTRL_ID_PAUSE:
15692 case GAME_CTRL_ID_PAUSE2:
15693 case GAME_CTRL_ID_PANEL_PAUSE:
15694 if (network.enabled && game_status == GAME_MODE_PLAYING)
15697 SendToServer_ContinuePlaying();
15699 SendToServer_PausePlaying();
15702 TapeTogglePause(TAPE_TOGGLE_MANUAL);
15704 game_undo_executed = FALSE;
15708 case GAME_CTRL_ID_PLAY:
15709 case GAME_CTRL_ID_PANEL_PLAY:
15710 if (game_status == GAME_MODE_MAIN)
15712 StartGameActions(network.enabled, setup.autorecord, level.random_seed);
15714 else if (tape.pausing)
15716 if (network.enabled)
15717 SendToServer_ContinuePlaying();
15719 TapeTogglePause(TAPE_TOGGLE_MANUAL | TAPE_TOGGLE_PLAY_PAUSE);
15723 case GAME_CTRL_ID_UNDO:
15724 // Important: When using "save snapshot when collecting an item" mode,
15725 // load last (current) snapshot for first "undo" after pressing "pause"
15726 // (else the last-but-one snapshot would be loaded, because the snapshot
15727 // pointer already points to the last snapshot when pressing "pause",
15728 // which is fine for "every step/move" mode, but not for "every collect")
15729 if (game.snapshot.mode == SNAPSHOT_MODE_EVERY_COLLECT &&
15730 !game_undo_executed)
15733 game_undo_executed = TRUE;
15738 case GAME_CTRL_ID_REDO:
15742 case GAME_CTRL_ID_SAVE:
15746 case GAME_CTRL_ID_LOAD:
15750 case SOUND_CTRL_ID_MUSIC:
15751 case SOUND_CTRL_ID_PANEL_MUSIC:
15752 if (setup.sound_music)
15754 setup.sound_music = FALSE;
15758 else if (audio.music_available)
15760 setup.sound = setup.sound_music = TRUE;
15762 SetAudioMode(setup.sound);
15764 if (game_status == GAME_MODE_PLAYING)
15768 RedrawSoundButtonGadget(id);
15772 case SOUND_CTRL_ID_LOOPS:
15773 case SOUND_CTRL_ID_PANEL_LOOPS:
15774 if (setup.sound_loops)
15775 setup.sound_loops = FALSE;
15776 else if (audio.loops_available)
15778 setup.sound = setup.sound_loops = TRUE;
15780 SetAudioMode(setup.sound);
15783 RedrawSoundButtonGadget(id);
15787 case SOUND_CTRL_ID_SIMPLE:
15788 case SOUND_CTRL_ID_PANEL_SIMPLE:
15789 if (setup.sound_simple)
15790 setup.sound_simple = FALSE;
15791 else if (audio.sound_available)
15793 setup.sound = setup.sound_simple = TRUE;
15795 SetAudioMode(setup.sound);
15798 RedrawSoundButtonGadget(id);
15807 static void HandleGameButtons(struct GadgetInfo *gi)
15809 HandleGameButtonsExt(gi->custom_id, gi->event.button);
15812 void HandleSoundButtonKeys(Key key)
15814 if (key == setup.shortcut.sound_simple)
15815 ClickOnGadget(game_gadget[SOUND_CTRL_ID_SIMPLE], MB_LEFTBUTTON);
15816 else if (key == setup.shortcut.sound_loops)
15817 ClickOnGadget(game_gadget[SOUND_CTRL_ID_LOOPS], MB_LEFTBUTTON);
15818 else if (key == setup.shortcut.sound_music)
15819 ClickOnGadget(game_gadget[SOUND_CTRL_ID_MUSIC], MB_LEFTBUTTON);