1 // ============================================================================
2 // Rocks'n'Diamonds - McDuffin Strikes Back!
3 // ----------------------------------------------------------------------------
4 // (c) 1995-2014 by Artsoft Entertainment
7 // http://www.artsoft.org/
8 // ----------------------------------------------------------------------------
10 // ============================================================================
12 #include "libgame/libgame.h"
26 #define DEBUG_INIT_PLAYER 1
27 #define DEBUG_PLAYER_ACTIONS 0
29 /* EXPERIMENTAL STUFF */
30 #define USE_NEW_AMOEBA_CODE FALSE
32 /* EXPERIMENTAL STUFF */
33 #define USE_QUICKSAND_BD_ROCK_BUGFIX 0
34 #define USE_QUICKSAND_IMPACT_BUGFIX 0
35 #define USE_DELAYED_GFX_REDRAW 0
36 #define USE_NEW_PLAYER_ASSIGNMENTS 1
38 #if USE_DELAYED_GFX_REDRAW
39 #define TEST_DrawLevelField(x, y) \
40 GfxRedraw[x][y] |= GFX_REDRAW_TILE
41 #define TEST_DrawLevelFieldCrumbled(x, y) \
42 GfxRedraw[x][y] |= GFX_REDRAW_TILE_CRUMBLED
43 #define TEST_DrawLevelFieldCrumbledNeighbours(x, y) \
44 GfxRedraw[x][y] |= GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS
45 #define TEST_DrawTwinkleOnField(x, y) \
46 GfxRedraw[x][y] |= GFX_REDRAW_TILE_TWINKLED
48 #define TEST_DrawLevelField(x, y) \
50 #define TEST_DrawLevelFieldCrumbled(x, y) \
51 DrawLevelFieldCrumbled(x, y)
52 #define TEST_DrawLevelFieldCrumbledNeighbours(x, y) \
53 DrawLevelFieldCrumbledNeighbours(x, y)
54 #define TEST_DrawTwinkleOnField(x, y) \
55 DrawTwinkleOnField(x, y)
64 /* for MovePlayer() */
65 #define MP_NO_ACTION 0
68 #define MP_DONT_RUN_INTO (MP_MOVING | MP_ACTION)
70 /* for ScrollPlayer() */
72 #define SCROLL_GO_ON 1
74 /* for Bang()/Explode() */
75 #define EX_PHASE_START 0
76 #define EX_TYPE_NONE 0
77 #define EX_TYPE_NORMAL (1 << 0)
78 #define EX_TYPE_CENTER (1 << 1)
79 #define EX_TYPE_BORDER (1 << 2)
80 #define EX_TYPE_CROSS (1 << 3)
81 #define EX_TYPE_DYNA (1 << 4)
82 #define EX_TYPE_SINGLE_TILE (EX_TYPE_CENTER | EX_TYPE_BORDER)
84 #define PANEL_OFF() (local_player->LevelSolved_PanelOff)
85 #define PANEL_DEACTIVATED(p) ((p)->x < 0 || (p)->y < 0 || PANEL_OFF())
86 #define PANEL_XPOS(p) (DX + ALIGNED_TEXT_XPOS(p))
87 #define PANEL_YPOS(p) (DY + ALIGNED_TEXT_YPOS(p))
89 /* game panel display and control definitions */
90 #define GAME_PANEL_LEVEL_NUMBER 0
91 #define GAME_PANEL_GEMS 1
92 #define GAME_PANEL_INVENTORY_COUNT 2
93 #define GAME_PANEL_INVENTORY_FIRST_1 3
94 #define GAME_PANEL_INVENTORY_FIRST_2 4
95 #define GAME_PANEL_INVENTORY_FIRST_3 5
96 #define GAME_PANEL_INVENTORY_FIRST_4 6
97 #define GAME_PANEL_INVENTORY_FIRST_5 7
98 #define GAME_PANEL_INVENTORY_FIRST_6 8
99 #define GAME_PANEL_INVENTORY_FIRST_7 9
100 #define GAME_PANEL_INVENTORY_FIRST_8 10
101 #define GAME_PANEL_INVENTORY_LAST_1 11
102 #define GAME_PANEL_INVENTORY_LAST_2 12
103 #define GAME_PANEL_INVENTORY_LAST_3 13
104 #define GAME_PANEL_INVENTORY_LAST_4 14
105 #define GAME_PANEL_INVENTORY_LAST_5 15
106 #define GAME_PANEL_INVENTORY_LAST_6 16
107 #define GAME_PANEL_INVENTORY_LAST_7 17
108 #define GAME_PANEL_INVENTORY_LAST_8 18
109 #define GAME_PANEL_KEY_1 19
110 #define GAME_PANEL_KEY_2 20
111 #define GAME_PANEL_KEY_3 21
112 #define GAME_PANEL_KEY_4 22
113 #define GAME_PANEL_KEY_5 23
114 #define GAME_PANEL_KEY_6 24
115 #define GAME_PANEL_KEY_7 25
116 #define GAME_PANEL_KEY_8 26
117 #define GAME_PANEL_KEY_WHITE 27
118 #define GAME_PANEL_KEY_WHITE_COUNT 28
119 #define GAME_PANEL_SCORE 29
120 #define GAME_PANEL_HIGHSCORE 30
121 #define GAME_PANEL_TIME 31
122 #define GAME_PANEL_TIME_HH 32
123 #define GAME_PANEL_TIME_MM 33
124 #define GAME_PANEL_TIME_SS 34
125 #define GAME_PANEL_TIME_ANIM 35
126 #define GAME_PANEL_HEALTH 36
127 #define GAME_PANEL_HEALTH_ANIM 37
128 #define GAME_PANEL_FRAME 38
129 #define GAME_PANEL_SHIELD_NORMAL 39
130 #define GAME_PANEL_SHIELD_NORMAL_TIME 40
131 #define GAME_PANEL_SHIELD_DEADLY 41
132 #define GAME_PANEL_SHIELD_DEADLY_TIME 42
133 #define GAME_PANEL_EXIT 43
134 #define GAME_PANEL_EMC_MAGIC_BALL 44
135 #define GAME_PANEL_EMC_MAGIC_BALL_SWITCH 45
136 #define GAME_PANEL_LIGHT_SWITCH 46
137 #define GAME_PANEL_LIGHT_SWITCH_TIME 47
138 #define GAME_PANEL_TIMEGATE_SWITCH 48
139 #define GAME_PANEL_TIMEGATE_SWITCH_TIME 49
140 #define GAME_PANEL_SWITCHGATE_SWITCH 50
141 #define GAME_PANEL_EMC_LENSES 51
142 #define GAME_PANEL_EMC_LENSES_TIME 52
143 #define GAME_PANEL_EMC_MAGNIFIER 53
144 #define GAME_PANEL_EMC_MAGNIFIER_TIME 54
145 #define GAME_PANEL_BALLOON_SWITCH 55
146 #define GAME_PANEL_DYNABOMB_NUMBER 56
147 #define GAME_PANEL_DYNABOMB_SIZE 57
148 #define GAME_PANEL_DYNABOMB_POWER 58
149 #define GAME_PANEL_PENGUINS 59
150 #define GAME_PANEL_SOKOBAN_OBJECTS 60
151 #define GAME_PANEL_SOKOBAN_FIELDS 61
152 #define GAME_PANEL_ROBOT_WHEEL 62
153 #define GAME_PANEL_CONVEYOR_BELT_1 63
154 #define GAME_PANEL_CONVEYOR_BELT_2 64
155 #define GAME_PANEL_CONVEYOR_BELT_3 65
156 #define GAME_PANEL_CONVEYOR_BELT_4 66
157 #define GAME_PANEL_CONVEYOR_BELT_1_SWITCH 67
158 #define GAME_PANEL_CONVEYOR_BELT_2_SWITCH 68
159 #define GAME_PANEL_CONVEYOR_BELT_3_SWITCH 69
160 #define GAME_PANEL_CONVEYOR_BELT_4_SWITCH 70
161 #define GAME_PANEL_MAGIC_WALL 71
162 #define GAME_PANEL_MAGIC_WALL_TIME 72
163 #define GAME_PANEL_GRAVITY_STATE 73
164 #define GAME_PANEL_GRAPHIC_1 74
165 #define GAME_PANEL_GRAPHIC_2 75
166 #define GAME_PANEL_GRAPHIC_3 76
167 #define GAME_PANEL_GRAPHIC_4 77
168 #define GAME_PANEL_GRAPHIC_5 78
169 #define GAME_PANEL_GRAPHIC_6 79
170 #define GAME_PANEL_GRAPHIC_7 80
171 #define GAME_PANEL_GRAPHIC_8 81
172 #define GAME_PANEL_ELEMENT_1 82
173 #define GAME_PANEL_ELEMENT_2 83
174 #define GAME_PANEL_ELEMENT_3 84
175 #define GAME_PANEL_ELEMENT_4 85
176 #define GAME_PANEL_ELEMENT_5 86
177 #define GAME_PANEL_ELEMENT_6 87
178 #define GAME_PANEL_ELEMENT_7 88
179 #define GAME_PANEL_ELEMENT_8 89
180 #define GAME_PANEL_ELEMENT_COUNT_1 90
181 #define GAME_PANEL_ELEMENT_COUNT_2 91
182 #define GAME_PANEL_ELEMENT_COUNT_3 92
183 #define GAME_PANEL_ELEMENT_COUNT_4 93
184 #define GAME_PANEL_ELEMENT_COUNT_5 94
185 #define GAME_PANEL_ELEMENT_COUNT_6 95
186 #define GAME_PANEL_ELEMENT_COUNT_7 96
187 #define GAME_PANEL_ELEMENT_COUNT_8 97
188 #define GAME_PANEL_CE_SCORE_1 98
189 #define GAME_PANEL_CE_SCORE_2 99
190 #define GAME_PANEL_CE_SCORE_3 100
191 #define GAME_PANEL_CE_SCORE_4 101
192 #define GAME_PANEL_CE_SCORE_5 102
193 #define GAME_PANEL_CE_SCORE_6 103
194 #define GAME_PANEL_CE_SCORE_7 104
195 #define GAME_PANEL_CE_SCORE_8 105
196 #define GAME_PANEL_CE_SCORE_1_ELEMENT 106
197 #define GAME_PANEL_CE_SCORE_2_ELEMENT 107
198 #define GAME_PANEL_CE_SCORE_3_ELEMENT 108
199 #define GAME_PANEL_CE_SCORE_4_ELEMENT 109
200 #define GAME_PANEL_CE_SCORE_5_ELEMENT 110
201 #define GAME_PANEL_CE_SCORE_6_ELEMENT 111
202 #define GAME_PANEL_CE_SCORE_7_ELEMENT 112
203 #define GAME_PANEL_CE_SCORE_8_ELEMENT 113
204 #define GAME_PANEL_PLAYER_NAME 114
205 #define GAME_PANEL_LEVEL_NAME 115
206 #define GAME_PANEL_LEVEL_AUTHOR 116
208 #define NUM_GAME_PANEL_CONTROLS 117
210 struct GamePanelOrderInfo
216 static struct GamePanelOrderInfo game_panel_order[NUM_GAME_PANEL_CONTROLS];
218 struct GamePanelControlInfo
222 struct TextPosInfo *pos;
225 int graphic, graphic_active;
227 int value, last_value;
228 int frame, last_frame;
233 static struct GamePanelControlInfo game_panel_controls[] =
236 GAME_PANEL_LEVEL_NUMBER,
237 &game.panel.level_number,
246 GAME_PANEL_INVENTORY_COUNT,
247 &game.panel.inventory_count,
251 GAME_PANEL_INVENTORY_FIRST_1,
252 &game.panel.inventory_first[0],
256 GAME_PANEL_INVENTORY_FIRST_2,
257 &game.panel.inventory_first[1],
261 GAME_PANEL_INVENTORY_FIRST_3,
262 &game.panel.inventory_first[2],
266 GAME_PANEL_INVENTORY_FIRST_4,
267 &game.panel.inventory_first[3],
271 GAME_PANEL_INVENTORY_FIRST_5,
272 &game.panel.inventory_first[4],
276 GAME_PANEL_INVENTORY_FIRST_6,
277 &game.panel.inventory_first[5],
281 GAME_PANEL_INVENTORY_FIRST_7,
282 &game.panel.inventory_first[6],
286 GAME_PANEL_INVENTORY_FIRST_8,
287 &game.panel.inventory_first[7],
291 GAME_PANEL_INVENTORY_LAST_1,
292 &game.panel.inventory_last[0],
296 GAME_PANEL_INVENTORY_LAST_2,
297 &game.panel.inventory_last[1],
301 GAME_PANEL_INVENTORY_LAST_3,
302 &game.panel.inventory_last[2],
306 GAME_PANEL_INVENTORY_LAST_4,
307 &game.panel.inventory_last[3],
311 GAME_PANEL_INVENTORY_LAST_5,
312 &game.panel.inventory_last[4],
316 GAME_PANEL_INVENTORY_LAST_6,
317 &game.panel.inventory_last[5],
321 GAME_PANEL_INVENTORY_LAST_7,
322 &game.panel.inventory_last[6],
326 GAME_PANEL_INVENTORY_LAST_8,
327 &game.panel.inventory_last[7],
371 GAME_PANEL_KEY_WHITE,
372 &game.panel.key_white,
376 GAME_PANEL_KEY_WHITE_COUNT,
377 &game.panel.key_white_count,
386 GAME_PANEL_HIGHSCORE,
387 &game.panel.highscore,
411 GAME_PANEL_TIME_ANIM,
412 &game.panel.time_anim,
415 IMG_GFX_GAME_PANEL_TIME_ANIM,
416 IMG_GFX_GAME_PANEL_TIME_ANIM_ACTIVE
424 GAME_PANEL_HEALTH_ANIM,
425 &game.panel.health_anim,
428 IMG_GFX_GAME_PANEL_HEALTH_ANIM,
429 IMG_GFX_GAME_PANEL_HEALTH_ANIM_ACTIVE
437 GAME_PANEL_SHIELD_NORMAL,
438 &game.panel.shield_normal,
442 GAME_PANEL_SHIELD_NORMAL_TIME,
443 &game.panel.shield_normal_time,
447 GAME_PANEL_SHIELD_DEADLY,
448 &game.panel.shield_deadly,
452 GAME_PANEL_SHIELD_DEADLY_TIME,
453 &game.panel.shield_deadly_time,
462 GAME_PANEL_EMC_MAGIC_BALL,
463 &game.panel.emc_magic_ball,
467 GAME_PANEL_EMC_MAGIC_BALL_SWITCH,
468 &game.panel.emc_magic_ball_switch,
472 GAME_PANEL_LIGHT_SWITCH,
473 &game.panel.light_switch,
477 GAME_PANEL_LIGHT_SWITCH_TIME,
478 &game.panel.light_switch_time,
482 GAME_PANEL_TIMEGATE_SWITCH,
483 &game.panel.timegate_switch,
487 GAME_PANEL_TIMEGATE_SWITCH_TIME,
488 &game.panel.timegate_switch_time,
492 GAME_PANEL_SWITCHGATE_SWITCH,
493 &game.panel.switchgate_switch,
497 GAME_PANEL_EMC_LENSES,
498 &game.panel.emc_lenses,
502 GAME_PANEL_EMC_LENSES_TIME,
503 &game.panel.emc_lenses_time,
507 GAME_PANEL_EMC_MAGNIFIER,
508 &game.panel.emc_magnifier,
512 GAME_PANEL_EMC_MAGNIFIER_TIME,
513 &game.panel.emc_magnifier_time,
517 GAME_PANEL_BALLOON_SWITCH,
518 &game.panel.balloon_switch,
522 GAME_PANEL_DYNABOMB_NUMBER,
523 &game.panel.dynabomb_number,
527 GAME_PANEL_DYNABOMB_SIZE,
528 &game.panel.dynabomb_size,
532 GAME_PANEL_DYNABOMB_POWER,
533 &game.panel.dynabomb_power,
538 &game.panel.penguins,
542 GAME_PANEL_SOKOBAN_OBJECTS,
543 &game.panel.sokoban_objects,
547 GAME_PANEL_SOKOBAN_FIELDS,
548 &game.panel.sokoban_fields,
552 GAME_PANEL_ROBOT_WHEEL,
553 &game.panel.robot_wheel,
557 GAME_PANEL_CONVEYOR_BELT_1,
558 &game.panel.conveyor_belt[0],
562 GAME_PANEL_CONVEYOR_BELT_2,
563 &game.panel.conveyor_belt[1],
567 GAME_PANEL_CONVEYOR_BELT_3,
568 &game.panel.conveyor_belt[2],
572 GAME_PANEL_CONVEYOR_BELT_4,
573 &game.panel.conveyor_belt[3],
577 GAME_PANEL_CONVEYOR_BELT_1_SWITCH,
578 &game.panel.conveyor_belt_switch[0],
582 GAME_PANEL_CONVEYOR_BELT_2_SWITCH,
583 &game.panel.conveyor_belt_switch[1],
587 GAME_PANEL_CONVEYOR_BELT_3_SWITCH,
588 &game.panel.conveyor_belt_switch[2],
592 GAME_PANEL_CONVEYOR_BELT_4_SWITCH,
593 &game.panel.conveyor_belt_switch[3],
597 GAME_PANEL_MAGIC_WALL,
598 &game.panel.magic_wall,
602 GAME_PANEL_MAGIC_WALL_TIME,
603 &game.panel.magic_wall_time,
607 GAME_PANEL_GRAVITY_STATE,
608 &game.panel.gravity_state,
612 GAME_PANEL_GRAPHIC_1,
613 &game.panel.graphic[0],
617 GAME_PANEL_GRAPHIC_2,
618 &game.panel.graphic[1],
622 GAME_PANEL_GRAPHIC_3,
623 &game.panel.graphic[2],
627 GAME_PANEL_GRAPHIC_4,
628 &game.panel.graphic[3],
632 GAME_PANEL_GRAPHIC_5,
633 &game.panel.graphic[4],
637 GAME_PANEL_GRAPHIC_6,
638 &game.panel.graphic[5],
642 GAME_PANEL_GRAPHIC_7,
643 &game.panel.graphic[6],
647 GAME_PANEL_GRAPHIC_8,
648 &game.panel.graphic[7],
652 GAME_PANEL_ELEMENT_1,
653 &game.panel.element[0],
657 GAME_PANEL_ELEMENT_2,
658 &game.panel.element[1],
662 GAME_PANEL_ELEMENT_3,
663 &game.panel.element[2],
667 GAME_PANEL_ELEMENT_4,
668 &game.panel.element[3],
672 GAME_PANEL_ELEMENT_5,
673 &game.panel.element[4],
677 GAME_PANEL_ELEMENT_6,
678 &game.panel.element[5],
682 GAME_PANEL_ELEMENT_7,
683 &game.panel.element[6],
687 GAME_PANEL_ELEMENT_8,
688 &game.panel.element[7],
692 GAME_PANEL_ELEMENT_COUNT_1,
693 &game.panel.element_count[0],
697 GAME_PANEL_ELEMENT_COUNT_2,
698 &game.panel.element_count[1],
702 GAME_PANEL_ELEMENT_COUNT_3,
703 &game.panel.element_count[2],
707 GAME_PANEL_ELEMENT_COUNT_4,
708 &game.panel.element_count[3],
712 GAME_PANEL_ELEMENT_COUNT_5,
713 &game.panel.element_count[4],
717 GAME_PANEL_ELEMENT_COUNT_6,
718 &game.panel.element_count[5],
722 GAME_PANEL_ELEMENT_COUNT_7,
723 &game.panel.element_count[6],
727 GAME_PANEL_ELEMENT_COUNT_8,
728 &game.panel.element_count[7],
732 GAME_PANEL_CE_SCORE_1,
733 &game.panel.ce_score[0],
737 GAME_PANEL_CE_SCORE_2,
738 &game.panel.ce_score[1],
742 GAME_PANEL_CE_SCORE_3,
743 &game.panel.ce_score[2],
747 GAME_PANEL_CE_SCORE_4,
748 &game.panel.ce_score[3],
752 GAME_PANEL_CE_SCORE_5,
753 &game.panel.ce_score[4],
757 GAME_PANEL_CE_SCORE_6,
758 &game.panel.ce_score[5],
762 GAME_PANEL_CE_SCORE_7,
763 &game.panel.ce_score[6],
767 GAME_PANEL_CE_SCORE_8,
768 &game.panel.ce_score[7],
772 GAME_PANEL_CE_SCORE_1_ELEMENT,
773 &game.panel.ce_score_element[0],
777 GAME_PANEL_CE_SCORE_2_ELEMENT,
778 &game.panel.ce_score_element[1],
782 GAME_PANEL_CE_SCORE_3_ELEMENT,
783 &game.panel.ce_score_element[2],
787 GAME_PANEL_CE_SCORE_4_ELEMENT,
788 &game.panel.ce_score_element[3],
792 GAME_PANEL_CE_SCORE_5_ELEMENT,
793 &game.panel.ce_score_element[4],
797 GAME_PANEL_CE_SCORE_6_ELEMENT,
798 &game.panel.ce_score_element[5],
802 GAME_PANEL_CE_SCORE_7_ELEMENT,
803 &game.panel.ce_score_element[6],
807 GAME_PANEL_CE_SCORE_8_ELEMENT,
808 &game.panel.ce_score_element[7],
812 GAME_PANEL_PLAYER_NAME,
813 &game.panel.player_name,
817 GAME_PANEL_LEVEL_NAME,
818 &game.panel.level_name,
822 GAME_PANEL_LEVEL_AUTHOR,
823 &game.panel.level_author,
834 /* values for delayed check of falling and moving elements and for collision */
835 #define CHECK_DELAY_MOVING 3
836 #define CHECK_DELAY_FALLING CHECK_DELAY_MOVING
837 #define CHECK_DELAY_COLLISION 2
838 #define CHECK_DELAY_IMPACT CHECK_DELAY_COLLISION
840 /* values for initial player move delay (initial delay counter value) */
841 #define INITIAL_MOVE_DELAY_OFF -1
842 #define INITIAL_MOVE_DELAY_ON 0
844 /* values for player movement speed (which is in fact a delay value) */
845 #define MOVE_DELAY_MIN_SPEED 32
846 #define MOVE_DELAY_NORMAL_SPEED 8
847 #define MOVE_DELAY_HIGH_SPEED 4
848 #define MOVE_DELAY_MAX_SPEED 1
850 #define DOUBLE_MOVE_DELAY(x) (x = (x < MOVE_DELAY_MIN_SPEED ? x * 2 : x))
851 #define HALVE_MOVE_DELAY(x) (x = (x > MOVE_DELAY_MAX_SPEED ? x / 2 : x))
853 #define DOUBLE_PLAYER_SPEED(p) (HALVE_MOVE_DELAY( (p)->move_delay_value))
854 #define HALVE_PLAYER_SPEED(p) (DOUBLE_MOVE_DELAY((p)->move_delay_value))
856 /* values for scroll positions */
857 #define SCROLL_POSITION_X(x) ((x) < SBX_Left + MIDPOSX ? SBX_Left : \
858 (x) > SBX_Right + MIDPOSX ? SBX_Right :\
860 #define SCROLL_POSITION_Y(y) ((y) < SBY_Upper + MIDPOSY ? SBY_Upper :\
861 (y) > SBY_Lower + MIDPOSY ? SBY_Lower :\
864 /* values for other actions */
865 #define MOVE_STEPSIZE_NORMAL (TILEX / MOVE_DELAY_NORMAL_SPEED)
866 #define MOVE_STEPSIZE_MIN (1)
867 #define MOVE_STEPSIZE_MAX (TILEX)
869 #define GET_DX_FROM_DIR(d) ((d) == MV_LEFT ? -1 : (d) == MV_RIGHT ? 1 : 0)
870 #define GET_DY_FROM_DIR(d) ((d) == MV_UP ? -1 : (d) == MV_DOWN ? 1 : 0)
872 #define INIT_GFX_RANDOM() (GetSimpleRandom(1000000))
874 #define GET_NEW_PUSH_DELAY(e) ( (element_info[e].push_delay_fixed) + \
875 RND(element_info[e].push_delay_random))
876 #define GET_NEW_DROP_DELAY(e) ( (element_info[e].drop_delay_fixed) + \
877 RND(element_info[e].drop_delay_random))
878 #define GET_NEW_MOVE_DELAY(e) ( (element_info[e].move_delay_fixed) + \
879 RND(element_info[e].move_delay_random))
880 #define GET_MAX_MOVE_DELAY(e) ( (element_info[e].move_delay_fixed) + \
881 (element_info[e].move_delay_random))
882 #define GET_NEW_CE_VALUE(e) ( (element_info[e].ce_value_fixed_initial) +\
883 RND(element_info[e].ce_value_random_initial))
884 #define GET_CE_SCORE(e) ( (element_info[e].collect_score))
885 #define GET_CHANGE_DELAY(c) ( ((c)->delay_fixed * (c)->delay_frames) + \
886 RND((c)->delay_random * (c)->delay_frames))
887 #define GET_CE_DELAY_VALUE(c) ( ((c)->delay_fixed) + \
888 RND((c)->delay_random))
891 #define GET_VALID_RUNTIME_ELEMENT(e) \
892 ((e) >= NUM_RUNTIME_ELEMENTS ? EL_UNKNOWN : (e))
894 #define RESOLVED_REFERENCE_ELEMENT(be, e) \
895 ((be) + (e) - EL_SELF < EL_CUSTOM_START ? EL_CUSTOM_START : \
896 (be) + (e) - EL_SELF > EL_CUSTOM_END ? EL_CUSTOM_END : \
897 (be) + (e) - EL_SELF)
899 #define GET_PLAYER_FROM_BITS(p) \
900 (EL_PLAYER_1 + ((p) != PLAYER_BITS_ANY ? log_2(p) : 0))
902 #define GET_TARGET_ELEMENT(be, e, ch, cv, cs) \
903 ((e) == EL_TRIGGER_PLAYER ? (ch)->actual_trigger_player : \
904 (e) == EL_TRIGGER_ELEMENT ? (ch)->actual_trigger_element : \
905 (e) == EL_TRIGGER_CE_VALUE ? (ch)->actual_trigger_ce_value : \
906 (e) == EL_TRIGGER_CE_SCORE ? (ch)->actual_trigger_ce_score : \
907 (e) == EL_CURRENT_CE_VALUE ? (cv) : \
908 (e) == EL_CURRENT_CE_SCORE ? (cs) : \
909 (e) >= EL_PREV_CE_8 && (e) <= EL_NEXT_CE_8 ? \
910 RESOLVED_REFERENCE_ELEMENT(be, e) : \
913 #define CAN_GROW_INTO(e) \
914 ((e) == EL_SAND || (IS_DIGGABLE(e) && level.grow_into_diggable))
916 #define ELEMENT_CAN_ENTER_FIELD_BASE_X(x, y, condition) \
917 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
920 #define ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, condition) \
921 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
922 (CAN_MOVE_INTO_ACID(e) && \
923 Feld[x][y] == EL_ACID) || \
926 #define ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, condition) \
927 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) || \
928 (CAN_MOVE_INTO_ACID(e) && \
929 Feld[x][y] == EL_ACID) || \
932 #define ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, condition) \
933 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
935 (CAN_MOVE_INTO_ACID(e) && \
936 Feld[x][y] == EL_ACID) || \
937 (DONT_COLLIDE_WITH(e) && \
939 !PLAYER_ENEMY_PROTECTED(x, y))))
941 #define ELEMENT_CAN_ENTER_FIELD(e, x, y) \
942 ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, 0)
944 #define SATELLITE_CAN_ENTER_FIELD(x, y) \
945 ELEMENT_CAN_ENTER_FIELD_BASE_2(EL_SATELLITE, x, y, 0)
947 #define ANDROID_CAN_ENTER_FIELD(e, x, y) \
948 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, Feld[x][y] == EL_EMC_PLANT)
950 #define ANDROID_CAN_CLONE_FIELD(x, y) \
951 (IN_LEV_FIELD(x, y) && (CAN_BE_CLONED_BY_ANDROID(Feld[x][y]) || \
952 CAN_BE_CLONED_BY_ANDROID(EL_TRIGGER_ELEMENT)))
954 #define ENEMY_CAN_ENTER_FIELD(e, x, y) \
955 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
957 #define YAMYAM_CAN_ENTER_FIELD(e, x, y) \
958 ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, Feld[x][y] == EL_DIAMOND)
960 #define DARK_YAMYAM_CAN_ENTER_FIELD(e, x, y) \
961 ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x,y, IS_FOOD_DARK_YAMYAM(Feld[x][y]))
963 #define PACMAN_CAN_ENTER_FIELD(e, x, y) \
964 ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, IS_AMOEBOID(Feld[x][y]))
966 #define PIG_CAN_ENTER_FIELD(e, x, y) \
967 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, IS_FOOD_PIG(Feld[x][y]))
969 #define PENGUIN_CAN_ENTER_FIELD(e, x, y) \
970 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (Feld[x][y] == EL_EXIT_OPEN || \
971 Feld[x][y] == EL_EM_EXIT_OPEN || \
972 Feld[x][y] == EL_STEEL_EXIT_OPEN || \
973 Feld[x][y] == EL_EM_STEEL_EXIT_OPEN || \
974 IS_FOOD_PENGUIN(Feld[x][y])))
975 #define DRAGON_CAN_ENTER_FIELD(e, x, y) \
976 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
978 #define MOLE_CAN_ENTER_FIELD(e, x, y, condition) \
979 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (condition))
981 #define SPRING_CAN_ENTER_FIELD(e, x, y) \
982 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
984 #define SPRING_CAN_BUMP_FROM_FIELD(x, y) \
985 (IN_LEV_FIELD(x, y) && (Feld[x][y] == EL_EMC_SPRING_BUMPER || \
986 Feld[x][y] == EL_EMC_SPRING_BUMPER_ACTIVE))
988 #define MOVE_ENTER_EL(e) (element_info[e].move_enter_element)
990 #define CE_ENTER_FIELD_COND(e, x, y) \
991 (!IS_PLAYER(x, y) && \
992 IS_EQUAL_OR_IN_GROUP(Feld[x][y], MOVE_ENTER_EL(e)))
994 #define CUSTOM_ELEMENT_CAN_ENTER_FIELD(e, x, y) \
995 ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, CE_ENTER_FIELD_COND(e, x, y))
997 #define IN_LEV_FIELD_AND_IS_FREE(x, y) (IN_LEV_FIELD(x, y) && IS_FREE(x, y))
998 #define IN_LEV_FIELD_AND_NOT_FREE(x, y) (IN_LEV_FIELD(x, y) && !IS_FREE(x, y))
1000 #define ACCESS_FROM(e, d) (element_info[e].access_direction &(d))
1001 #define IS_WALKABLE_FROM(e, d) (IS_WALKABLE(e) && ACCESS_FROM(e, d))
1002 #define IS_PASSABLE_FROM(e, d) (IS_PASSABLE(e) && ACCESS_FROM(e, d))
1003 #define IS_ACCESSIBLE_FROM(e, d) (IS_ACCESSIBLE(e) && ACCESS_FROM(e, d))
1005 #define MM_HEALTH(x) (MIN(MAX(0, MAX_HEALTH - (x)), MAX_HEALTH))
1007 /* game button identifiers */
1008 #define GAME_CTRL_ID_STOP 0
1009 #define GAME_CTRL_ID_PAUSE 1
1010 #define GAME_CTRL_ID_PLAY 2
1011 #define GAME_CTRL_ID_UNDO 3
1012 #define GAME_CTRL_ID_REDO 4
1013 #define GAME_CTRL_ID_SAVE 5
1014 #define GAME_CTRL_ID_PAUSE2 6
1015 #define GAME_CTRL_ID_LOAD 7
1016 #define GAME_CTRL_ID_PANEL_STOP 8
1017 #define GAME_CTRL_ID_PANEL_PAUSE 9
1018 #define GAME_CTRL_ID_PANEL_PLAY 10
1019 #define SOUND_CTRL_ID_MUSIC 11
1020 #define SOUND_CTRL_ID_LOOPS 12
1021 #define SOUND_CTRL_ID_SIMPLE 13
1022 #define SOUND_CTRL_ID_PANEL_MUSIC 14
1023 #define SOUND_CTRL_ID_PANEL_LOOPS 15
1024 #define SOUND_CTRL_ID_PANEL_SIMPLE 16
1026 #define NUM_GAME_BUTTONS 17
1029 /* forward declaration for internal use */
1031 static void CreateField(int, int, int);
1033 static void ResetGfxAnimation(int, int);
1035 static void SetPlayerWaiting(struct PlayerInfo *, boolean);
1036 static void AdvanceFrameAndPlayerCounters(int);
1038 static boolean MovePlayerOneStep(struct PlayerInfo *, int, int, int, int);
1039 static boolean MovePlayer(struct PlayerInfo *, int, int);
1040 static void ScrollPlayer(struct PlayerInfo *, int);
1041 static void ScrollScreen(struct PlayerInfo *, int);
1043 static int DigField(struct PlayerInfo *, int, int, int, int, int, int, int);
1044 static boolean DigFieldByCE(int, int, int);
1045 static boolean SnapField(struct PlayerInfo *, int, int);
1046 static boolean DropElement(struct PlayerInfo *);
1048 static void InitBeltMovement(void);
1049 static void CloseAllOpenTimegates(void);
1050 static void CheckGravityMovement(struct PlayerInfo *);
1051 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *);
1052 static void KillPlayerUnlessEnemyProtected(int, int);
1053 static void KillPlayerUnlessExplosionProtected(int, int);
1055 static void TestIfPlayerTouchesCustomElement(int, int);
1056 static void TestIfElementTouchesCustomElement(int, int);
1057 static void TestIfElementHitsCustomElement(int, int, int);
1059 static void HandleElementChange(int, int, int);
1060 static void ExecuteCustomElementAction(int, int, int, int);
1061 static boolean ChangeElement(int, int, int, int);
1063 static boolean CheckTriggeredElementChangeExt(int, int, int, int, int,int,int);
1064 #define CheckTriggeredElementChange(x, y, e, ev) \
1065 CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY,CH_SIDE_ANY, -1)
1066 #define CheckTriggeredElementChangeByPlayer(x, y, e, ev, p, s) \
1067 CheckTriggeredElementChangeExt(x, y, e, ev, p, s, -1)
1068 #define CheckTriggeredElementChangeBySide(x, y, e, ev, s) \
1069 CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, s, -1)
1070 #define CheckTriggeredElementChangeByPage(x, y, e, ev, p) \
1071 CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY, CH_SIDE_ANY, p)
1073 static boolean CheckElementChangeExt(int, int, int, int, int, int, int);
1074 #define CheckElementChange(x, y, e, te, ev) \
1075 CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, CH_SIDE_ANY)
1076 #define CheckElementChangeByPlayer(x, y, e, ev, p, s) \
1077 CheckElementChangeExt(x, y, e, EL_EMPTY, ev, p, s)
1078 #define CheckElementChangeBySide(x, y, e, te, ev, s) \
1079 CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, s)
1081 static void PlayLevelSound(int, int, int);
1082 static void PlayLevelSoundNearest(int, int, int);
1083 static void PlayLevelSoundAction(int, int, int);
1084 static void PlayLevelSoundElementAction(int, int, int, int);
1085 static void PlayLevelSoundElementActionIfLoop(int, int, int, int);
1086 static void PlayLevelSoundActionIfLoop(int, int, int);
1087 static void StopLevelSoundActionIfLoop(int, int, int);
1088 static void PlayLevelMusic();
1089 static void FadeLevelSoundsAndMusic();
1091 static void HandleGameButtons(struct GadgetInfo *);
1093 int AmoebeNachbarNr(int, int);
1094 void AmoebeUmwandeln(int, int);
1095 void ContinueMoving(int, int);
1096 void Bang(int, int);
1097 void InitMovDir(int, int);
1098 void InitAmoebaNr(int, int);
1099 int NewHiScore(void);
1101 void TestIfGoodThingHitsBadThing(int, int, int);
1102 void TestIfBadThingHitsGoodThing(int, int, int);
1103 void TestIfPlayerTouchesBadThing(int, int);
1104 void TestIfPlayerRunsIntoBadThing(int, int, int);
1105 void TestIfBadThingTouchesPlayer(int, int);
1106 void TestIfBadThingRunsIntoPlayer(int, int, int);
1107 void TestIfFriendTouchesBadThing(int, int);
1108 void TestIfBadThingTouchesFriend(int, int);
1109 void TestIfBadThingTouchesOtherBadThing(int, int);
1110 void TestIfGoodThingGetsHitByBadThing(int, int, int);
1112 void KillPlayer(struct PlayerInfo *);
1113 void BuryPlayer(struct PlayerInfo *);
1114 void RemovePlayer(struct PlayerInfo *);
1116 static int getInvisibleActiveFromInvisibleElement(int);
1117 static int getInvisibleFromInvisibleActiveElement(int);
1119 static struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS];
1121 /* for detection of endless loops, caused by custom element programming */
1122 /* (using maximal playfield width x 10 is just a rough approximation) */
1123 #define MAX_ELEMENT_CHANGE_RECURSION_DEPTH (MAX_PLAYFIELD_WIDTH * 10)
1125 #define RECURSION_LOOP_DETECTION_START(e, rc) \
1127 if (recursion_loop_detected) \
1130 if (recursion_loop_depth > MAX_ELEMENT_CHANGE_RECURSION_DEPTH) \
1132 recursion_loop_detected = TRUE; \
1133 recursion_loop_element = (e); \
1136 recursion_loop_depth++; \
1139 #define RECURSION_LOOP_DETECTION_END() \
1141 recursion_loop_depth--; \
1144 static int recursion_loop_depth;
1145 static boolean recursion_loop_detected;
1146 static boolean recursion_loop_element;
1148 static int map_player_action[MAX_PLAYERS];
1151 /* ------------------------------------------------------------------------- */
1152 /* definition of elements that automatically change to other elements after */
1153 /* a specified time, eventually calling a function when changing */
1154 /* ------------------------------------------------------------------------- */
1156 /* forward declaration for changer functions */
1157 static void InitBuggyBase(int, int);
1158 static void WarnBuggyBase(int, int);
1160 static void InitTrap(int, int);
1161 static void ActivateTrap(int, int);
1162 static void ChangeActiveTrap(int, int);
1164 static void InitRobotWheel(int, int);
1165 static void RunRobotWheel(int, int);
1166 static void StopRobotWheel(int, int);
1168 static void InitTimegateWheel(int, int);
1169 static void RunTimegateWheel(int, int);
1171 static void InitMagicBallDelay(int, int);
1172 static void ActivateMagicBall(int, int);
1174 struct ChangingElementInfo
1179 void (*pre_change_function)(int x, int y);
1180 void (*change_function)(int x, int y);
1181 void (*post_change_function)(int x, int y);
1184 static struct ChangingElementInfo change_delay_list[] =
1219 EL_STEEL_EXIT_OPENING,
1227 EL_STEEL_EXIT_CLOSING,
1228 EL_STEEL_EXIT_CLOSED,
1251 EL_EM_STEEL_EXIT_OPENING,
1252 EL_EM_STEEL_EXIT_OPEN,
1259 EL_EM_STEEL_EXIT_CLOSING,
1283 EL_SWITCHGATE_OPENING,
1291 EL_SWITCHGATE_CLOSING,
1292 EL_SWITCHGATE_CLOSED,
1299 EL_TIMEGATE_OPENING,
1307 EL_TIMEGATE_CLOSING,
1316 EL_ACID_SPLASH_LEFT,
1324 EL_ACID_SPLASH_RIGHT,
1333 EL_SP_BUGGY_BASE_ACTIVATING,
1340 EL_SP_BUGGY_BASE_ACTIVATING,
1341 EL_SP_BUGGY_BASE_ACTIVE,
1348 EL_SP_BUGGY_BASE_ACTIVE,
1372 EL_ROBOT_WHEEL_ACTIVE,
1380 EL_TIMEGATE_SWITCH_ACTIVE,
1388 EL_DC_TIMEGATE_SWITCH_ACTIVE,
1389 EL_DC_TIMEGATE_SWITCH,
1396 EL_EMC_MAGIC_BALL_ACTIVE,
1397 EL_EMC_MAGIC_BALL_ACTIVE,
1404 EL_EMC_SPRING_BUMPER_ACTIVE,
1405 EL_EMC_SPRING_BUMPER,
1412 EL_DIAGONAL_SHRINKING,
1420 EL_DIAGONAL_GROWING,
1441 int push_delay_fixed, push_delay_random;
1445 { EL_SPRING, 0, 0 },
1446 { EL_BALLOON, 0, 0 },
1448 { EL_SOKOBAN_OBJECT, 2, 0 },
1449 { EL_SOKOBAN_FIELD_FULL, 2, 0 },
1450 { EL_SATELLITE, 2, 0 },
1451 { EL_SP_DISK_YELLOW, 2, 0 },
1453 { EL_UNDEFINED, 0, 0 },
1461 move_stepsize_list[] =
1463 { EL_AMOEBA_DROP, 2 },
1464 { EL_AMOEBA_DROPPING, 2 },
1465 { EL_QUICKSAND_FILLING, 1 },
1466 { EL_QUICKSAND_EMPTYING, 1 },
1467 { EL_QUICKSAND_FAST_FILLING, 2 },
1468 { EL_QUICKSAND_FAST_EMPTYING, 2 },
1469 { EL_MAGIC_WALL_FILLING, 2 },
1470 { EL_MAGIC_WALL_EMPTYING, 2 },
1471 { EL_BD_MAGIC_WALL_FILLING, 2 },
1472 { EL_BD_MAGIC_WALL_EMPTYING, 2 },
1473 { EL_DC_MAGIC_WALL_FILLING, 2 },
1474 { EL_DC_MAGIC_WALL_EMPTYING, 2 },
1476 { EL_UNDEFINED, 0 },
1484 collect_count_list[] =
1487 { EL_BD_DIAMOND, 1 },
1488 { EL_EMERALD_YELLOW, 1 },
1489 { EL_EMERALD_RED, 1 },
1490 { EL_EMERALD_PURPLE, 1 },
1492 { EL_SP_INFOTRON, 1 },
1496 { EL_UNDEFINED, 0 },
1504 access_direction_list[] =
1506 { EL_TUBE_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1507 { EL_TUBE_VERTICAL, MV_UP | MV_DOWN },
1508 { EL_TUBE_HORIZONTAL, MV_LEFT | MV_RIGHT },
1509 { EL_TUBE_VERTICAL_LEFT, MV_LEFT | MV_UP | MV_DOWN },
1510 { EL_TUBE_VERTICAL_RIGHT, MV_RIGHT | MV_UP | MV_DOWN },
1511 { EL_TUBE_HORIZONTAL_UP, MV_LEFT | MV_RIGHT | MV_UP },
1512 { EL_TUBE_HORIZONTAL_DOWN, MV_LEFT | MV_RIGHT | MV_DOWN },
1513 { EL_TUBE_LEFT_UP, MV_LEFT | MV_UP },
1514 { EL_TUBE_LEFT_DOWN, MV_LEFT | MV_DOWN },
1515 { EL_TUBE_RIGHT_UP, MV_RIGHT | MV_UP },
1516 { EL_TUBE_RIGHT_DOWN, MV_RIGHT | MV_DOWN },
1518 { EL_SP_PORT_LEFT, MV_RIGHT },
1519 { EL_SP_PORT_RIGHT, MV_LEFT },
1520 { EL_SP_PORT_UP, MV_DOWN },
1521 { EL_SP_PORT_DOWN, MV_UP },
1522 { EL_SP_PORT_HORIZONTAL, MV_LEFT | MV_RIGHT },
1523 { EL_SP_PORT_VERTICAL, MV_UP | MV_DOWN },
1524 { EL_SP_PORT_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1525 { EL_SP_GRAVITY_PORT_LEFT, MV_RIGHT },
1526 { EL_SP_GRAVITY_PORT_RIGHT, MV_LEFT },
1527 { EL_SP_GRAVITY_PORT_UP, MV_DOWN },
1528 { EL_SP_GRAVITY_PORT_DOWN, MV_UP },
1529 { EL_SP_GRAVITY_ON_PORT_LEFT, MV_RIGHT },
1530 { EL_SP_GRAVITY_ON_PORT_RIGHT, MV_LEFT },
1531 { EL_SP_GRAVITY_ON_PORT_UP, MV_DOWN },
1532 { EL_SP_GRAVITY_ON_PORT_DOWN, MV_UP },
1533 { EL_SP_GRAVITY_OFF_PORT_LEFT, MV_RIGHT },
1534 { EL_SP_GRAVITY_OFF_PORT_RIGHT, MV_LEFT },
1535 { EL_SP_GRAVITY_OFF_PORT_UP, MV_DOWN },
1536 { EL_SP_GRAVITY_OFF_PORT_DOWN, MV_UP },
1538 { EL_UNDEFINED, MV_NONE }
1541 static boolean trigger_events[MAX_NUM_ELEMENTS][NUM_CHANGE_EVENTS];
1543 #define IS_AUTO_CHANGING(e) (element_info[e].has_change_event[CE_DELAY])
1544 #define IS_JUST_CHANGING(x, y) (ChangeDelay[x][y] != 0)
1545 #define IS_CHANGING(x, y) (IS_AUTO_CHANGING(Feld[x][y]) || \
1546 IS_JUST_CHANGING(x, y))
1548 #define CE_PAGE(e, ce) (element_info[e].event_page[ce])
1550 /* static variables for playfield scan mode (scanning forward or backward) */
1551 static int playfield_scan_start_x = 0;
1552 static int playfield_scan_start_y = 0;
1553 static int playfield_scan_delta_x = 1;
1554 static int playfield_scan_delta_y = 1;
1556 #define SCAN_PLAYFIELD(x, y) for ((y) = playfield_scan_start_y; \
1557 (y) >= 0 && (y) <= lev_fieldy - 1; \
1558 (y) += playfield_scan_delta_y) \
1559 for ((x) = playfield_scan_start_x; \
1560 (x) >= 0 && (x) <= lev_fieldx - 1; \
1561 (x) += playfield_scan_delta_x)
1564 void DEBUG_SetMaximumDynamite()
1568 for (i = 0; i < MAX_INVENTORY_SIZE; i++)
1569 if (local_player->inventory_size < MAX_INVENTORY_SIZE)
1570 local_player->inventory_element[local_player->inventory_size++] =
1575 static void InitPlayfieldScanModeVars()
1577 if (game.use_reverse_scan_direction)
1579 playfield_scan_start_x = lev_fieldx - 1;
1580 playfield_scan_start_y = lev_fieldy - 1;
1582 playfield_scan_delta_x = -1;
1583 playfield_scan_delta_y = -1;
1587 playfield_scan_start_x = 0;
1588 playfield_scan_start_y = 0;
1590 playfield_scan_delta_x = 1;
1591 playfield_scan_delta_y = 1;
1595 static void InitPlayfieldScanMode(int mode)
1597 game.use_reverse_scan_direction =
1598 (mode == CA_ARG_SCAN_MODE_REVERSE ? TRUE : FALSE);
1600 InitPlayfieldScanModeVars();
1603 static int get_move_delay_from_stepsize(int move_stepsize)
1606 MIN(MAX(MOVE_STEPSIZE_MIN, move_stepsize), MOVE_STEPSIZE_MAX);
1608 /* make sure that stepsize value is always a power of 2 */
1609 move_stepsize = (1 << log_2(move_stepsize));
1611 return TILEX / move_stepsize;
1614 static void SetPlayerMoveSpeed(struct PlayerInfo *player, int move_stepsize,
1617 int player_nr = player->index_nr;
1618 int move_delay = get_move_delay_from_stepsize(move_stepsize);
1619 boolean cannot_move = (move_stepsize == STEPSIZE_NOT_MOVING ? TRUE : FALSE);
1621 /* do no immediately change move delay -- the player might just be moving */
1622 player->move_delay_value_next = move_delay;
1624 /* information if player can move must be set separately */
1625 player->cannot_move = cannot_move;
1629 player->move_delay = game.initial_move_delay[player_nr];
1630 player->move_delay_value = game.initial_move_delay_value[player_nr];
1632 player->move_delay_value_next = -1;
1634 player->move_delay_reset_counter = 0;
1638 void GetPlayerConfig()
1640 GameFrameDelay = setup.game_frame_delay;
1642 if (!audio.sound_available)
1643 setup.sound_simple = FALSE;
1645 if (!audio.loops_available)
1646 setup.sound_loops = FALSE;
1648 if (!audio.music_available)
1649 setup.sound_music = FALSE;
1651 if (!video.fullscreen_available)
1652 setup.fullscreen = FALSE;
1654 setup.sound = (setup.sound_simple || setup.sound_loops || setup.sound_music);
1656 SetAudioMode(setup.sound);
1659 int GetElementFromGroupElement(int element)
1661 if (IS_GROUP_ELEMENT(element))
1663 struct ElementGroupInfo *group = element_info[element].group;
1664 int last_anim_random_frame = gfx.anim_random_frame;
1667 if (group->choice_mode == ANIM_RANDOM)
1668 gfx.anim_random_frame = RND(group->num_elements_resolved);
1670 element_pos = getAnimationFrame(group->num_elements_resolved, 1,
1671 group->choice_mode, 0,
1674 if (group->choice_mode == ANIM_RANDOM)
1675 gfx.anim_random_frame = last_anim_random_frame;
1677 group->choice_pos++;
1679 element = group->element_resolved[element_pos];
1685 static void InitPlayerField(int x, int y, int element, boolean init_game)
1687 if (element == EL_SP_MURPHY)
1691 if (stored_player[0].present)
1693 Feld[x][y] = EL_SP_MURPHY_CLONE;
1699 stored_player[0].initial_element = element;
1700 stored_player[0].use_murphy = TRUE;
1702 if (!level.use_artwork_element[0])
1703 stored_player[0].artwork_element = EL_SP_MURPHY;
1706 Feld[x][y] = EL_PLAYER_1;
1712 struct PlayerInfo *player = &stored_player[Feld[x][y] - EL_PLAYER_1];
1713 int jx = player->jx, jy = player->jy;
1715 player->present = TRUE;
1717 player->block_last_field = (element == EL_SP_MURPHY ?
1718 level.sp_block_last_field :
1719 level.block_last_field);
1721 /* ---------- initialize player's last field block delay --------------- */
1723 /* always start with reliable default value (no adjustment needed) */
1724 player->block_delay_adjustment = 0;
1726 /* special case 1: in Supaplex, Murphy blocks last field one more frame */
1727 if (player->block_last_field && element == EL_SP_MURPHY)
1728 player->block_delay_adjustment = 1;
1730 /* special case 2: in game engines before 3.1.1, blocking was different */
1731 if (game.use_block_last_field_bug)
1732 player->block_delay_adjustment = (player->block_last_field ? -1 : 1);
1734 if (!options.network || player->connected)
1736 player->active = TRUE;
1738 /* remove potentially duplicate players */
1739 if (StorePlayer[jx][jy] == Feld[x][y])
1740 StorePlayer[jx][jy] = 0;
1742 StorePlayer[x][y] = Feld[x][y];
1744 #if DEBUG_INIT_PLAYER
1747 printf("- player element %d activated", player->element_nr);
1748 printf(" (local player is %d and currently %s)\n",
1749 local_player->element_nr,
1750 local_player->active ? "active" : "not active");
1755 Feld[x][y] = EL_EMPTY;
1757 player->jx = player->last_jx = x;
1758 player->jy = player->last_jy = y;
1763 int player_nr = GET_PLAYER_NR(element);
1764 struct PlayerInfo *player = &stored_player[player_nr];
1766 if (player->active && player->killed)
1767 player->reanimated = TRUE; /* if player was just killed, reanimate him */
1771 static void InitField(int x, int y, boolean init_game)
1773 int element = Feld[x][y];
1782 InitPlayerField(x, y, element, init_game);
1785 case EL_SOKOBAN_FIELD_PLAYER:
1786 element = Feld[x][y] = EL_PLAYER_1;
1787 InitField(x, y, init_game);
1789 element = Feld[x][y] = EL_SOKOBAN_FIELD_EMPTY;
1790 InitField(x, y, init_game);
1793 case EL_SOKOBAN_FIELD_EMPTY:
1794 local_player->sokobanfields_still_needed++;
1798 if (x < lev_fieldx-1 && Feld[x+1][y] == EL_ACID)
1799 Feld[x][y] = EL_ACID_POOL_TOPLEFT;
1800 else if (x > 0 && Feld[x-1][y] == EL_ACID)
1801 Feld[x][y] = EL_ACID_POOL_TOPRIGHT;
1802 else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPLEFT)
1803 Feld[x][y] = EL_ACID_POOL_BOTTOMLEFT;
1804 else if (y > 0 && Feld[x][y-1] == EL_ACID)
1805 Feld[x][y] = EL_ACID_POOL_BOTTOM;
1806 else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPRIGHT)
1807 Feld[x][y] = EL_ACID_POOL_BOTTOMRIGHT;
1816 case EL_SPACESHIP_RIGHT:
1817 case EL_SPACESHIP_UP:
1818 case EL_SPACESHIP_LEFT:
1819 case EL_SPACESHIP_DOWN:
1820 case EL_BD_BUTTERFLY:
1821 case EL_BD_BUTTERFLY_RIGHT:
1822 case EL_BD_BUTTERFLY_UP:
1823 case EL_BD_BUTTERFLY_LEFT:
1824 case EL_BD_BUTTERFLY_DOWN:
1826 case EL_BD_FIREFLY_RIGHT:
1827 case EL_BD_FIREFLY_UP:
1828 case EL_BD_FIREFLY_LEFT:
1829 case EL_BD_FIREFLY_DOWN:
1830 case EL_PACMAN_RIGHT:
1832 case EL_PACMAN_LEFT:
1833 case EL_PACMAN_DOWN:
1835 case EL_YAMYAM_LEFT:
1836 case EL_YAMYAM_RIGHT:
1838 case EL_YAMYAM_DOWN:
1839 case EL_DARK_YAMYAM:
1842 case EL_SP_SNIKSNAK:
1843 case EL_SP_ELECTRON:
1852 case EL_AMOEBA_FULL:
1857 case EL_AMOEBA_DROP:
1858 if (y == lev_fieldy - 1)
1860 Feld[x][y] = EL_AMOEBA_GROWING;
1861 Store[x][y] = EL_AMOEBA_WET;
1865 case EL_DYNAMITE_ACTIVE:
1866 case EL_SP_DISK_RED_ACTIVE:
1867 case EL_DYNABOMB_PLAYER_1_ACTIVE:
1868 case EL_DYNABOMB_PLAYER_2_ACTIVE:
1869 case EL_DYNABOMB_PLAYER_3_ACTIVE:
1870 case EL_DYNABOMB_PLAYER_4_ACTIVE:
1871 MovDelay[x][y] = 96;
1874 case EL_EM_DYNAMITE_ACTIVE:
1875 MovDelay[x][y] = 32;
1879 local_player->lights_still_needed++;
1883 local_player->friends_still_needed++;
1888 GfxDir[x][y] = MovDir[x][y] = 1 << RND(4);
1891 case EL_CONVEYOR_BELT_1_SWITCH_LEFT:
1892 case EL_CONVEYOR_BELT_1_SWITCH_MIDDLE:
1893 case EL_CONVEYOR_BELT_1_SWITCH_RIGHT:
1894 case EL_CONVEYOR_BELT_2_SWITCH_LEFT:
1895 case EL_CONVEYOR_BELT_2_SWITCH_MIDDLE:
1896 case EL_CONVEYOR_BELT_2_SWITCH_RIGHT:
1897 case EL_CONVEYOR_BELT_3_SWITCH_LEFT:
1898 case EL_CONVEYOR_BELT_3_SWITCH_MIDDLE:
1899 case EL_CONVEYOR_BELT_3_SWITCH_RIGHT:
1900 case EL_CONVEYOR_BELT_4_SWITCH_LEFT:
1901 case EL_CONVEYOR_BELT_4_SWITCH_MIDDLE:
1902 case EL_CONVEYOR_BELT_4_SWITCH_RIGHT:
1905 int belt_nr = getBeltNrFromBeltSwitchElement(Feld[x][y]);
1906 int belt_dir = getBeltDirFromBeltSwitchElement(Feld[x][y]);
1907 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(Feld[x][y]);
1909 if (game.belt_dir_nr[belt_nr] == 3) /* initial value */
1911 game.belt_dir[belt_nr] = belt_dir;
1912 game.belt_dir_nr[belt_nr] = belt_dir_nr;
1914 else /* more than one switch -- set it like the first switch */
1916 Feld[x][y] = Feld[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
1921 case EL_LIGHT_SWITCH_ACTIVE:
1923 game.light_time_left = level.time_light * FRAMES_PER_SECOND;
1926 case EL_INVISIBLE_STEELWALL:
1927 case EL_INVISIBLE_WALL:
1928 case EL_INVISIBLE_SAND:
1929 if (game.light_time_left > 0 ||
1930 game.lenses_time_left > 0)
1931 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
1934 case EL_EMC_MAGIC_BALL:
1935 if (game.ball_state)
1936 Feld[x][y] = EL_EMC_MAGIC_BALL_ACTIVE;
1939 case EL_EMC_MAGIC_BALL_SWITCH:
1940 if (game.ball_state)
1941 Feld[x][y] = EL_EMC_MAGIC_BALL_SWITCH_ACTIVE;
1944 case EL_TRIGGER_PLAYER:
1945 case EL_TRIGGER_ELEMENT:
1946 case EL_TRIGGER_CE_VALUE:
1947 case EL_TRIGGER_CE_SCORE:
1949 case EL_ANY_ELEMENT:
1950 case EL_CURRENT_CE_VALUE:
1951 case EL_CURRENT_CE_SCORE:
1968 /* reference elements should not be used on the playfield */
1969 Feld[x][y] = EL_EMPTY;
1973 if (IS_CUSTOM_ELEMENT(element))
1975 if (CAN_MOVE(element))
1978 if (!element_info[element].use_last_ce_value || init_game)
1979 CustomValue[x][y] = GET_NEW_CE_VALUE(Feld[x][y]);
1981 else if (IS_GROUP_ELEMENT(element))
1983 Feld[x][y] = GetElementFromGroupElement(element);
1985 InitField(x, y, init_game);
1992 CheckTriggeredElementChange(x, y, element, CE_CREATION_OF_X);
1995 inline static void InitField_WithBug1(int x, int y, boolean init_game)
1997 InitField(x, y, init_game);
1999 /* not needed to call InitMovDir() -- already done by InitField()! */
2000 if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2001 CAN_MOVE(Feld[x][y]))
2005 inline static void InitField_WithBug2(int x, int y, boolean init_game)
2007 int old_element = Feld[x][y];
2009 InitField(x, y, init_game);
2011 /* not needed to call InitMovDir() -- already done by InitField()! */
2012 if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2013 CAN_MOVE(old_element) &&
2014 (old_element < EL_MOLE_LEFT || old_element > EL_MOLE_DOWN))
2017 /* this case is in fact a combination of not less than three bugs:
2018 first, it calls InitMovDir() for elements that can move, although this is
2019 already done by InitField(); then, it checks the element that was at this
2020 field _before_ the call to InitField() (which can change it); lastly, it
2021 was not called for "mole with direction" elements, which were treated as
2022 "cannot move" due to (fixed) wrong element initialization in "src/init.c"
2026 static int get_key_element_from_nr(int key_nr)
2028 int key_base_element = (key_nr >= STD_NUM_KEYS ? EL_EMC_KEY_5 - STD_NUM_KEYS :
2029 level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2030 EL_EM_KEY_1 : EL_KEY_1);
2032 return key_base_element + key_nr;
2035 static int get_next_dropped_element(struct PlayerInfo *player)
2037 return (player->inventory_size > 0 ?
2038 player->inventory_element[player->inventory_size - 1] :
2039 player->inventory_infinite_element != EL_UNDEFINED ?
2040 player->inventory_infinite_element :
2041 player->dynabombs_left > 0 ?
2042 EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
2046 static int get_inventory_element_from_pos(struct PlayerInfo *player, int pos)
2048 /* pos >= 0: get element from bottom of the stack;
2049 pos < 0: get element from top of the stack */
2053 int min_inventory_size = -pos;
2054 int inventory_pos = player->inventory_size - min_inventory_size;
2055 int min_dynabombs_left = min_inventory_size - player->inventory_size;
2057 return (player->inventory_size >= min_inventory_size ?
2058 player->inventory_element[inventory_pos] :
2059 player->inventory_infinite_element != EL_UNDEFINED ?
2060 player->inventory_infinite_element :
2061 player->dynabombs_left >= min_dynabombs_left ?
2062 EL_DYNABOMB_PLAYER_1 + player->index_nr :
2067 int min_dynabombs_left = pos + 1;
2068 int min_inventory_size = pos + 1 - player->dynabombs_left;
2069 int inventory_pos = pos - player->dynabombs_left;
2071 return (player->inventory_infinite_element != EL_UNDEFINED ?
2072 player->inventory_infinite_element :
2073 player->dynabombs_left >= min_dynabombs_left ?
2074 EL_DYNABOMB_PLAYER_1 + player->index_nr :
2075 player->inventory_size >= min_inventory_size ?
2076 player->inventory_element[inventory_pos] :
2081 static int compareGamePanelOrderInfo(const void *object1, const void *object2)
2083 const struct GamePanelOrderInfo *gpo1 = (struct GamePanelOrderInfo *)object1;
2084 const struct GamePanelOrderInfo *gpo2 = (struct GamePanelOrderInfo *)object2;
2087 if (gpo1->sort_priority != gpo2->sort_priority)
2088 compare_result = gpo1->sort_priority - gpo2->sort_priority;
2090 compare_result = gpo1->nr - gpo2->nr;
2092 return compare_result;
2095 int getPlayerInventorySize(int player_nr)
2097 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2098 return level.native_em_level->ply[player_nr]->dynamite;
2099 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
2100 return level.native_sp_level->game_sp->red_disk_count;
2102 return stored_player[player_nr].inventory_size;
2105 void InitGameControlValues()
2109 for (i = 0; game_panel_controls[i].nr != -1; i++)
2111 struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2112 struct GamePanelOrderInfo *gpo = &game_panel_order[i];
2113 struct TextPosInfo *pos = gpc->pos;
2115 int type = gpc->type;
2119 Error(ERR_INFO, "'game_panel_controls' structure corrupted at %d", i);
2120 Error(ERR_EXIT, "this should not happen -- please debug");
2123 /* force update of game controls after initialization */
2124 gpc->value = gpc->last_value = -1;
2125 gpc->frame = gpc->last_frame = -1;
2126 gpc->gfx_frame = -1;
2128 /* determine panel value width for later calculation of alignment */
2129 if (type == TYPE_INTEGER || type == TYPE_STRING)
2131 pos->width = pos->size * getFontWidth(pos->font);
2132 pos->height = getFontHeight(pos->font);
2134 else if (type == TYPE_ELEMENT)
2136 pos->width = pos->size;
2137 pos->height = pos->size;
2140 /* fill structure for game panel draw order */
2142 gpo->sort_priority = pos->sort_priority;
2145 /* sort game panel controls according to sort_priority and control number */
2146 qsort(game_panel_order, NUM_GAME_PANEL_CONTROLS,
2147 sizeof(struct GamePanelOrderInfo), compareGamePanelOrderInfo);
2150 void UpdatePlayfieldElementCount()
2152 boolean use_element_count = FALSE;
2155 /* first check if it is needed at all to calculate playfield element count */
2156 for (i = GAME_PANEL_ELEMENT_COUNT_1; i <= GAME_PANEL_ELEMENT_COUNT_8; i++)
2157 if (!PANEL_DEACTIVATED(game_panel_controls[i].pos))
2158 use_element_count = TRUE;
2160 if (!use_element_count)
2163 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2164 element_info[i].element_count = 0;
2166 SCAN_PLAYFIELD(x, y)
2168 element_info[Feld[x][y]].element_count++;
2171 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
2172 for (j = 0; j < MAX_NUM_ELEMENTS; j++)
2173 if (IS_IN_GROUP(j, i))
2174 element_info[EL_GROUP_START + i].element_count +=
2175 element_info[j].element_count;
2178 void UpdateGameControlValues()
2181 int time = (local_player->LevelSolved ?
2182 local_player->LevelSolved_CountingTime :
2183 level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2184 level.native_em_level->lev->time :
2185 level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2186 level.native_sp_level->game_sp->time_played :
2187 level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2188 game_mm.energy_left :
2189 game.no_time_limit ? TimePlayed : TimeLeft);
2190 int score = (local_player->LevelSolved ?
2191 local_player->LevelSolved_CountingScore :
2192 level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2193 level.native_em_level->lev->score :
2194 level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2195 level.native_sp_level->game_sp->score :
2196 level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2198 local_player->score);
2199 int gems = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2200 level.native_em_level->lev->required :
2201 level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2202 level.native_sp_level->game_sp->infotrons_still_needed :
2203 level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2204 game_mm.kettles_still_needed :
2205 local_player->gems_still_needed);
2206 int exit_closed = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2207 level.native_em_level->lev->required > 0 :
2208 level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2209 level.native_sp_level->game_sp->infotrons_still_needed > 0 :
2210 level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2211 game_mm.kettles_still_needed > 0 ||
2212 game_mm.lights_still_needed > 0 :
2213 local_player->gems_still_needed > 0 ||
2214 local_player->sokobanfields_still_needed > 0 ||
2215 local_player->lights_still_needed > 0);
2216 int health = (local_player->LevelSolved ?
2217 local_player->LevelSolved_CountingHealth :
2218 level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2219 MM_HEALTH(game_mm.laser_overload_value) :
2220 local_player->health);
2222 UpdatePlayfieldElementCount();
2224 /* update game panel control values */
2226 game_panel_controls[GAME_PANEL_LEVEL_NUMBER].value = level_nr;
2227 game_panel_controls[GAME_PANEL_GEMS].value = gems;
2229 game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value = 0;
2230 for (i = 0; i < MAX_NUM_KEYS; i++)
2231 game_panel_controls[GAME_PANEL_KEY_1 + i].value = EL_EMPTY;
2232 game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_EMPTY;
2233 game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value = 0;
2235 if (game.centered_player_nr == -1)
2237 for (i = 0; i < MAX_PLAYERS; i++)
2239 /* only one player in Supaplex game engine */
2240 if (level.game_engine_type == GAME_ENGINE_TYPE_SP && i > 0)
2243 for (k = 0; k < MAX_NUM_KEYS; k++)
2245 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2247 if (level.native_em_level->ply[i]->keys & (1 << k))
2248 game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2249 get_key_element_from_nr(k);
2251 else if (stored_player[i].key[k])
2252 game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2253 get_key_element_from_nr(k);
2256 game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2257 getPlayerInventorySize(i);
2259 if (stored_player[i].num_white_keys > 0)
2260 game_panel_controls[GAME_PANEL_KEY_WHITE].value =
2263 game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2264 stored_player[i].num_white_keys;
2269 int player_nr = game.centered_player_nr;
2271 for (k = 0; k < MAX_NUM_KEYS; k++)
2273 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2275 if (level.native_em_level->ply[player_nr]->keys & (1 << k))
2276 game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2277 get_key_element_from_nr(k);
2279 else if (stored_player[player_nr].key[k])
2280 game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2281 get_key_element_from_nr(k);
2284 game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2285 getPlayerInventorySize(player_nr);
2287 if (stored_player[player_nr].num_white_keys > 0)
2288 game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_DC_KEY_WHITE;
2290 game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2291 stored_player[player_nr].num_white_keys;
2294 for (i = 0; i < NUM_PANEL_INVENTORY; i++)
2296 game_panel_controls[GAME_PANEL_INVENTORY_FIRST_1 + i].value =
2297 get_inventory_element_from_pos(local_player, i);
2298 game_panel_controls[GAME_PANEL_INVENTORY_LAST_1 + i].value =
2299 get_inventory_element_from_pos(local_player, -i - 1);
2302 game_panel_controls[GAME_PANEL_SCORE].value = score;
2303 game_panel_controls[GAME_PANEL_HIGHSCORE].value = highscore[0].Score;
2305 game_panel_controls[GAME_PANEL_TIME].value = time;
2307 game_panel_controls[GAME_PANEL_TIME_HH].value = time / 3600;
2308 game_panel_controls[GAME_PANEL_TIME_MM].value = (time / 60) % 60;
2309 game_panel_controls[GAME_PANEL_TIME_SS].value = time % 60;
2311 if (level.time == 0)
2312 game_panel_controls[GAME_PANEL_TIME_ANIM].value = 100;
2314 game_panel_controls[GAME_PANEL_TIME_ANIM].value = time * 100 / level.time;
2316 game_panel_controls[GAME_PANEL_HEALTH].value = health;
2317 game_panel_controls[GAME_PANEL_HEALTH_ANIM].value = health;
2319 game_panel_controls[GAME_PANEL_FRAME].value = FrameCounter;
2321 game_panel_controls[GAME_PANEL_SHIELD_NORMAL].value =
2322 (local_player->shield_normal_time_left > 0 ? EL_SHIELD_NORMAL_ACTIVE :
2324 game_panel_controls[GAME_PANEL_SHIELD_NORMAL_TIME].value =
2325 local_player->shield_normal_time_left;
2326 game_panel_controls[GAME_PANEL_SHIELD_DEADLY].value =
2327 (local_player->shield_deadly_time_left > 0 ? EL_SHIELD_DEADLY_ACTIVE :
2329 game_panel_controls[GAME_PANEL_SHIELD_DEADLY_TIME].value =
2330 local_player->shield_deadly_time_left;
2332 game_panel_controls[GAME_PANEL_EXIT].value =
2333 (exit_closed ? EL_EXIT_CLOSED : EL_EXIT_OPEN);
2335 game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL].value =
2336 (game.ball_state ? EL_EMC_MAGIC_BALL_ACTIVE : EL_EMC_MAGIC_BALL);
2337 game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL_SWITCH].value =
2338 (game.ball_state ? EL_EMC_MAGIC_BALL_SWITCH_ACTIVE :
2339 EL_EMC_MAGIC_BALL_SWITCH);
2341 game_panel_controls[GAME_PANEL_LIGHT_SWITCH].value =
2342 (game.light_time_left > 0 ? EL_LIGHT_SWITCH_ACTIVE : EL_LIGHT_SWITCH);
2343 game_panel_controls[GAME_PANEL_LIGHT_SWITCH_TIME].value =
2344 game.light_time_left;
2346 game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH].value =
2347 (game.timegate_time_left > 0 ? EL_TIMEGATE_OPEN : EL_TIMEGATE_CLOSED);
2348 game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH_TIME].value =
2349 game.timegate_time_left;
2351 game_panel_controls[GAME_PANEL_SWITCHGATE_SWITCH].value =
2352 EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
2354 game_panel_controls[GAME_PANEL_EMC_LENSES].value =
2355 (game.lenses_time_left > 0 ? EL_EMC_LENSES : EL_EMPTY);
2356 game_panel_controls[GAME_PANEL_EMC_LENSES_TIME].value =
2357 game.lenses_time_left;
2359 game_panel_controls[GAME_PANEL_EMC_MAGNIFIER].value =
2360 (game.magnify_time_left > 0 ? EL_EMC_MAGNIFIER : EL_EMPTY);
2361 game_panel_controls[GAME_PANEL_EMC_MAGNIFIER_TIME].value =
2362 game.magnify_time_left;
2364 game_panel_controls[GAME_PANEL_BALLOON_SWITCH].value =
2365 (game.wind_direction == MV_LEFT ? EL_BALLOON_SWITCH_LEFT :
2366 game.wind_direction == MV_RIGHT ? EL_BALLOON_SWITCH_RIGHT :
2367 game.wind_direction == MV_UP ? EL_BALLOON_SWITCH_UP :
2368 game.wind_direction == MV_DOWN ? EL_BALLOON_SWITCH_DOWN :
2369 EL_BALLOON_SWITCH_NONE);
2371 game_panel_controls[GAME_PANEL_DYNABOMB_NUMBER].value =
2372 local_player->dynabomb_count;
2373 game_panel_controls[GAME_PANEL_DYNABOMB_SIZE].value =
2374 local_player->dynabomb_size;
2375 game_panel_controls[GAME_PANEL_DYNABOMB_POWER].value =
2376 (local_player->dynabomb_xl ? EL_DYNABOMB_INCREASE_POWER : EL_EMPTY);
2378 game_panel_controls[GAME_PANEL_PENGUINS].value =
2379 local_player->friends_still_needed;
2381 game_panel_controls[GAME_PANEL_SOKOBAN_OBJECTS].value =
2382 local_player->sokobanfields_still_needed;
2383 game_panel_controls[GAME_PANEL_SOKOBAN_FIELDS].value =
2384 local_player->sokobanfields_still_needed;
2386 game_panel_controls[GAME_PANEL_ROBOT_WHEEL].value =
2387 (game.robot_wheel_active ? EL_ROBOT_WHEEL_ACTIVE : EL_ROBOT_WHEEL);
2389 for (i = 0; i < NUM_BELTS; i++)
2391 game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1 + i].value =
2392 (game.belt_dir[i] != MV_NONE ? EL_CONVEYOR_BELT_1_MIDDLE_ACTIVE :
2393 EL_CONVEYOR_BELT_1_MIDDLE) + i;
2394 game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1_SWITCH + i].value =
2395 getBeltSwitchElementFromBeltNrAndBeltDir(i, game.belt_dir[i]);
2398 game_panel_controls[GAME_PANEL_MAGIC_WALL].value =
2399 (game.magic_wall_active ? EL_MAGIC_WALL_ACTIVE : EL_MAGIC_WALL);
2400 game_panel_controls[GAME_PANEL_MAGIC_WALL_TIME].value =
2401 game.magic_wall_time_left;
2403 game_panel_controls[GAME_PANEL_GRAVITY_STATE].value =
2404 local_player->gravity;
2406 for (i = 0; i < NUM_PANEL_GRAPHICS; i++)
2407 game_panel_controls[GAME_PANEL_GRAPHIC_1 + i].value = EL_GRAPHIC_1 + i;
2409 for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2410 game_panel_controls[GAME_PANEL_ELEMENT_1 + i].value =
2411 (IS_DRAWABLE_ELEMENT(game.panel.element[i].id) ?
2412 game.panel.element[i].id : EL_UNDEFINED);
2414 for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2415 game_panel_controls[GAME_PANEL_ELEMENT_COUNT_1 + i].value =
2416 (IS_VALID_ELEMENT(game.panel.element_count[i].id) ?
2417 element_info[game.panel.element_count[i].id].element_count : 0);
2419 for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2420 game_panel_controls[GAME_PANEL_CE_SCORE_1 + i].value =
2421 (IS_CUSTOM_ELEMENT(game.panel.ce_score[i].id) ?
2422 element_info[game.panel.ce_score[i].id].collect_score : 0);
2424 for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2425 game_panel_controls[GAME_PANEL_CE_SCORE_1_ELEMENT + i].value =
2426 (IS_CUSTOM_ELEMENT(game.panel.ce_score_element[i].id) ?
2427 element_info[game.panel.ce_score_element[i].id].collect_score :
2430 game_panel_controls[GAME_PANEL_PLAYER_NAME].value = 0;
2431 game_panel_controls[GAME_PANEL_LEVEL_NAME].value = 0;
2432 game_panel_controls[GAME_PANEL_LEVEL_AUTHOR].value = 0;
2434 /* update game panel control frames */
2436 for (i = 0; game_panel_controls[i].nr != -1; i++)
2438 struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2440 if (gpc->type == TYPE_ELEMENT)
2442 if (gpc->value != EL_UNDEFINED && gpc->value != EL_EMPTY)
2444 int last_anim_random_frame = gfx.anim_random_frame;
2445 int element = gpc->value;
2446 int graphic = el2panelimg(element);
2448 if (gpc->value != gpc->last_value)
2451 gpc->gfx_random = INIT_GFX_RANDOM();
2457 if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2458 IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2459 gpc->gfx_random = INIT_GFX_RANDOM();
2462 if (ANIM_MODE(graphic) == ANIM_RANDOM)
2463 gfx.anim_random_frame = gpc->gfx_random;
2465 if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
2466 gpc->gfx_frame = element_info[element].collect_score;
2468 gpc->frame = getGraphicAnimationFrame(el2panelimg(gpc->value),
2471 if (ANIM_MODE(graphic) == ANIM_RANDOM)
2472 gfx.anim_random_frame = last_anim_random_frame;
2475 else if (gpc->type == TYPE_GRAPHIC)
2477 if (gpc->graphic != IMG_UNDEFINED)
2479 int last_anim_random_frame = gfx.anim_random_frame;
2480 int graphic = gpc->graphic;
2482 if (gpc->value != gpc->last_value)
2485 gpc->gfx_random = INIT_GFX_RANDOM();
2491 if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2492 IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2493 gpc->gfx_random = INIT_GFX_RANDOM();
2496 if (ANIM_MODE(graphic) == ANIM_RANDOM)
2497 gfx.anim_random_frame = gpc->gfx_random;
2499 gpc->frame = getGraphicAnimationFrame(graphic, gpc->gfx_frame);
2501 if (ANIM_MODE(graphic) == ANIM_RANDOM)
2502 gfx.anim_random_frame = last_anim_random_frame;
2508 void DisplayGameControlValues()
2510 boolean redraw_panel = FALSE;
2513 for (i = 0; game_panel_controls[i].nr != -1; i++)
2515 struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2517 if (PANEL_DEACTIVATED(gpc->pos))
2520 if (gpc->value == gpc->last_value &&
2521 gpc->frame == gpc->last_frame)
2524 redraw_panel = TRUE;
2530 /* copy default game door content to main double buffer */
2532 /* !!! CHECK AGAIN !!! */
2533 SetPanelBackground();
2534 // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
2535 DrawBackground(DX, DY, DXSIZE, DYSIZE);
2537 /* redraw game control buttons */
2538 RedrawGameButtons();
2540 SetGameStatus(GAME_MODE_PSEUDO_PANEL);
2542 for (i = 0; i < NUM_GAME_PANEL_CONTROLS; i++)
2544 int nr = game_panel_order[i].nr;
2545 struct GamePanelControlInfo *gpc = &game_panel_controls[nr];
2546 struct TextPosInfo *pos = gpc->pos;
2547 int type = gpc->type;
2548 int value = gpc->value;
2549 int frame = gpc->frame;
2550 int size = pos->size;
2551 int font = pos->font;
2552 boolean draw_masked = pos->draw_masked;
2553 int mask_mode = (draw_masked ? BLIT_MASKED : BLIT_OPAQUE);
2555 if (PANEL_DEACTIVATED(pos))
2558 gpc->last_value = value;
2559 gpc->last_frame = frame;
2561 if (type == TYPE_INTEGER)
2563 if (nr == GAME_PANEL_LEVEL_NUMBER ||
2564 nr == GAME_PANEL_TIME)
2566 boolean use_dynamic_size = (size == -1 ? TRUE : FALSE);
2568 if (use_dynamic_size) /* use dynamic number of digits */
2570 int value_change = (nr == GAME_PANEL_LEVEL_NUMBER ? 100 : 1000);
2571 int size1 = (nr == GAME_PANEL_LEVEL_NUMBER ? 2 : 3);
2572 int size2 = size1 + 1;
2573 int font1 = pos->font;
2574 int font2 = pos->font_alt;
2576 size = (value < value_change ? size1 : size2);
2577 font = (value < value_change ? font1 : font2);
2581 /* correct text size if "digits" is zero or less */
2583 size = strlen(int2str(value, size));
2585 /* dynamically correct text alignment */
2586 pos->width = size * getFontWidth(font);
2588 DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2589 int2str(value, size), font, mask_mode);
2591 else if (type == TYPE_ELEMENT)
2593 int element, graphic;
2597 int dst_x = PANEL_XPOS(pos);
2598 int dst_y = PANEL_YPOS(pos);
2600 if (value != EL_UNDEFINED && value != EL_EMPTY)
2603 graphic = el2panelimg(value);
2605 // printf("::: %d, '%s' [%d]\n", element, EL_NAME(element), size);
2607 if (element >= EL_GRAPHIC_1 && element <= EL_GRAPHIC_8 && size == 0)
2610 getSizedGraphicSource(graphic, frame, size, &src_bitmap,
2613 width = graphic_info[graphic].width * size / TILESIZE;
2614 height = graphic_info[graphic].height * size / TILESIZE;
2617 BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2620 BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2624 else if (type == TYPE_GRAPHIC)
2626 int graphic = gpc->graphic;
2627 int graphic_active = gpc->graphic_active;
2631 int dst_x = PANEL_XPOS(pos);
2632 int dst_y = PANEL_YPOS(pos);
2633 boolean skip = (pos->class == get_hash_from_key("mm_engine_only") &&
2634 level.game_engine_type != GAME_ENGINE_TYPE_MM);
2636 if (graphic != IMG_UNDEFINED && !skip)
2638 if (pos->style == STYLE_REVERSE)
2639 value = 100 - value;
2641 getGraphicSource(graphic_active, frame, &src_bitmap, &src_x, &src_y);
2643 if (pos->direction & MV_HORIZONTAL)
2645 width = graphic_info[graphic_active].width * value / 100;
2646 height = graphic_info[graphic_active].height;
2648 if (pos->direction == MV_LEFT)
2650 src_x += graphic_info[graphic_active].width - width;
2651 dst_x += graphic_info[graphic_active].width - width;
2656 width = graphic_info[graphic_active].width;
2657 height = graphic_info[graphic_active].height * value / 100;
2659 if (pos->direction == MV_UP)
2661 src_y += graphic_info[graphic_active].height - height;
2662 dst_y += graphic_info[graphic_active].height - height;
2667 BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2670 BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2673 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
2675 if (pos->direction & MV_HORIZONTAL)
2677 if (pos->direction == MV_RIGHT)
2684 dst_x = PANEL_XPOS(pos);
2687 width = graphic_info[graphic].width - width;
2691 if (pos->direction == MV_DOWN)
2698 dst_y = PANEL_YPOS(pos);
2701 height = graphic_info[graphic].height - height;
2705 BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2708 BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2712 else if (type == TYPE_STRING)
2714 boolean active = (value != 0);
2715 char *state_normal = "off";
2716 char *state_active = "on";
2717 char *state = (active ? state_active : state_normal);
2718 char *s = (nr == GAME_PANEL_GRAVITY_STATE ? state :
2719 nr == GAME_PANEL_PLAYER_NAME ? setup.player_name :
2720 nr == GAME_PANEL_LEVEL_NAME ? level.name :
2721 nr == GAME_PANEL_LEVEL_AUTHOR ? level.author : NULL);
2723 if (nr == GAME_PANEL_GRAVITY_STATE)
2725 int font1 = pos->font; /* (used for normal state) */
2726 int font2 = pos->font_alt; /* (used for active state) */
2728 font = (active ? font2 : font1);
2737 /* don't truncate output if "chars" is zero or less */
2740 /* dynamically correct text alignment */
2741 pos->width = size * getFontWidth(font);
2744 s_cut = getStringCopyN(s, size);
2746 DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2747 s_cut, font, mask_mode);
2753 redraw_mask |= REDRAW_DOOR_1;
2756 SetGameStatus(GAME_MODE_PLAYING);
2759 void UpdateAndDisplayGameControlValues()
2761 if (tape.deactivate_display)
2764 UpdateGameControlValues();
2765 DisplayGameControlValues();
2768 void UpdateGameDoorValues()
2770 UpdateGameControlValues();
2773 void DrawGameDoorValues()
2775 DisplayGameControlValues();
2780 =============================================================================
2782 -----------------------------------------------------------------------------
2783 initialize game engine due to level / tape version number
2784 =============================================================================
2787 static void InitGameEngine()
2789 int i, j, k, l, x, y;
2791 /* set game engine from tape file when re-playing, else from level file */
2792 game.engine_version = (tape.playing ? tape.engine_version :
2793 level.game_version);
2795 /* set single or multi-player game mode (needed for re-playing tapes) */
2796 game.team_mode = setup.team_mode;
2800 int num_players = 0;
2802 for (i = 0; i < MAX_PLAYERS; i++)
2803 if (tape.player_participates[i])
2806 /* multi-player tapes contain input data for more than one player */
2807 game.team_mode = (num_players > 1);
2810 /* ---------------------------------------------------------------------- */
2811 /* set flags for bugs and changes according to active game engine version */
2812 /* ---------------------------------------------------------------------- */
2815 Summary of bugfix/change:
2816 Fixed handling for custom elements that change when pushed by the player.
2818 Fixed/changed in version:
2822 Before 3.1.0, custom elements that "change when pushing" changed directly
2823 after the player started pushing them (until then handled in "DigField()").
2824 Since 3.1.0, these custom elements are not changed until the "pushing"
2825 move of the element is finished (now handled in "ContinueMoving()").
2827 Affected levels/tapes:
2828 The first condition is generally needed for all levels/tapes before version
2829 3.1.0, which might use the old behaviour before it was changed; known tapes
2830 that are affected are some tapes from the level set "Walpurgis Gardens" by
2832 The second condition is an exception from the above case and is needed for
2833 the special case of tapes recorded with game (not engine!) version 3.1.0 or
2834 above (including some development versions of 3.1.0), but before it was
2835 known that this change would break tapes like the above and was fixed in
2836 3.1.1, so that the changed behaviour was active although the engine version
2837 while recording maybe was before 3.1.0. There is at least one tape that is
2838 affected by this exception, which is the tape for the one-level set "Bug
2839 Machine" by Juergen Bonhagen.
2842 game.use_change_when_pushing_bug =
2843 (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2845 tape.game_version >= VERSION_IDENT(3,1,0,0) &&
2846 tape.game_version < VERSION_IDENT(3,1,1,0)));
2849 Summary of bugfix/change:
2850 Fixed handling for blocking the field the player leaves when moving.
2852 Fixed/changed in version:
2856 Before 3.1.1, when "block last field when moving" was enabled, the field
2857 the player is leaving when moving was blocked for the time of the move,
2858 and was directly unblocked afterwards. This resulted in the last field
2859 being blocked for exactly one less than the number of frames of one player
2860 move. Additionally, even when blocking was disabled, the last field was
2861 blocked for exactly one frame.
2862 Since 3.1.1, due to changes in player movement handling, the last field
2863 is not blocked at all when blocking is disabled. When blocking is enabled,
2864 the last field is blocked for exactly the number of frames of one player
2865 move. Additionally, if the player is Murphy, the hero of Supaplex, the
2866 last field is blocked for exactly one more than the number of frames of
2869 Affected levels/tapes:
2870 (!!! yet to be determined -- probably many !!!)
2873 game.use_block_last_field_bug =
2874 (game.engine_version < VERSION_IDENT(3,1,1,0));
2876 game_em.use_single_button =
2877 (game.engine_version > VERSION_IDENT(4,0,0,2));
2879 game_em.use_snap_key_bug =
2880 (game.engine_version < VERSION_IDENT(4,0,1,0));
2882 /* ---------------------------------------------------------------------- */
2884 /* set maximal allowed number of custom element changes per game frame */
2885 game.max_num_changes_per_frame = 1;
2887 /* default scan direction: scan playfield from top/left to bottom/right */
2888 InitPlayfieldScanMode(CA_ARG_SCAN_MODE_NORMAL);
2890 /* dynamically adjust element properties according to game engine version */
2891 InitElementPropertiesEngine(game.engine_version);
2894 printf("level %d: level version == %06d\n", level_nr, level.game_version);
2895 printf(" tape version == %06d [%s] [file: %06d]\n",
2896 tape.engine_version, (tape.playing ? "PLAYING" : "RECORDING"),
2898 printf(" => game.engine_version == %06d\n", game.engine_version);
2901 /* ---------- initialize player's initial move delay --------------------- */
2903 /* dynamically adjust player properties according to level information */
2904 for (i = 0; i < MAX_PLAYERS; i++)
2905 game.initial_move_delay_value[i] =
2906 get_move_delay_from_stepsize(level.initial_player_stepsize[i]);
2908 /* dynamically adjust player properties according to game engine version */
2909 for (i = 0; i < MAX_PLAYERS; i++)
2910 game.initial_move_delay[i] =
2911 (game.engine_version <= VERSION_IDENT(2,0,1,0) ?
2912 game.initial_move_delay_value[i] : 0);
2914 /* ---------- initialize player's initial push delay --------------------- */
2916 /* dynamically adjust player properties according to game engine version */
2917 game.initial_push_delay_value =
2918 (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
2920 /* ---------- initialize changing elements ------------------------------- */
2922 /* initialize changing elements information */
2923 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2925 struct ElementInfo *ei = &element_info[i];
2927 /* this pointer might have been changed in the level editor */
2928 ei->change = &ei->change_page[0];
2930 if (!IS_CUSTOM_ELEMENT(i))
2932 ei->change->target_element = EL_EMPTY_SPACE;
2933 ei->change->delay_fixed = 0;
2934 ei->change->delay_random = 0;
2935 ei->change->delay_frames = 1;
2938 for (j = 0; j < NUM_CHANGE_EVENTS; j++)
2940 ei->has_change_event[j] = FALSE;
2942 ei->event_page_nr[j] = 0;
2943 ei->event_page[j] = &ei->change_page[0];
2947 /* add changing elements from pre-defined list */
2948 for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
2950 struct ChangingElementInfo *ch_delay = &change_delay_list[i];
2951 struct ElementInfo *ei = &element_info[ch_delay->element];
2953 ei->change->target_element = ch_delay->target_element;
2954 ei->change->delay_fixed = ch_delay->change_delay;
2956 ei->change->pre_change_function = ch_delay->pre_change_function;
2957 ei->change->change_function = ch_delay->change_function;
2958 ei->change->post_change_function = ch_delay->post_change_function;
2960 ei->change->can_change = TRUE;
2961 ei->change->can_change_or_has_action = TRUE;
2963 ei->has_change_event[CE_DELAY] = TRUE;
2965 SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE, TRUE);
2966 SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE_OR_HAS_ACTION, TRUE);
2969 /* ---------- initialize internal run-time variables --------------------- */
2971 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2973 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
2975 for (j = 0; j < ei->num_change_pages; j++)
2977 ei->change_page[j].can_change_or_has_action =
2978 (ei->change_page[j].can_change |
2979 ei->change_page[j].has_action);
2983 /* add change events from custom element configuration */
2984 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2986 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
2988 for (j = 0; j < ei->num_change_pages; j++)
2990 if (!ei->change_page[j].can_change_or_has_action)
2993 for (k = 0; k < NUM_CHANGE_EVENTS; k++)
2995 /* only add event page for the first page found with this event */
2996 if (ei->change_page[j].has_event[k] && !(ei->has_change_event[k]))
2998 ei->has_change_event[k] = TRUE;
3000 ei->event_page_nr[k] = j;
3001 ei->event_page[k] = &ei->change_page[j];
3007 /* ---------- initialize reference elements in change conditions --------- */
3009 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3011 int element = EL_CUSTOM_START + i;
3012 struct ElementInfo *ei = &element_info[element];
3014 for (j = 0; j < ei->num_change_pages; j++)
3016 int trigger_element = ei->change_page[j].initial_trigger_element;
3018 if (trigger_element >= EL_PREV_CE_8 &&
3019 trigger_element <= EL_NEXT_CE_8)
3020 trigger_element = RESOLVED_REFERENCE_ELEMENT(element, trigger_element);
3022 ei->change_page[j].trigger_element = trigger_element;
3026 /* ---------- initialize run-time trigger player and element ------------- */
3028 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3030 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3032 for (j = 0; j < ei->num_change_pages; j++)
3034 ei->change_page[j].actual_trigger_element = EL_EMPTY;
3035 ei->change_page[j].actual_trigger_player = EL_EMPTY;
3036 ei->change_page[j].actual_trigger_player_bits = CH_PLAYER_NONE;
3037 ei->change_page[j].actual_trigger_side = CH_SIDE_NONE;
3038 ei->change_page[j].actual_trigger_ce_value = 0;
3039 ei->change_page[j].actual_trigger_ce_score = 0;
3043 /* ---------- initialize trigger events ---------------------------------- */
3045 /* initialize trigger events information */
3046 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3047 for (j = 0; j < NUM_CHANGE_EVENTS; j++)
3048 trigger_events[i][j] = FALSE;
3050 /* add trigger events from element change event properties */
3051 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3053 struct ElementInfo *ei = &element_info[i];
3055 for (j = 0; j < ei->num_change_pages; j++)
3057 if (!ei->change_page[j].can_change_or_has_action)
3060 if (ei->change_page[j].has_event[CE_BY_OTHER_ACTION])
3062 int trigger_element = ei->change_page[j].trigger_element;
3064 for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3066 if (ei->change_page[j].has_event[k])
3068 if (IS_GROUP_ELEMENT(trigger_element))
3070 struct ElementGroupInfo *group =
3071 element_info[trigger_element].group;
3073 for (l = 0; l < group->num_elements_resolved; l++)
3074 trigger_events[group->element_resolved[l]][k] = TRUE;
3076 else if (trigger_element == EL_ANY_ELEMENT)
3077 for (l = 0; l < MAX_NUM_ELEMENTS; l++)
3078 trigger_events[l][k] = TRUE;
3080 trigger_events[trigger_element][k] = TRUE;
3087 /* ---------- initialize push delay -------------------------------------- */
3089 /* initialize push delay values to default */
3090 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3092 if (!IS_CUSTOM_ELEMENT(i))
3094 /* set default push delay values (corrected since version 3.0.7-1) */
3095 if (game.engine_version < VERSION_IDENT(3,0,7,1))
3097 element_info[i].push_delay_fixed = 2;
3098 element_info[i].push_delay_random = 8;
3102 element_info[i].push_delay_fixed = 8;
3103 element_info[i].push_delay_random = 8;
3108 /* set push delay value for certain elements from pre-defined list */
3109 for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
3111 int e = push_delay_list[i].element;
3113 element_info[e].push_delay_fixed = push_delay_list[i].push_delay_fixed;
3114 element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
3117 /* set push delay value for Supaplex elements for newer engine versions */
3118 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
3120 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3122 if (IS_SP_ELEMENT(i))
3124 /* set SP push delay to just enough to push under a falling zonk */
3125 int delay = (game.engine_version >= VERSION_IDENT(3,1,1,0) ? 8 : 6);
3127 element_info[i].push_delay_fixed = delay;
3128 element_info[i].push_delay_random = 0;
3133 /* ---------- initialize move stepsize ----------------------------------- */
3135 /* initialize move stepsize values to default */
3136 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3137 if (!IS_CUSTOM_ELEMENT(i))
3138 element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
3140 /* set move stepsize value for certain elements from pre-defined list */
3141 for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
3143 int e = move_stepsize_list[i].element;
3145 element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
3148 /* ---------- initialize collect score ----------------------------------- */
3150 /* initialize collect score values for custom elements from initial value */
3151 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3152 if (IS_CUSTOM_ELEMENT(i))
3153 element_info[i].collect_score = element_info[i].collect_score_initial;
3155 /* ---------- initialize collect count ----------------------------------- */
3157 /* initialize collect count values for non-custom elements */
3158 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3159 if (!IS_CUSTOM_ELEMENT(i))
3160 element_info[i].collect_count_initial = 0;
3162 /* add collect count values for all elements from pre-defined list */
3163 for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
3164 element_info[collect_count_list[i].element].collect_count_initial =
3165 collect_count_list[i].count;
3167 /* ---------- initialize access direction -------------------------------- */
3169 /* initialize access direction values to default (access from every side) */
3170 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3171 if (!IS_CUSTOM_ELEMENT(i))
3172 element_info[i].access_direction = MV_ALL_DIRECTIONS;
3174 /* set access direction value for certain elements from pre-defined list */
3175 for (i = 0; access_direction_list[i].element != EL_UNDEFINED; i++)
3176 element_info[access_direction_list[i].element].access_direction =
3177 access_direction_list[i].direction;
3179 /* ---------- initialize explosion content ------------------------------- */
3180 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3182 if (IS_CUSTOM_ELEMENT(i))
3185 for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
3187 /* (content for EL_YAMYAM set at run-time with game.yamyam_content_nr) */
3189 element_info[i].content.e[x][y] =
3190 (i == EL_PLAYER_1 ? EL_EMERALD_YELLOW :
3191 i == EL_PLAYER_2 ? EL_EMERALD_RED :
3192 i == EL_PLAYER_3 ? EL_EMERALD :
3193 i == EL_PLAYER_4 ? EL_EMERALD_PURPLE :
3194 i == EL_MOLE ? EL_EMERALD_RED :
3195 i == EL_PENGUIN ? EL_EMERALD_PURPLE :
3196 i == EL_BUG ? (x == 1 && y == 1 ? EL_DIAMOND : EL_EMERALD) :
3197 i == EL_BD_BUTTERFLY ? EL_BD_DIAMOND :
3198 i == EL_SP_ELECTRON ? EL_SP_INFOTRON :
3199 i == EL_AMOEBA_TO_DIAMOND ? level.amoeba_content :
3200 i == EL_WALL_EMERALD ? EL_EMERALD :
3201 i == EL_WALL_DIAMOND ? EL_DIAMOND :
3202 i == EL_WALL_BD_DIAMOND ? EL_BD_DIAMOND :
3203 i == EL_WALL_EMERALD_YELLOW ? EL_EMERALD_YELLOW :
3204 i == EL_WALL_EMERALD_RED ? EL_EMERALD_RED :
3205 i == EL_WALL_EMERALD_PURPLE ? EL_EMERALD_PURPLE :
3206 i == EL_WALL_PEARL ? EL_PEARL :
3207 i == EL_WALL_CRYSTAL ? EL_CRYSTAL :
3212 /* ---------- initialize recursion detection ------------------------------ */
3213 recursion_loop_depth = 0;
3214 recursion_loop_detected = FALSE;
3215 recursion_loop_element = EL_UNDEFINED;
3217 /* ---------- initialize graphics engine ---------------------------------- */
3218 game.scroll_delay_value =
3219 (game.forced_scroll_delay_value != -1 ? game.forced_scroll_delay_value :
3220 setup.scroll_delay ? setup.scroll_delay_value : 0);
3221 game.scroll_delay_value =
3222 MIN(MAX(MIN_SCROLL_DELAY, game.scroll_delay_value), MAX_SCROLL_DELAY);
3224 /* ---------- initialize game engine snapshots ---------------------------- */
3225 for (i = 0; i < MAX_PLAYERS; i++)
3226 game.snapshot.last_action[i] = 0;
3227 game.snapshot.changed_action = FALSE;
3228 game.snapshot.collected_item = FALSE;
3229 game.snapshot.mode =
3230 (strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_STEP) ?
3231 SNAPSHOT_MODE_EVERY_STEP :
3232 strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_MOVE) ?
3233 SNAPSHOT_MODE_EVERY_MOVE :
3234 strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_COLLECT) ?
3235 SNAPSHOT_MODE_EVERY_COLLECT : SNAPSHOT_MODE_OFF);
3236 game.snapshot.save_snapshot = FALSE;
3238 /* ---------- initialize level time for Supaplex engine ------------------- */
3239 /* Supaplex levels with time limit currently unsupported -- should be added */
3240 if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
3244 int get_num_special_action(int element, int action_first, int action_last)
3246 int num_special_action = 0;
3249 for (i = action_first; i <= action_last; i++)
3251 boolean found = FALSE;
3253 for (j = 0; j < NUM_DIRECTIONS; j++)
3254 if (el_act_dir2img(element, i, j) !=
3255 el_act_dir2img(element, ACTION_DEFAULT, j))
3259 num_special_action++;
3264 return num_special_action;
3269 =============================================================================
3271 -----------------------------------------------------------------------------
3272 initialize and start new game
3273 =============================================================================
3278 int full_lev_fieldx = lev_fieldx + (BorderElement != EL_EMPTY ? 2 : 0);
3279 int full_lev_fieldy = lev_fieldy + (BorderElement != EL_EMPTY ? 2 : 0);
3280 int fade_mask = REDRAW_FIELD;
3282 boolean emulate_bd = TRUE; /* unless non-BOULDERDASH elements found */
3283 boolean emulate_sb = TRUE; /* unless non-SOKOBAN elements found */
3284 boolean emulate_sp = TRUE; /* unless non-SUPAPLEX elements found */
3285 int initial_move_dir = MV_DOWN;
3288 // required here to update video display before fading (FIX THIS)
3289 DrawMaskedBorder(REDRAW_DOOR_2);
3291 if (!game.restart_level)
3292 CloseDoor(DOOR_CLOSE_1);
3294 SetGameStatus(GAME_MODE_PLAYING);
3296 if (level_editor_test_game)
3297 FadeSkipNextFadeIn();
3299 FadeSetEnterScreen();
3301 if (CheckIfGlobalBorderOrPlayfieldViewportHasChanged())
3302 fade_mask = REDRAW_ALL;
3304 FadeLevelSoundsAndMusic();
3306 ExpireSoundLoops(TRUE);
3310 /* needed if different viewport properties defined for playing */
3311 ChangeViewportPropertiesIfNeeded();
3315 OpenDoor(GetDoorState() | DOOR_NO_DELAY | DOOR_FORCE_REDRAW);
3317 DrawCompleteVideoDisplay();
3320 InitGameControlValues();
3322 /* don't play tapes over network */
3323 network_playing = (options.network && !tape.playing);
3325 for (i = 0; i < MAX_PLAYERS; i++)
3327 struct PlayerInfo *player = &stored_player[i];
3329 player->index_nr = i;
3330 player->index_bit = (1 << i);
3331 player->element_nr = EL_PLAYER_1 + i;
3333 player->present = FALSE;
3334 player->active = FALSE;
3335 player->mapped = FALSE;
3337 player->killed = FALSE;
3338 player->reanimated = FALSE;
3341 player->effective_action = 0;
3342 player->programmed_action = 0;
3344 player->mouse_action.lx = 0;
3345 player->mouse_action.ly = 0;
3346 player->mouse_action.button = 0;
3348 player->effective_mouse_action.lx = 0;
3349 player->effective_mouse_action.ly = 0;
3350 player->effective_mouse_action.button = 0;
3353 player->score_final = 0;
3355 player->health = MAX_HEALTH;
3356 player->health_final = MAX_HEALTH;
3358 player->gems_still_needed = level.gems_needed;
3359 player->sokobanfields_still_needed = 0;
3360 player->lights_still_needed = 0;
3361 player->friends_still_needed = 0;
3363 for (j = 0; j < MAX_NUM_KEYS; j++)
3364 player->key[j] = FALSE;
3366 player->num_white_keys = 0;
3368 player->dynabomb_count = 0;
3369 player->dynabomb_size = 1;
3370 player->dynabombs_left = 0;
3371 player->dynabomb_xl = FALSE;
3373 player->MovDir = initial_move_dir;
3376 player->GfxDir = initial_move_dir;
3377 player->GfxAction = ACTION_DEFAULT;
3379 player->StepFrame = 0;
3381 player->initial_element = player->element_nr;
3382 player->artwork_element =
3383 (level.use_artwork_element[i] ? level.artwork_element[i] :
3384 player->element_nr);
3385 player->use_murphy = FALSE;
3387 player->block_last_field = FALSE; /* initialized in InitPlayerField() */
3388 player->block_delay_adjustment = 0; /* initialized in InitPlayerField() */
3390 player->gravity = level.initial_player_gravity[i];
3392 player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
3394 player->actual_frame_counter = 0;
3396 player->step_counter = 0;
3398 player->last_move_dir = initial_move_dir;
3400 player->is_active = FALSE;
3402 player->is_waiting = FALSE;
3403 player->is_moving = FALSE;
3404 player->is_auto_moving = FALSE;
3405 player->is_digging = FALSE;
3406 player->is_snapping = FALSE;
3407 player->is_collecting = FALSE;
3408 player->is_pushing = FALSE;
3409 player->is_switching = FALSE;
3410 player->is_dropping = FALSE;
3411 player->is_dropping_pressed = FALSE;
3413 player->is_bored = FALSE;
3414 player->is_sleeping = FALSE;
3416 player->was_waiting = TRUE;
3417 player->was_moving = FALSE;
3418 player->was_snapping = FALSE;
3419 player->was_dropping = FALSE;
3421 player->force_dropping = FALSE;
3423 player->frame_counter_bored = -1;
3424 player->frame_counter_sleeping = -1;
3426 player->anim_delay_counter = 0;
3427 player->post_delay_counter = 0;
3429 player->dir_waiting = initial_move_dir;
3430 player->action_waiting = ACTION_DEFAULT;
3431 player->last_action_waiting = ACTION_DEFAULT;
3432 player->special_action_bored = ACTION_DEFAULT;
3433 player->special_action_sleeping = ACTION_DEFAULT;
3435 player->switch_x = -1;
3436 player->switch_y = -1;
3438 player->drop_x = -1;
3439 player->drop_y = -1;
3441 player->show_envelope = 0;
3443 SetPlayerMoveSpeed(player, level.initial_player_stepsize[i], TRUE);
3445 player->push_delay = -1; /* initialized when pushing starts */
3446 player->push_delay_value = game.initial_push_delay_value;
3448 player->drop_delay = 0;
3449 player->drop_pressed_delay = 0;
3451 player->last_jx = -1;
3452 player->last_jy = -1;
3456 player->shield_normal_time_left = 0;
3457 player->shield_deadly_time_left = 0;
3459 player->inventory_infinite_element = EL_UNDEFINED;
3460 player->inventory_size = 0;
3462 if (level.use_initial_inventory[i])
3464 for (j = 0; j < level.initial_inventory_size[i]; j++)
3466 int element = level.initial_inventory_content[i][j];
3467 int collect_count = element_info[element].collect_count_initial;
3470 if (!IS_CUSTOM_ELEMENT(element))
3473 if (collect_count == 0)
3474 player->inventory_infinite_element = element;
3476 for (k = 0; k < collect_count; k++)
3477 if (player->inventory_size < MAX_INVENTORY_SIZE)
3478 player->inventory_element[player->inventory_size++] = element;
3482 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
3483 SnapField(player, 0, 0);
3485 player->LevelSolved = FALSE;
3486 player->GameOver = FALSE;
3488 player->LevelSolved_GameWon = FALSE;
3489 player->LevelSolved_GameEnd = FALSE;
3490 player->LevelSolved_PanelOff = FALSE;
3491 player->LevelSolved_SaveTape = FALSE;
3492 player->LevelSolved_SaveScore = FALSE;
3494 player->LevelSolved_CountingTime = 0;
3495 player->LevelSolved_CountingScore = 0;
3496 player->LevelSolved_CountingHealth = 0;
3498 map_player_action[i] = i;
3501 network_player_action_received = FALSE;
3503 #if defined(NETWORK_AVALIABLE)
3504 /* initial null action */
3505 if (network_playing)
3506 SendToServer_MovePlayer(MV_NONE);
3515 TimeLeft = level.time;
3518 ScreenMovDir = MV_NONE;
3522 ScrollStepSize = 0; /* will be correctly initialized by ScrollScreen() */
3524 AllPlayersGone = FALSE;
3526 game.no_time_limit = (level.time == 0);
3528 game.yamyam_content_nr = 0;
3529 game.robot_wheel_active = FALSE;
3530 game.magic_wall_active = FALSE;
3531 game.magic_wall_time_left = 0;
3532 game.light_time_left = 0;
3533 game.timegate_time_left = 0;
3534 game.switchgate_pos = 0;
3535 game.wind_direction = level.wind_direction_initial;
3537 game.lenses_time_left = 0;
3538 game.magnify_time_left = 0;
3540 game.ball_state = level.ball_state_initial;
3541 game.ball_content_nr = 0;
3543 game.envelope_active = FALSE;
3545 /* set focus to local player for network games, else to all players */
3546 game.centered_player_nr = (network_playing ? local_player->index_nr : -1);
3547 game.centered_player_nr_next = game.centered_player_nr;
3548 game.set_centered_player = FALSE;
3550 if (network_playing && tape.recording)
3552 /* store client dependent player focus when recording network games */
3553 tape.centered_player_nr_next = game.centered_player_nr_next;
3554 tape.set_centered_player = TRUE;
3557 for (i = 0; i < NUM_BELTS; i++)
3559 game.belt_dir[i] = MV_NONE;
3560 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
3563 for (i = 0; i < MAX_NUM_AMOEBA; i++)
3564 AmoebaCnt[i] = AmoebaCnt2[i] = 0;
3566 #if DEBUG_INIT_PLAYER
3569 printf("Player status at level initialization:\n");
3573 SCAN_PLAYFIELD(x, y)
3575 Feld[x][y] = level.field[x][y];
3576 MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
3577 ChangeDelay[x][y] = 0;
3578 ChangePage[x][y] = -1;
3579 CustomValue[x][y] = 0; /* initialized in InitField() */
3580 Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
3582 WasJustMoving[x][y] = 0;
3583 WasJustFalling[x][y] = 0;
3584 CheckCollision[x][y] = 0;
3585 CheckImpact[x][y] = 0;
3587 Pushed[x][y] = FALSE;
3589 ChangeCount[x][y] = 0;
3590 ChangeEvent[x][y] = -1;
3592 ExplodePhase[x][y] = 0;
3593 ExplodeDelay[x][y] = 0;
3594 ExplodeField[x][y] = EX_TYPE_NONE;
3596 RunnerVisit[x][y] = 0;
3597 PlayerVisit[x][y] = 0;
3600 GfxRandom[x][y] = INIT_GFX_RANDOM();
3601 GfxElement[x][y] = EL_UNDEFINED;
3602 GfxAction[x][y] = ACTION_DEFAULT;
3603 GfxDir[x][y] = MV_NONE;
3604 GfxRedraw[x][y] = GFX_REDRAW_NONE;
3607 SCAN_PLAYFIELD(x, y)
3609 if (emulate_bd && !IS_BD_ELEMENT(Feld[x][y]))
3611 if (emulate_sb && !IS_SB_ELEMENT(Feld[x][y]))
3613 if (emulate_sp && !IS_SP_ELEMENT(Feld[x][y]))
3616 InitField(x, y, TRUE);
3618 ResetGfxAnimation(x, y);
3623 for (i = 0; i < MAX_PLAYERS; i++)
3625 struct PlayerInfo *player = &stored_player[i];
3627 /* set number of special actions for bored and sleeping animation */
3628 player->num_special_action_bored =
3629 get_num_special_action(player->artwork_element,
3630 ACTION_BORING_1, ACTION_BORING_LAST);
3631 player->num_special_action_sleeping =
3632 get_num_special_action(player->artwork_element,
3633 ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
3636 game.emulation = (emulate_bd ? EMU_BOULDERDASH :
3637 emulate_sb ? EMU_SOKOBAN :
3638 emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
3640 /* initialize type of slippery elements */
3641 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3643 if (!IS_CUSTOM_ELEMENT(i))
3645 /* default: elements slip down either to the left or right randomly */
3646 element_info[i].slippery_type = SLIPPERY_ANY_RANDOM;
3648 /* SP style elements prefer to slip down on the left side */
3649 if (game.engine_version >= VERSION_IDENT(3,1,1,0) && IS_SP_ELEMENT(i))
3650 element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3652 /* BD style elements prefer to slip down on the left side */
3653 if (game.emulation == EMU_BOULDERDASH)
3654 element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3658 /* initialize explosion and ignition delay */
3659 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3661 if (!IS_CUSTOM_ELEMENT(i))
3664 int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
3665 game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
3666 game.emulation == EMU_SUPAPLEX ? 3 : 2);
3667 int last_phase = (num_phase + 1) * delay;
3668 int half_phase = (num_phase / 2) * delay;
3670 element_info[i].explosion_delay = last_phase - 1;
3671 element_info[i].ignition_delay = half_phase;
3673 if (i == EL_BLACK_ORB)
3674 element_info[i].ignition_delay = 1;
3678 /* correct non-moving belts to start moving left */
3679 for (i = 0; i < NUM_BELTS; i++)
3680 if (game.belt_dir[i] == MV_NONE)
3681 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
3683 #if USE_NEW_PLAYER_ASSIGNMENTS
3684 /* !!! SAME AS init.c:InitPlayerInfo() -- FIX THIS !!! */
3685 /* choose default local player */
3686 local_player = &stored_player[0];
3688 for (i = 0; i < MAX_PLAYERS; i++)
3689 stored_player[i].connected = FALSE;
3691 local_player->connected = TRUE;
3692 /* !!! SAME AS init.c:InitPlayerInfo() -- FIX THIS !!! */
3696 for (i = 0; i < MAX_PLAYERS; i++)
3697 stored_player[i].connected = tape.player_participates[i];
3699 else if (game.team_mode && !options.network)
3701 /* try to guess locally connected team mode players (needed for correct
3702 assignment of player figures from level to locally playing players) */
3704 for (i = 0; i < MAX_PLAYERS; i++)
3705 if (setup.input[i].use_joystick ||
3706 setup.input[i].key.left != KSYM_UNDEFINED)
3707 stored_player[i].connected = TRUE;
3710 #if DEBUG_INIT_PLAYER
3713 printf("Player status after level initialization:\n");
3715 for (i = 0; i < MAX_PLAYERS; i++)
3717 struct PlayerInfo *player = &stored_player[i];
3719 printf("- player %d: present == %d, connected == %d, active == %d",
3725 if (local_player == player)
3726 printf(" (local player)");
3733 #if DEBUG_INIT_PLAYER
3735 printf("Reassigning players ...\n");
3738 /* check if any connected player was not found in playfield */
3739 for (i = 0; i < MAX_PLAYERS; i++)
3741 struct PlayerInfo *player = &stored_player[i];
3743 if (player->connected && !player->present)
3745 struct PlayerInfo *field_player = NULL;
3747 #if DEBUG_INIT_PLAYER
3749 printf("- looking for field player for player %d ...\n", i + 1);
3752 /* assign first free player found that is present in the playfield */
3754 /* first try: look for unmapped playfield player that is not connected */
3755 for (j = 0; j < MAX_PLAYERS; j++)
3756 if (field_player == NULL &&
3757 stored_player[j].present &&
3758 !stored_player[j].mapped &&
3759 !stored_player[j].connected)
3760 field_player = &stored_player[j];
3762 /* second try: look for *any* unmapped playfield player */
3763 for (j = 0; j < MAX_PLAYERS; j++)
3764 if (field_player == NULL &&
3765 stored_player[j].present &&
3766 !stored_player[j].mapped)
3767 field_player = &stored_player[j];
3769 if (field_player != NULL)
3771 int jx = field_player->jx, jy = field_player->jy;
3773 #if DEBUG_INIT_PLAYER
3775 printf("- found player %d\n", field_player->index_nr + 1);
3778 player->present = FALSE;
3779 player->active = FALSE;
3781 field_player->present = TRUE;
3782 field_player->active = TRUE;
3785 player->initial_element = field_player->initial_element;
3786 player->artwork_element = field_player->artwork_element;
3788 player->block_last_field = field_player->block_last_field;
3789 player->block_delay_adjustment = field_player->block_delay_adjustment;
3792 StorePlayer[jx][jy] = field_player->element_nr;
3794 field_player->jx = field_player->last_jx = jx;
3795 field_player->jy = field_player->last_jy = jy;
3797 if (local_player == player)
3798 local_player = field_player;
3800 map_player_action[field_player->index_nr] = i;
3802 field_player->mapped = TRUE;
3804 #if DEBUG_INIT_PLAYER
3806 printf("- map_player_action[%d] == %d\n",
3807 field_player->index_nr + 1, i + 1);
3812 if (player->connected && player->present)
3813 player->mapped = TRUE;
3816 #if DEBUG_INIT_PLAYER
3819 printf("Player status after player assignment (first stage):\n");
3821 for (i = 0; i < MAX_PLAYERS; i++)
3823 struct PlayerInfo *player = &stored_player[i];
3825 printf("- player %d: present == %d, connected == %d, active == %d",
3831 if (local_player == player)
3832 printf(" (local player)");
3841 /* check if any connected player was not found in playfield */
3842 for (i = 0; i < MAX_PLAYERS; i++)
3844 struct PlayerInfo *player = &stored_player[i];
3846 if (player->connected && !player->present)
3848 for (j = 0; j < MAX_PLAYERS; j++)
3850 struct PlayerInfo *field_player = &stored_player[j];
3851 int jx = field_player->jx, jy = field_player->jy;
3853 /* assign first free player found that is present in the playfield */
3854 if (field_player->present && !field_player->connected)
3856 player->present = TRUE;
3857 player->active = TRUE;
3859 field_player->present = FALSE;
3860 field_player->active = FALSE;
3862 player->initial_element = field_player->initial_element;
3863 player->artwork_element = field_player->artwork_element;
3865 player->block_last_field = field_player->block_last_field;
3866 player->block_delay_adjustment = field_player->block_delay_adjustment;
3868 StorePlayer[jx][jy] = player->element_nr;
3870 player->jx = player->last_jx = jx;
3871 player->jy = player->last_jy = jy;
3881 printf("::: local_player->present == %d\n", local_player->present);
3886 /* when playing a tape, eliminate all players who do not participate */
3888 #if USE_NEW_PLAYER_ASSIGNMENTS
3890 if (!game.team_mode)
3892 for (i = 0; i < MAX_PLAYERS; i++)
3894 if (stored_player[i].active &&
3895 !tape.player_participates[map_player_action[i]])
3897 struct PlayerInfo *player = &stored_player[i];
3898 int jx = player->jx, jy = player->jy;
3900 #if DEBUG_INIT_PLAYER
3902 printf("Removing player %d at (%d, %d)\n", i + 1, jx, jy);
3905 player->active = FALSE;
3906 StorePlayer[jx][jy] = 0;
3907 Feld[jx][jy] = EL_EMPTY;
3914 for (i = 0; i < MAX_PLAYERS; i++)
3916 if (stored_player[i].active &&
3917 !tape.player_participates[i])
3919 struct PlayerInfo *player = &stored_player[i];
3920 int jx = player->jx, jy = player->jy;
3922 player->active = FALSE;
3923 StorePlayer[jx][jy] = 0;
3924 Feld[jx][jy] = EL_EMPTY;
3929 else if (!options.network && !game.team_mode) /* && !tape.playing */
3931 /* when in single player mode, eliminate all but the first active player */
3933 for (i = 0; i < MAX_PLAYERS; i++)
3935 if (stored_player[i].active)
3937 for (j = i + 1; j < MAX_PLAYERS; j++)
3939 if (stored_player[j].active)
3941 struct PlayerInfo *player = &stored_player[j];
3942 int jx = player->jx, jy = player->jy;
3944 player->active = FALSE;
3945 player->present = FALSE;
3947 StorePlayer[jx][jy] = 0;
3948 Feld[jx][jy] = EL_EMPTY;
3955 /* when recording the game, store which players take part in the game */
3958 #if USE_NEW_PLAYER_ASSIGNMENTS
3959 for (i = 0; i < MAX_PLAYERS; i++)
3960 if (stored_player[i].connected)
3961 tape.player_participates[i] = TRUE;
3963 for (i = 0; i < MAX_PLAYERS; i++)
3964 if (stored_player[i].active)
3965 tape.player_participates[i] = TRUE;
3969 #if DEBUG_INIT_PLAYER
3972 printf("Player status after player assignment (final stage):\n");
3974 for (i = 0; i < MAX_PLAYERS; i++)
3976 struct PlayerInfo *player = &stored_player[i];
3978 printf("- player %d: present == %d, connected == %d, active == %d",
3984 if (local_player == player)
3985 printf(" (local player)");
3992 if (BorderElement == EL_EMPTY)
3995 SBX_Right = lev_fieldx - SCR_FIELDX;
3997 SBY_Lower = lev_fieldy - SCR_FIELDY;
4002 SBX_Right = lev_fieldx - SCR_FIELDX + 1;
4004 SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
4007 if (full_lev_fieldx <= SCR_FIELDX)
4008 SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
4009 if (full_lev_fieldy <= SCR_FIELDY)
4010 SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
4012 if (EVEN(SCR_FIELDX) && full_lev_fieldx > SCR_FIELDX)
4014 if (EVEN(SCR_FIELDY) && full_lev_fieldy > SCR_FIELDY)
4017 /* if local player not found, look for custom element that might create
4018 the player (make some assumptions about the right custom element) */
4019 if (!local_player->present)
4021 int start_x = 0, start_y = 0;
4022 int found_rating = 0;
4023 int found_element = EL_UNDEFINED;
4024 int player_nr = local_player->index_nr;
4026 SCAN_PLAYFIELD(x, y)
4028 int element = Feld[x][y];
4033 if (level.use_start_element[player_nr] &&
4034 level.start_element[player_nr] == element &&
4041 found_element = element;
4044 if (!IS_CUSTOM_ELEMENT(element))
4047 if (CAN_CHANGE(element))
4049 for (i = 0; i < element_info[element].num_change_pages; i++)
4051 /* check for player created from custom element as single target */
4052 content = element_info[element].change_page[i].target_element;
4053 is_player = ELEM_IS_PLAYER(content);
4055 if (is_player && (found_rating < 3 ||
4056 (found_rating == 3 && element < found_element)))
4062 found_element = element;
4067 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
4069 /* check for player created from custom element as explosion content */
4070 content = element_info[element].content.e[xx][yy];
4071 is_player = ELEM_IS_PLAYER(content);
4073 if (is_player && (found_rating < 2 ||
4074 (found_rating == 2 && element < found_element)))
4076 start_x = x + xx - 1;
4077 start_y = y + yy - 1;
4080 found_element = element;
4083 if (!CAN_CHANGE(element))
4086 for (i = 0; i < element_info[element].num_change_pages; i++)
4088 /* check for player created from custom element as extended target */
4090 element_info[element].change_page[i].target_content.e[xx][yy];
4092 is_player = ELEM_IS_PLAYER(content);
4094 if (is_player && (found_rating < 1 ||
4095 (found_rating == 1 && element < found_element)))
4097 start_x = x + xx - 1;
4098 start_y = y + yy - 1;
4101 found_element = element;
4107 scroll_x = SCROLL_POSITION_X(start_x);
4108 scroll_y = SCROLL_POSITION_Y(start_y);
4112 scroll_x = SCROLL_POSITION_X(local_player->jx);
4113 scroll_y = SCROLL_POSITION_Y(local_player->jy);
4116 /* !!! FIX THIS (START) !!! */
4117 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
4119 InitGameEngine_EM();
4121 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
4123 InitGameEngine_SP();
4125 else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4127 InitGameEngine_MM();
4131 DrawLevel(REDRAW_FIELD);
4134 /* after drawing the level, correct some elements */
4135 if (game.timegate_time_left == 0)
4136 CloseAllOpenTimegates();
4139 /* blit playfield from scroll buffer to normal back buffer for fading in */
4140 BlitScreenToBitmap(backbuffer);
4141 /* !!! FIX THIS (END) !!! */
4143 DrawMaskedBorder(fade_mask);
4148 // full screen redraw is required at this point in the following cases:
4149 // - special editor door undrawn when game was started from level editor
4150 // - drawing area (playfield) was changed and has to be removed completely
4151 redraw_mask = REDRAW_ALL;
4155 if (!game.restart_level)
4157 /* copy default game door content to main double buffer */
4159 /* !!! CHECK AGAIN !!! */
4160 SetPanelBackground();
4161 // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
4162 DrawBackground(DX, DY, DXSIZE, DYSIZE);
4165 SetPanelBackground();
4166 SetDrawBackgroundMask(REDRAW_DOOR_1);
4168 UpdateAndDisplayGameControlValues();
4170 if (!game.restart_level)
4176 CreateGameButtons();
4181 /* copy actual game door content to door double buffer for OpenDoor() */
4182 BlitBitmap(drawto, bitmap_db_door_1, DX, DY, DXSIZE, DYSIZE, 0, 0);
4184 OpenDoor(DOOR_OPEN_ALL);
4186 PlaySound(SND_GAME_STARTING);
4188 if (setup.sound_music)
4191 KeyboardAutoRepeatOffUnlessAutoplay();
4193 #if DEBUG_INIT_PLAYER
4196 printf("Player status (final):\n");
4198 for (i = 0; i < MAX_PLAYERS; i++)
4200 struct PlayerInfo *player = &stored_player[i];
4202 printf("- player %d: present == %d, connected == %d, active == %d",
4208 if (local_player == player)
4209 printf(" (local player)");
4222 if (!game.restart_level && !tape.playing)
4224 LevelStats_incPlayed(level_nr);
4226 SaveLevelSetup_SeriesInfo();
4229 game.restart_level = FALSE;
4230 game.restart_game_message = NULL;
4232 if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4233 InitGameActions_MM();
4235 SaveEngineSnapshotToListInitial();
4238 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y,
4239 int actual_player_x, int actual_player_y)
4241 /* this is used for non-R'n'D game engines to update certain engine values */
4243 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
4245 actual_player_x = correctLevelPosX_EM(actual_player_x);
4246 actual_player_y = correctLevelPosY_EM(actual_player_y);
4249 /* needed to determine if sounds are played within the visible screen area */
4250 scroll_x = actual_scroll_x;
4251 scroll_y = actual_scroll_y;
4253 /* needed to get player position for "follow finger" playing input method */
4254 local_player->jx = actual_player_x;
4255 local_player->jy = actual_player_y;
4258 void InitMovDir(int x, int y)
4260 int i, element = Feld[x][y];
4261 static int xy[4][2] =
4268 static int direction[3][4] =
4270 { MV_RIGHT, MV_UP, MV_LEFT, MV_DOWN },
4271 { MV_LEFT, MV_DOWN, MV_RIGHT, MV_UP },
4272 { MV_LEFT, MV_RIGHT, MV_UP, MV_DOWN }
4281 Feld[x][y] = EL_BUG;
4282 MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
4285 case EL_SPACESHIP_RIGHT:
4286 case EL_SPACESHIP_UP:
4287 case EL_SPACESHIP_LEFT:
4288 case EL_SPACESHIP_DOWN:
4289 Feld[x][y] = EL_SPACESHIP;
4290 MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
4293 case EL_BD_BUTTERFLY_RIGHT:
4294 case EL_BD_BUTTERFLY_UP:
4295 case EL_BD_BUTTERFLY_LEFT:
4296 case EL_BD_BUTTERFLY_DOWN:
4297 Feld[x][y] = EL_BD_BUTTERFLY;
4298 MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
4301 case EL_BD_FIREFLY_RIGHT:
4302 case EL_BD_FIREFLY_UP:
4303 case EL_BD_FIREFLY_LEFT:
4304 case EL_BD_FIREFLY_DOWN:
4305 Feld[x][y] = EL_BD_FIREFLY;
4306 MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
4309 case EL_PACMAN_RIGHT:
4311 case EL_PACMAN_LEFT:
4312 case EL_PACMAN_DOWN:
4313 Feld[x][y] = EL_PACMAN;
4314 MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
4317 case EL_YAMYAM_LEFT:
4318 case EL_YAMYAM_RIGHT:
4320 case EL_YAMYAM_DOWN:
4321 Feld[x][y] = EL_YAMYAM;
4322 MovDir[x][y] = direction[2][element - EL_YAMYAM_LEFT];
4325 case EL_SP_SNIKSNAK:
4326 MovDir[x][y] = MV_UP;
4329 case EL_SP_ELECTRON:
4330 MovDir[x][y] = MV_LEFT;
4337 Feld[x][y] = EL_MOLE;
4338 MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
4342 if (IS_CUSTOM_ELEMENT(element))
4344 struct ElementInfo *ei = &element_info[element];
4345 int move_direction_initial = ei->move_direction_initial;
4346 int move_pattern = ei->move_pattern;
4348 if (move_direction_initial == MV_START_PREVIOUS)
4350 if (MovDir[x][y] != MV_NONE)
4353 move_direction_initial = MV_START_AUTOMATIC;
4356 if (move_direction_initial == MV_START_RANDOM)
4357 MovDir[x][y] = 1 << RND(4);
4358 else if (move_direction_initial & MV_ANY_DIRECTION)
4359 MovDir[x][y] = move_direction_initial;
4360 else if (move_pattern == MV_ALL_DIRECTIONS ||
4361 move_pattern == MV_TURNING_LEFT ||
4362 move_pattern == MV_TURNING_RIGHT ||
4363 move_pattern == MV_TURNING_LEFT_RIGHT ||
4364 move_pattern == MV_TURNING_RIGHT_LEFT ||
4365 move_pattern == MV_TURNING_RANDOM)
4366 MovDir[x][y] = 1 << RND(4);
4367 else if (move_pattern == MV_HORIZONTAL)
4368 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
4369 else if (move_pattern == MV_VERTICAL)
4370 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
4371 else if (move_pattern & MV_ANY_DIRECTION)
4372 MovDir[x][y] = element_info[element].move_pattern;
4373 else if (move_pattern == MV_ALONG_LEFT_SIDE ||
4374 move_pattern == MV_ALONG_RIGHT_SIDE)
4376 /* use random direction as default start direction */
4377 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
4378 MovDir[x][y] = 1 << RND(4);
4380 for (i = 0; i < NUM_DIRECTIONS; i++)
4382 int x1 = x + xy[i][0];
4383 int y1 = y + xy[i][1];
4385 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4387 if (move_pattern == MV_ALONG_RIGHT_SIDE)
4388 MovDir[x][y] = direction[0][i];
4390 MovDir[x][y] = direction[1][i];
4399 MovDir[x][y] = 1 << RND(4);
4401 if (element != EL_BUG &&
4402 element != EL_SPACESHIP &&
4403 element != EL_BD_BUTTERFLY &&
4404 element != EL_BD_FIREFLY)
4407 for (i = 0; i < NUM_DIRECTIONS; i++)
4409 int x1 = x + xy[i][0];
4410 int y1 = y + xy[i][1];
4412 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4414 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
4416 MovDir[x][y] = direction[0][i];
4419 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
4420 element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4422 MovDir[x][y] = direction[1][i];
4431 GfxDir[x][y] = MovDir[x][y];
4434 void InitAmoebaNr(int x, int y)
4437 int group_nr = AmoebeNachbarNr(x, y);
4441 for (i = 1; i < MAX_NUM_AMOEBA; i++)
4443 if (AmoebaCnt[i] == 0)
4451 AmoebaNr[x][y] = group_nr;
4452 AmoebaCnt[group_nr]++;
4453 AmoebaCnt2[group_nr]++;
4456 static void PlayerWins(struct PlayerInfo *player)
4458 player->LevelSolved = TRUE;
4459 player->GameOver = TRUE;
4461 player->score_final = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
4462 level.native_em_level->lev->score :
4463 level.game_engine_type == GAME_ENGINE_TYPE_MM ?
4466 player->health_final = (level.game_engine_type == GAME_ENGINE_TYPE_MM ?
4467 MM_HEALTH(game_mm.laser_overload_value) :
4470 player->LevelSolved_CountingTime = (game.no_time_limit ? TimePlayed :
4472 player->LevelSolved_CountingScore = player->score_final;
4473 player->LevelSolved_CountingHealth = player->health_final;
4478 static int time_count_steps;
4479 static int time, time_final;
4480 static int score, score_final;
4481 static int health, health_final;
4482 static int game_over_delay_1 = 0;
4483 static int game_over_delay_2 = 0;
4484 static int game_over_delay_3 = 0;
4485 int game_over_delay_value_1 = 50;
4486 int game_over_delay_value_2 = 25;
4487 int game_over_delay_value_3 = 50;
4489 if (!local_player->LevelSolved_GameWon)
4493 /* do not start end game actions before the player stops moving (to exit) */
4494 if (local_player->MovPos)
4497 local_player->LevelSolved_GameWon = TRUE;
4498 local_player->LevelSolved_SaveTape = tape.recording;
4499 local_player->LevelSolved_SaveScore = !tape.playing;
4503 LevelStats_incSolved(level_nr);
4505 SaveLevelSetup_SeriesInfo();
4508 if (tape.auto_play) /* tape might already be stopped here */
4509 tape.auto_play_level_solved = TRUE;
4513 game_over_delay_1 = 0;
4514 game_over_delay_2 = 0;
4515 game_over_delay_3 = game_over_delay_value_3;
4517 time = time_final = (game.no_time_limit ? TimePlayed : TimeLeft);
4518 score = score_final = local_player->score_final;
4519 health = health_final = local_player->health_final;
4521 if (level.score[SC_TIME_BONUS] > 0)
4526 score_final += TimeLeft * level.score[SC_TIME_BONUS];
4528 else if (game.no_time_limit && TimePlayed < 999)
4531 score_final += (999 - TimePlayed) * level.score[SC_TIME_BONUS];
4534 time_count_steps = MAX(1, ABS(time_final - time) / 100);
4536 game_over_delay_1 = game_over_delay_value_1;
4538 if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4541 score_final += health * level.score[SC_TIME_BONUS];
4543 game_over_delay_2 = game_over_delay_value_2;
4546 local_player->score_final = score_final;
4547 local_player->health_final = health_final;
4550 if (level_editor_test_game)
4553 score = score_final;
4555 local_player->LevelSolved_CountingTime = time;
4556 local_player->LevelSolved_CountingScore = score;
4558 game_panel_controls[GAME_PANEL_TIME].value = time;
4559 game_panel_controls[GAME_PANEL_SCORE].value = score;
4561 DisplayGameControlValues();
4564 if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
4566 if (ExitX >= 0 && ExitY >= 0) /* local player has left the level */
4568 /* close exit door after last player */
4569 if ((AllPlayersGone &&
4570 (Feld[ExitX][ExitY] == EL_EXIT_OPEN ||
4571 Feld[ExitX][ExitY] == EL_SP_EXIT_OPEN ||
4572 Feld[ExitX][ExitY] == EL_STEEL_EXIT_OPEN)) ||
4573 Feld[ExitX][ExitY] == EL_EM_EXIT_OPEN ||
4574 Feld[ExitX][ExitY] == EL_EM_STEEL_EXIT_OPEN)
4576 int element = Feld[ExitX][ExitY];
4578 Feld[ExitX][ExitY] =
4579 (element == EL_EXIT_OPEN ? EL_EXIT_CLOSING :
4580 element == EL_EM_EXIT_OPEN ? EL_EM_EXIT_CLOSING :
4581 element == EL_SP_EXIT_OPEN ? EL_SP_EXIT_CLOSING:
4582 element == EL_STEEL_EXIT_OPEN ? EL_STEEL_EXIT_CLOSING:
4583 EL_EM_STEEL_EXIT_CLOSING);
4585 PlayLevelSoundElementAction(ExitX, ExitY, element, ACTION_CLOSING);
4588 /* player disappears */
4589 DrawLevelField(ExitX, ExitY);
4592 for (i = 0; i < MAX_PLAYERS; i++)
4594 struct PlayerInfo *player = &stored_player[i];
4596 if (player->present)
4598 RemovePlayer(player);
4600 /* player disappears */
4601 DrawLevelField(player->jx, player->jy);
4606 PlaySound(SND_GAME_WINNING);
4609 if (game_over_delay_1 > 0)
4611 game_over_delay_1--;
4616 if (time != time_final)
4618 int time_to_go = ABS(time_final - time);
4619 int time_count_dir = (time < time_final ? +1 : -1);
4621 if (time_to_go < time_count_steps)
4622 time_count_steps = 1;
4624 time += time_count_steps * time_count_dir;
4625 score += time_count_steps * level.score[SC_TIME_BONUS];
4627 local_player->LevelSolved_CountingTime = time;
4628 local_player->LevelSolved_CountingScore = score;
4630 game_panel_controls[GAME_PANEL_TIME].value = time;
4631 game_panel_controls[GAME_PANEL_SCORE].value = score;
4633 DisplayGameControlValues();
4635 if (time == time_final)
4636 StopSound(SND_GAME_LEVELTIME_BONUS);
4637 else if (setup.sound_loops)
4638 PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
4640 PlaySound(SND_GAME_LEVELTIME_BONUS);
4645 if (game_over_delay_2 > 0)
4647 game_over_delay_2--;
4652 if (health != health_final)
4654 int health_count_dir = (health < health_final ? +1 : -1);
4656 health += health_count_dir;
4657 score += level.score[SC_TIME_BONUS];
4659 local_player->LevelSolved_CountingHealth = health;
4660 local_player->LevelSolved_CountingScore = score;
4662 game_panel_controls[GAME_PANEL_HEALTH].value = health;
4663 game_panel_controls[GAME_PANEL_SCORE].value = score;
4665 DisplayGameControlValues();
4667 if (health == health_final)
4668 StopSound(SND_GAME_LEVELTIME_BONUS);
4669 else if (setup.sound_loops)
4670 PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
4672 PlaySound(SND_GAME_LEVELTIME_BONUS);
4677 local_player->LevelSolved_PanelOff = TRUE;
4679 if (game_over_delay_3 > 0)
4681 game_over_delay_3--;
4692 boolean raise_level = FALSE;
4694 local_player->LevelSolved_GameEnd = TRUE;
4696 if (local_player->LevelSolved_SaveTape)
4698 /* make sure that request dialog to save tape does not open door again */
4699 if (!global.use_envelope_request)
4700 CloseDoor(DOOR_CLOSE_1);
4702 SaveTapeChecked(tape.level_nr); /* ask to save tape */
4705 /* if no tape is to be saved, close both doors simultaneously */
4706 CloseDoor(DOOR_CLOSE_ALL);
4708 if (level_editor_test_game)
4710 SetGameStatus(GAME_MODE_MAIN);
4717 if (!local_player->LevelSolved_SaveScore)
4719 SetGameStatus(GAME_MODE_MAIN);
4726 if (level_nr == leveldir_current->handicap_level)
4728 leveldir_current->handicap_level++;
4730 SaveLevelSetup_SeriesInfo();
4733 if (setup.increment_levels &&
4734 level_nr < leveldir_current->last_level)
4735 raise_level = TRUE; /* advance to next level */
4737 if ((hi_pos = NewHiScore()) >= 0)
4739 SetGameStatus(GAME_MODE_SCORES);
4741 DrawHallOfFame(hi_pos);
4751 SetGameStatus(GAME_MODE_MAIN);
4767 boolean one_score_entry_per_name = !program.many_scores_per_name;
4769 LoadScore(level_nr);
4771 if (strEqual(setup.player_name, EMPTY_PLAYER_NAME) ||
4772 local_player->score_final < highscore[MAX_SCORE_ENTRIES - 1].Score)
4775 for (k = 0; k < MAX_SCORE_ENTRIES; k++)
4777 if (local_player->score_final > highscore[k].Score)
4779 /* player has made it to the hall of fame */
4781 if (k < MAX_SCORE_ENTRIES - 1)
4783 int m = MAX_SCORE_ENTRIES - 1;
4785 if (one_score_entry_per_name)
4787 for (l = k; l < MAX_SCORE_ENTRIES; l++)
4788 if (strEqual(setup.player_name, highscore[l].Name))
4791 if (m == k) /* player's new highscore overwrites his old one */
4795 for (l = m; l > k; l--)
4797 strcpy(highscore[l].Name, highscore[l - 1].Name);
4798 highscore[l].Score = highscore[l - 1].Score;
4804 strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
4805 highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
4806 highscore[k].Score = local_player->score_final;
4811 else if (one_score_entry_per_name &&
4812 !strncmp(setup.player_name, highscore[k].Name,
4813 MAX_PLAYER_NAME_LEN))
4814 break; /* player already there with a higher score */
4818 SaveScore(level_nr);
4823 inline static int getElementMoveStepsizeExt(int x, int y, int direction)
4825 int element = Feld[x][y];
4826 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
4827 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
4828 int horiz_move = (dx != 0);
4829 int sign = (horiz_move ? dx : dy);
4830 int step = sign * element_info[element].move_stepsize;
4832 /* special values for move stepsize for spring and things on conveyor belt */
4835 if (CAN_FALL(element) &&
4836 y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
4837 step = sign * MOVE_STEPSIZE_NORMAL / 2;
4838 else if (element == EL_SPRING)
4839 step = sign * MOVE_STEPSIZE_NORMAL * 2;
4845 inline static int getElementMoveStepsize(int x, int y)
4847 return getElementMoveStepsizeExt(x, y, MovDir[x][y]);
4850 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
4852 if (player->GfxAction != action || player->GfxDir != dir)
4854 player->GfxAction = action;
4855 player->GfxDir = dir;
4857 player->StepFrame = 0;
4861 static void ResetGfxFrame(int x, int y)
4863 // profiling showed that "autotest" spends 10~20% of its time in this function
4864 if (DrawingDeactivatedField())
4867 int element = Feld[x][y];
4868 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
4870 if (graphic_info[graphic].anim_global_sync)
4871 GfxFrame[x][y] = FrameCounter;
4872 else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
4873 GfxFrame[x][y] = CustomValue[x][y];
4874 else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
4875 GfxFrame[x][y] = element_info[element].collect_score;
4876 else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
4877 GfxFrame[x][y] = ChangeDelay[x][y];
4880 static void ResetGfxAnimation(int x, int y)
4882 GfxAction[x][y] = ACTION_DEFAULT;
4883 GfxDir[x][y] = MovDir[x][y];
4886 ResetGfxFrame(x, y);
4889 static void ResetRandomAnimationValue(int x, int y)
4891 GfxRandom[x][y] = INIT_GFX_RANDOM();
4894 void InitMovingField(int x, int y, int direction)
4896 int element = Feld[x][y];
4897 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
4898 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
4901 boolean is_moving_before, is_moving_after;
4903 /* check if element was/is moving or being moved before/after mode change */
4904 is_moving_before = (WasJustMoving[x][y] != 0);
4905 is_moving_after = (getElementMoveStepsizeExt(x, y, direction) != 0);
4907 /* reset animation only for moving elements which change direction of moving
4908 or which just started or stopped moving
4909 (else CEs with property "can move" / "not moving" are reset each frame) */
4910 if (is_moving_before != is_moving_after ||
4911 direction != MovDir[x][y])
4912 ResetGfxAnimation(x, y);
4914 MovDir[x][y] = direction;
4915 GfxDir[x][y] = direction;
4917 GfxAction[x][y] = (!is_moving_after ? ACTION_WAITING :
4918 direction == MV_DOWN && CAN_FALL(element) ?
4919 ACTION_FALLING : ACTION_MOVING);
4921 /* this is needed for CEs with property "can move" / "not moving" */
4923 if (is_moving_after)
4925 if (Feld[newx][newy] == EL_EMPTY)
4926 Feld[newx][newy] = EL_BLOCKED;
4928 MovDir[newx][newy] = MovDir[x][y];
4930 CustomValue[newx][newy] = CustomValue[x][y];
4932 GfxFrame[newx][newy] = GfxFrame[x][y];
4933 GfxRandom[newx][newy] = GfxRandom[x][y];
4934 GfxAction[newx][newy] = GfxAction[x][y];
4935 GfxDir[newx][newy] = GfxDir[x][y];
4939 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
4941 int direction = MovDir[x][y];
4942 int newx = x + (direction & MV_LEFT ? -1 : direction & MV_RIGHT ? +1 : 0);
4943 int newy = y + (direction & MV_UP ? -1 : direction & MV_DOWN ? +1 : 0);
4949 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
4951 int oldx = x, oldy = y;
4952 int direction = MovDir[x][y];
4954 if (direction == MV_LEFT)
4956 else if (direction == MV_RIGHT)
4958 else if (direction == MV_UP)
4960 else if (direction == MV_DOWN)
4963 *comes_from_x = oldx;
4964 *comes_from_y = oldy;
4967 int MovingOrBlocked2Element(int x, int y)
4969 int element = Feld[x][y];
4971 if (element == EL_BLOCKED)
4975 Blocked2Moving(x, y, &oldx, &oldy);
4976 return Feld[oldx][oldy];
4982 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
4984 /* like MovingOrBlocked2Element(), but if element is moving
4985 and (x,y) is the field the moving element is just leaving,
4986 return EL_BLOCKED instead of the element value */
4987 int element = Feld[x][y];
4989 if (IS_MOVING(x, y))
4991 if (element == EL_BLOCKED)
4995 Blocked2Moving(x, y, &oldx, &oldy);
4996 return Feld[oldx][oldy];
5005 static void RemoveField(int x, int y)
5007 Feld[x][y] = EL_EMPTY;
5013 CustomValue[x][y] = 0;
5016 ChangeDelay[x][y] = 0;
5017 ChangePage[x][y] = -1;
5018 Pushed[x][y] = FALSE;
5020 GfxElement[x][y] = EL_UNDEFINED;
5021 GfxAction[x][y] = ACTION_DEFAULT;
5022 GfxDir[x][y] = MV_NONE;
5025 void RemoveMovingField(int x, int y)
5027 int oldx = x, oldy = y, newx = x, newy = y;
5028 int element = Feld[x][y];
5029 int next_element = EL_UNDEFINED;
5031 if (element != EL_BLOCKED && !IS_MOVING(x, y))
5034 if (IS_MOVING(x, y))
5036 Moving2Blocked(x, y, &newx, &newy);
5038 if (Feld[newx][newy] != EL_BLOCKED)
5040 /* element is moving, but target field is not free (blocked), but
5041 already occupied by something different (example: acid pool);
5042 in this case, only remove the moving field, but not the target */
5044 RemoveField(oldx, oldy);
5046 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5048 TEST_DrawLevelField(oldx, oldy);
5053 else if (element == EL_BLOCKED)
5055 Blocked2Moving(x, y, &oldx, &oldy);
5056 if (!IS_MOVING(oldx, oldy))
5060 if (element == EL_BLOCKED &&
5061 (Feld[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
5062 Feld[oldx][oldy] == EL_QUICKSAND_FAST_EMPTYING ||
5063 Feld[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
5064 Feld[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
5065 Feld[oldx][oldy] == EL_DC_MAGIC_WALL_EMPTYING ||
5066 Feld[oldx][oldy] == EL_AMOEBA_DROPPING))
5067 next_element = get_next_element(Feld[oldx][oldy]);
5069 RemoveField(oldx, oldy);
5070 RemoveField(newx, newy);
5072 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5074 if (next_element != EL_UNDEFINED)
5075 Feld[oldx][oldy] = next_element;
5077 TEST_DrawLevelField(oldx, oldy);
5078 TEST_DrawLevelField(newx, newy);
5081 void DrawDynamite(int x, int y)
5083 int sx = SCREENX(x), sy = SCREENY(y);
5084 int graphic = el2img(Feld[x][y]);
5087 if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
5090 if (IS_WALKABLE_INSIDE(Back[x][y]))
5094 DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
5095 else if (Store[x][y])
5096 DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
5098 frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5100 if (Back[x][y] || Store[x][y])
5101 DrawGraphicThruMask(sx, sy, graphic, frame);
5103 DrawGraphic(sx, sy, graphic, frame);
5106 void CheckDynamite(int x, int y)
5108 if (MovDelay[x][y] != 0) /* dynamite is still waiting to explode */
5112 if (MovDelay[x][y] != 0)
5115 PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5121 StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5126 static void setMinimalPlayerBoundaries(int *sx1, int *sy1, int *sx2, int *sy2)
5128 boolean num_checked_players = 0;
5131 for (i = 0; i < MAX_PLAYERS; i++)
5133 if (stored_player[i].active)
5135 int sx = stored_player[i].jx;
5136 int sy = stored_player[i].jy;
5138 if (num_checked_players == 0)
5145 *sx1 = MIN(*sx1, sx);
5146 *sy1 = MIN(*sy1, sy);
5147 *sx2 = MAX(*sx2, sx);
5148 *sy2 = MAX(*sy2, sy);
5151 num_checked_players++;
5156 static boolean checkIfAllPlayersFitToScreen_RND()
5158 int sx1 = 0, sy1 = 0, sx2 = 0, sy2 = 0;
5160 setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5162 return (sx2 - sx1 < SCR_FIELDX &&
5163 sy2 - sy1 < SCR_FIELDY);
5166 static void setScreenCenteredToAllPlayers(int *sx, int *sy)
5168 int sx1 = scroll_x, sy1 = scroll_y, sx2 = scroll_x, sy2 = scroll_y;
5170 setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5172 *sx = (sx1 + sx2) / 2;
5173 *sy = (sy1 + sy2) / 2;
5176 void DrawRelocateScreen(int old_x, int old_y, int x, int y, int move_dir,
5177 boolean center_screen, boolean quick_relocation)
5179 unsigned int frame_delay_value_old = GetVideoFrameDelay();
5180 boolean ffwd_delay = (tape.playing && tape.fast_forward);
5181 boolean no_delay = (tape.warp_forward);
5182 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5183 int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5184 int new_scroll_x, new_scroll_y;
5186 if (level.lazy_relocation && IN_VIS_FIELD(SCREENX(x), SCREENY(y)))
5188 /* case 1: quick relocation inside visible screen (without scrolling) */
5195 if (!level.shifted_relocation || center_screen)
5197 /* relocation _with_ centering of screen */
5199 new_scroll_x = SCROLL_POSITION_X(x);
5200 new_scroll_y = SCROLL_POSITION_Y(y);
5204 /* relocation _without_ centering of screen */
5206 int center_scroll_x = SCROLL_POSITION_X(old_x);
5207 int center_scroll_y = SCROLL_POSITION_Y(old_y);
5208 int offset_x = x + (scroll_x - center_scroll_x);
5209 int offset_y = y + (scroll_y - center_scroll_y);
5211 /* for new screen position, apply previous offset to center position */
5212 new_scroll_x = SCROLL_POSITION_X(offset_x);
5213 new_scroll_y = SCROLL_POSITION_Y(offset_y);
5216 if (quick_relocation)
5218 /* case 2: quick relocation (redraw without visible scrolling) */
5220 scroll_x = new_scroll_x;
5221 scroll_y = new_scroll_y;
5228 /* case 3: visible relocation (with scrolling to new position) */
5230 ScrollScreen(NULL, SCROLL_GO_ON); /* scroll last frame to full tile */
5232 SetVideoFrameDelay(wait_delay_value);
5234 while (scroll_x != new_scroll_x || scroll_y != new_scroll_y)
5237 int fx = FX, fy = FY;
5239 dx = (new_scroll_x < scroll_x ? +1 : new_scroll_x > scroll_x ? -1 : 0);
5240 dy = (new_scroll_y < scroll_y ? +1 : new_scroll_y > scroll_y ? -1 : 0);
5242 if (dx == 0 && dy == 0) /* no scrolling needed at all */
5248 fx += dx * TILEX / 2;
5249 fy += dy * TILEY / 2;
5251 ScrollLevel(dx, dy);
5254 /* scroll in two steps of half tile size to make things smoother */
5255 BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
5257 /* scroll second step to align at full tile size */
5258 BlitScreenToBitmap(window);
5264 SetVideoFrameDelay(frame_delay_value_old);
5267 void RelocatePlayer(int jx, int jy, int el_player_raw)
5269 int el_player = GET_PLAYER_ELEMENT(el_player_raw);
5270 int player_nr = GET_PLAYER_NR(el_player);
5271 struct PlayerInfo *player = &stored_player[player_nr];
5272 boolean ffwd_delay = (tape.playing && tape.fast_forward);
5273 boolean no_delay = (tape.warp_forward);
5274 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5275 int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5276 int old_jx = player->jx;
5277 int old_jy = player->jy;
5278 int old_element = Feld[old_jx][old_jy];
5279 int element = Feld[jx][jy];
5280 boolean player_relocated = (old_jx != jx || old_jy != jy);
5282 int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
5283 int move_dir_vert = (jy < old_jy ? MV_UP : jy > old_jy ? MV_DOWN : 0);
5284 int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
5285 int enter_side_vert = MV_DIR_OPPOSITE(move_dir_vert);
5286 int leave_side_horiz = move_dir_horiz;
5287 int leave_side_vert = move_dir_vert;
5288 int enter_side = enter_side_horiz | enter_side_vert;
5289 int leave_side = leave_side_horiz | leave_side_vert;
5291 if (player->GameOver) /* do not reanimate dead player */
5294 if (!player_relocated) /* no need to relocate the player */
5297 if (IS_PLAYER(jx, jy)) /* player already placed at new position */
5299 RemoveField(jx, jy); /* temporarily remove newly placed player */
5300 DrawLevelField(jx, jy);
5303 if (player->present)
5305 while (player->MovPos)
5307 ScrollPlayer(player, SCROLL_GO_ON);
5308 ScrollScreen(NULL, SCROLL_GO_ON);
5310 AdvanceFrameAndPlayerCounters(player->index_nr);
5314 BackToFront_WithFrameDelay(wait_delay_value);
5317 DrawPlayer(player); /* needed here only to cleanup last field */
5318 DrawLevelField(player->jx, player->jy); /* remove player graphic */
5320 player->is_moving = FALSE;
5323 if (IS_CUSTOM_ELEMENT(old_element))
5324 CheckElementChangeByPlayer(old_jx, old_jy, old_element,
5326 player->index_bit, leave_side);
5328 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
5330 player->index_bit, leave_side);
5332 Feld[jx][jy] = el_player;
5333 InitPlayerField(jx, jy, el_player, TRUE);
5335 /* "InitPlayerField()" above sets Feld[jx][jy] to EL_EMPTY, but it may be
5336 possible that the relocation target field did not contain a player element,
5337 but a walkable element, to which the new player was relocated -- in this
5338 case, restore that (already initialized!) element on the player field */
5339 if (!ELEM_IS_PLAYER(element)) /* player may be set on walkable element */
5341 Feld[jx][jy] = element; /* restore previously existing element */
5344 /* only visually relocate centered player */
5345 DrawRelocateScreen(old_jx, old_jy, player->jx, player->jy, player->MovDir,
5346 FALSE, level.instant_relocation);
5348 TestIfPlayerTouchesBadThing(jx, jy);
5349 TestIfPlayerTouchesCustomElement(jx, jy);
5351 if (IS_CUSTOM_ELEMENT(element))
5352 CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
5353 player->index_bit, enter_side);
5355 CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
5356 player->index_bit, enter_side);
5358 if (player->is_switching)
5360 /* ensure that relocation while still switching an element does not cause
5361 a new element to be treated as also switched directly after relocation
5362 (this is important for teleporter switches that teleport the player to
5363 a place where another teleporter switch is in the same direction, which
5364 would then incorrectly be treated as immediately switched before the
5365 direction key that caused the switch was released) */
5367 player->switch_x += jx - old_jx;
5368 player->switch_y += jy - old_jy;
5372 void Explode(int ex, int ey, int phase, int mode)
5378 /* !!! eliminate this variable !!! */
5379 int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
5381 if (game.explosions_delayed)
5383 ExplodeField[ex][ey] = mode;
5387 if (phase == EX_PHASE_START) /* initialize 'Store[][]' field */
5389 int center_element = Feld[ex][ey];
5390 int artwork_element, explosion_element; /* set these values later */
5392 /* remove things displayed in background while burning dynamite */
5393 if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
5396 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
5398 /* put moving element to center field (and let it explode there) */
5399 center_element = MovingOrBlocked2Element(ex, ey);
5400 RemoveMovingField(ex, ey);
5401 Feld[ex][ey] = center_element;
5404 /* now "center_element" is finally determined -- set related values now */
5405 artwork_element = center_element; /* for custom player artwork */
5406 explosion_element = center_element; /* for custom player artwork */
5408 if (IS_PLAYER(ex, ey))
5410 int player_nr = GET_PLAYER_NR(StorePlayer[ex][ey]);
5412 artwork_element = stored_player[player_nr].artwork_element;
5414 if (level.use_explosion_element[player_nr])
5416 explosion_element = level.explosion_element[player_nr];
5417 artwork_element = explosion_element;
5421 if (mode == EX_TYPE_NORMAL ||
5422 mode == EX_TYPE_CENTER ||
5423 mode == EX_TYPE_CROSS)
5424 PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
5426 last_phase = element_info[explosion_element].explosion_delay + 1;
5428 for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
5430 int xx = x - ex + 1;
5431 int yy = y - ey + 1;
5434 if (!IN_LEV_FIELD(x, y) ||
5435 (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
5436 (mode == EX_TYPE_CROSS && (x != ex && y != ey)))
5439 element = Feld[x][y];
5441 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
5443 element = MovingOrBlocked2Element(x, y);
5445 if (!IS_EXPLOSION_PROOF(element))
5446 RemoveMovingField(x, y);
5449 /* indestructible elements can only explode in center (but not flames) */
5450 if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
5451 mode == EX_TYPE_BORDER)) ||
5452 element == EL_FLAMES)
5455 /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
5456 behaviour, for example when touching a yamyam that explodes to rocks
5457 with active deadly shield, a rock is created under the player !!! */
5458 /* (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8) */
5460 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
5461 (game.engine_version < VERSION_IDENT(3,1,0,0) ||
5462 (x == ex && y == ey && mode != EX_TYPE_BORDER)))
5464 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
5467 if (IS_ACTIVE_BOMB(element))
5469 /* re-activate things under the bomb like gate or penguin */
5470 Feld[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
5477 /* save walkable background elements while explosion on same tile */
5478 if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
5479 (x != ex || y != ey || mode == EX_TYPE_BORDER))
5480 Back[x][y] = element;
5482 /* ignite explodable elements reached by other explosion */
5483 if (element == EL_EXPLOSION)
5484 element = Store2[x][y];
5486 if (AmoebaNr[x][y] &&
5487 (element == EL_AMOEBA_FULL ||
5488 element == EL_BD_AMOEBA ||
5489 element == EL_AMOEBA_GROWING))
5491 AmoebaCnt[AmoebaNr[x][y]]--;
5492 AmoebaCnt2[AmoebaNr[x][y]]--;
5497 if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
5499 int player_nr = StorePlayer[ex][ey] - EL_PLAYER_1;
5501 Store[x][y] = EL_PLAYER_IS_EXPLODING_1 + player_nr;
5503 if (PLAYERINFO(ex, ey)->use_murphy)
5504 Store[x][y] = EL_EMPTY;
5507 /* !!! check this case -- currently needed for rnd_rado_negundo_v,
5508 !!! levels 015 018 019 020 021 022 023 026 027 028 !!! */
5509 else if (ELEM_IS_PLAYER(center_element))
5510 Store[x][y] = EL_EMPTY;
5511 else if (center_element == EL_YAMYAM)
5512 Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
5513 else if (element_info[center_element].content.e[xx][yy] != EL_EMPTY)
5514 Store[x][y] = element_info[center_element].content.e[xx][yy];
5516 /* needed because EL_BD_BUTTERFLY is not defined as "CAN_EXPLODE"
5517 (killing EL_BD_BUTTERFLY with dynamite would result in BD diamond
5518 otherwise) -- FIX THIS !!! */
5519 else if (!CAN_EXPLODE(element) && element != EL_BD_BUTTERFLY)
5520 Store[x][y] = element_info[element].content.e[1][1];
5522 else if (!CAN_EXPLODE(element))
5523 Store[x][y] = element_info[element].content.e[1][1];
5526 Store[x][y] = EL_EMPTY;
5528 if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
5529 center_element == EL_AMOEBA_TO_DIAMOND)
5530 Store2[x][y] = element;
5532 Feld[x][y] = EL_EXPLOSION;
5533 GfxElement[x][y] = artwork_element;
5535 ExplodePhase[x][y] = 1;
5536 ExplodeDelay[x][y] = last_phase;
5541 if (center_element == EL_YAMYAM)
5542 game.yamyam_content_nr =
5543 (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
5555 GfxFrame[x][y] = 0; /* restart explosion animation */
5557 last_phase = ExplodeDelay[x][y];
5559 ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
5561 /* this can happen if the player leaves an explosion just in time */
5562 if (GfxElement[x][y] == EL_UNDEFINED)
5563 GfxElement[x][y] = EL_EMPTY;
5565 border_element = Store2[x][y];
5566 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5567 border_element = StorePlayer[x][y];
5569 if (phase == element_info[border_element].ignition_delay ||
5570 phase == last_phase)
5572 boolean border_explosion = FALSE;
5574 if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
5575 !PLAYER_EXPLOSION_PROTECTED(x, y))
5577 KillPlayerUnlessExplosionProtected(x, y);
5578 border_explosion = TRUE;
5580 else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
5582 Feld[x][y] = Store2[x][y];
5585 border_explosion = TRUE;
5587 else if (border_element == EL_AMOEBA_TO_DIAMOND)
5589 AmoebeUmwandeln(x, y);
5591 border_explosion = TRUE;
5594 /* if an element just explodes due to another explosion (chain-reaction),
5595 do not immediately end the new explosion when it was the last frame of
5596 the explosion (as it would be done in the following "if"-statement!) */
5597 if (border_explosion && phase == last_phase)
5601 if (phase == last_phase)
5605 element = Feld[x][y] = Store[x][y];
5606 Store[x][y] = Store2[x][y] = 0;
5607 GfxElement[x][y] = EL_UNDEFINED;
5609 /* player can escape from explosions and might therefore be still alive */
5610 if (element >= EL_PLAYER_IS_EXPLODING_1 &&
5611 element <= EL_PLAYER_IS_EXPLODING_4)
5613 int player_nr = element - EL_PLAYER_IS_EXPLODING_1;
5614 int explosion_element = EL_PLAYER_1 + player_nr;
5615 int xx = MIN(MAX(0, x - stored_player[player_nr].jx + 1), 2);
5616 int yy = MIN(MAX(0, y - stored_player[player_nr].jy + 1), 2);
5618 if (level.use_explosion_element[player_nr])
5619 explosion_element = level.explosion_element[player_nr];
5621 Feld[x][y] = (stored_player[player_nr].active ? EL_EMPTY :
5622 element_info[explosion_element].content.e[xx][yy]);
5625 /* restore probably existing indestructible background element */
5626 if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
5627 element = Feld[x][y] = Back[x][y];
5630 MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
5631 GfxDir[x][y] = MV_NONE;
5632 ChangeDelay[x][y] = 0;
5633 ChangePage[x][y] = -1;
5635 CustomValue[x][y] = 0;
5637 InitField_WithBug2(x, y, FALSE);
5639 TEST_DrawLevelField(x, y);
5641 TestIfElementTouchesCustomElement(x, y);
5643 if (GFX_CRUMBLED(element))
5644 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
5646 if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
5647 StorePlayer[x][y] = 0;
5649 if (ELEM_IS_PLAYER(element))
5650 RelocatePlayer(x, y, element);
5652 else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
5654 int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
5655 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5658 TEST_DrawLevelFieldCrumbled(x, y);
5660 if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
5662 DrawLevelElement(x, y, Back[x][y]);
5663 DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
5665 else if (IS_WALKABLE_UNDER(Back[x][y]))
5667 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5668 DrawLevelElementThruMask(x, y, Back[x][y]);
5670 else if (!IS_WALKABLE_INSIDE(Back[x][y]))
5671 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5675 void DynaExplode(int ex, int ey)
5678 int dynabomb_element = Feld[ex][ey];
5679 int dynabomb_size = 1;
5680 boolean dynabomb_xl = FALSE;
5681 struct PlayerInfo *player;
5682 static int xy[4][2] =
5690 if (IS_ACTIVE_BOMB(dynabomb_element))
5692 player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
5693 dynabomb_size = player->dynabomb_size;
5694 dynabomb_xl = player->dynabomb_xl;
5695 player->dynabombs_left++;
5698 Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
5700 for (i = 0; i < NUM_DIRECTIONS; i++)
5702 for (j = 1; j <= dynabomb_size; j++)
5704 int x = ex + j * xy[i][0];
5705 int y = ey + j * xy[i][1];
5708 if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
5711 element = Feld[x][y];
5713 /* do not restart explosions of fields with active bombs */
5714 if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
5717 Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
5719 if (element != EL_EMPTY && element != EL_EXPLOSION &&
5720 !IS_DIGGABLE(element) && !dynabomb_xl)
5726 void Bang(int x, int y)
5728 int element = MovingOrBlocked2Element(x, y);
5729 int explosion_type = EX_TYPE_NORMAL;
5731 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5733 struct PlayerInfo *player = PLAYERINFO(x, y);
5735 element = Feld[x][y] = player->initial_element;
5737 if (level.use_explosion_element[player->index_nr])
5739 int explosion_element = level.explosion_element[player->index_nr];
5741 if (element_info[explosion_element].explosion_type == EXPLODES_CROSS)
5742 explosion_type = EX_TYPE_CROSS;
5743 else if (element_info[explosion_element].explosion_type == EXPLODES_1X1)
5744 explosion_type = EX_TYPE_CENTER;
5752 case EL_BD_BUTTERFLY:
5755 case EL_DARK_YAMYAM:
5759 RaiseScoreElement(element);
5762 case EL_DYNABOMB_PLAYER_1_ACTIVE:
5763 case EL_DYNABOMB_PLAYER_2_ACTIVE:
5764 case EL_DYNABOMB_PLAYER_3_ACTIVE:
5765 case EL_DYNABOMB_PLAYER_4_ACTIVE:
5766 case EL_DYNABOMB_INCREASE_NUMBER:
5767 case EL_DYNABOMB_INCREASE_SIZE:
5768 case EL_DYNABOMB_INCREASE_POWER:
5769 explosion_type = EX_TYPE_DYNA;
5772 case EL_DC_LANDMINE:
5773 explosion_type = EX_TYPE_CENTER;
5778 case EL_LAMP_ACTIVE:
5779 case EL_AMOEBA_TO_DIAMOND:
5780 if (!IS_PLAYER(x, y)) /* penguin and player may be at same field */
5781 explosion_type = EX_TYPE_CENTER;
5785 if (element_info[element].explosion_type == EXPLODES_CROSS)
5786 explosion_type = EX_TYPE_CROSS;
5787 else if (element_info[element].explosion_type == EXPLODES_1X1)
5788 explosion_type = EX_TYPE_CENTER;
5792 if (explosion_type == EX_TYPE_DYNA)
5795 Explode(x, y, EX_PHASE_START, explosion_type);
5797 CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
5800 void SplashAcid(int x, int y)
5802 if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
5803 (!IN_LEV_FIELD(x - 1, y - 2) ||
5804 !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
5805 Feld[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
5807 if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
5808 (!IN_LEV_FIELD(x + 1, y - 2) ||
5809 !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
5810 Feld[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
5812 PlayLevelSound(x, y, SND_ACID_SPLASHING);
5815 static void InitBeltMovement()
5817 static int belt_base_element[4] =
5819 EL_CONVEYOR_BELT_1_LEFT,
5820 EL_CONVEYOR_BELT_2_LEFT,
5821 EL_CONVEYOR_BELT_3_LEFT,
5822 EL_CONVEYOR_BELT_4_LEFT
5824 static int belt_base_active_element[4] =
5826 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
5827 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
5828 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
5829 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
5834 /* set frame order for belt animation graphic according to belt direction */
5835 for (i = 0; i < NUM_BELTS; i++)
5839 for (j = 0; j < NUM_BELT_PARTS; j++)
5841 int element = belt_base_active_element[belt_nr] + j;
5842 int graphic_1 = el2img(element);
5843 int graphic_2 = el2panelimg(element);
5845 if (game.belt_dir[i] == MV_LEFT)
5847 graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
5848 graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
5852 graphic_info[graphic_1].anim_mode |= ANIM_REVERSE;
5853 graphic_info[graphic_2].anim_mode |= ANIM_REVERSE;
5858 SCAN_PLAYFIELD(x, y)
5860 int element = Feld[x][y];
5862 for (i = 0; i < NUM_BELTS; i++)
5864 if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
5866 int e_belt_nr = getBeltNrFromBeltElement(element);
5869 if (e_belt_nr == belt_nr)
5871 int belt_part = Feld[x][y] - belt_base_element[belt_nr];
5873 Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
5880 static void ToggleBeltSwitch(int x, int y)
5882 static int belt_base_element[4] =
5884 EL_CONVEYOR_BELT_1_LEFT,
5885 EL_CONVEYOR_BELT_2_LEFT,
5886 EL_CONVEYOR_BELT_3_LEFT,
5887 EL_CONVEYOR_BELT_4_LEFT
5889 static int belt_base_active_element[4] =
5891 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
5892 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
5893 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
5894 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
5896 static int belt_base_switch_element[4] =
5898 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
5899 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
5900 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
5901 EL_CONVEYOR_BELT_4_SWITCH_LEFT
5903 static int belt_move_dir[4] =
5911 int element = Feld[x][y];
5912 int belt_nr = getBeltNrFromBeltSwitchElement(element);
5913 int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
5914 int belt_dir = belt_move_dir[belt_dir_nr];
5917 if (!IS_BELT_SWITCH(element))
5920 game.belt_dir_nr[belt_nr] = belt_dir_nr;
5921 game.belt_dir[belt_nr] = belt_dir;
5923 if (belt_dir_nr == 3)
5926 /* set frame order for belt animation graphic according to belt direction */
5927 for (i = 0; i < NUM_BELT_PARTS; i++)
5929 int element = belt_base_active_element[belt_nr] + i;
5930 int graphic_1 = el2img(element);
5931 int graphic_2 = el2panelimg(element);
5933 if (belt_dir == MV_LEFT)
5935 graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
5936 graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
5940 graphic_info[graphic_1].anim_mode |= ANIM_REVERSE;
5941 graphic_info[graphic_2].anim_mode |= ANIM_REVERSE;
5945 SCAN_PLAYFIELD(xx, yy)
5947 int element = Feld[xx][yy];
5949 if (IS_BELT_SWITCH(element))
5951 int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
5953 if (e_belt_nr == belt_nr)
5955 Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
5956 TEST_DrawLevelField(xx, yy);
5959 else if (IS_BELT(element) && belt_dir != MV_NONE)
5961 int e_belt_nr = getBeltNrFromBeltElement(element);
5963 if (e_belt_nr == belt_nr)
5965 int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
5967 Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
5968 TEST_DrawLevelField(xx, yy);
5971 else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
5973 int e_belt_nr = getBeltNrFromBeltActiveElement(element);
5975 if (e_belt_nr == belt_nr)
5977 int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
5979 Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
5980 TEST_DrawLevelField(xx, yy);
5986 static void ToggleSwitchgateSwitch(int x, int y)
5990 game.switchgate_pos = !game.switchgate_pos;
5992 SCAN_PLAYFIELD(xx, yy)
5994 int element = Feld[xx][yy];
5996 if (element == EL_SWITCHGATE_SWITCH_UP)
5998 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_DOWN;
5999 TEST_DrawLevelField(xx, yy);
6001 else if (element == EL_SWITCHGATE_SWITCH_DOWN)
6003 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP;
6004 TEST_DrawLevelField(xx, yy);
6006 else if (element == EL_DC_SWITCHGATE_SWITCH_UP)
6008 Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_DOWN;
6009 TEST_DrawLevelField(xx, yy);
6011 else if (element == EL_DC_SWITCHGATE_SWITCH_DOWN)
6013 Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP;
6014 TEST_DrawLevelField(xx, yy);
6016 else if (element == EL_SWITCHGATE_OPEN ||
6017 element == EL_SWITCHGATE_OPENING)
6019 Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
6021 PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
6023 else if (element == EL_SWITCHGATE_CLOSED ||
6024 element == EL_SWITCHGATE_CLOSING)
6026 Feld[xx][yy] = EL_SWITCHGATE_OPENING;
6028 PlayLevelSoundAction(xx, yy, ACTION_OPENING);
6033 static int getInvisibleActiveFromInvisibleElement(int element)
6035 return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
6036 element == EL_INVISIBLE_WALL ? EL_INVISIBLE_WALL_ACTIVE :
6037 element == EL_INVISIBLE_SAND ? EL_INVISIBLE_SAND_ACTIVE :
6041 static int getInvisibleFromInvisibleActiveElement(int element)
6043 return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
6044 element == EL_INVISIBLE_WALL_ACTIVE ? EL_INVISIBLE_WALL :
6045 element == EL_INVISIBLE_SAND_ACTIVE ? EL_INVISIBLE_SAND :
6049 static void RedrawAllLightSwitchesAndInvisibleElements()
6053 SCAN_PLAYFIELD(x, y)
6055 int element = Feld[x][y];
6057 if (element == EL_LIGHT_SWITCH &&
6058 game.light_time_left > 0)
6060 Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
6061 TEST_DrawLevelField(x, y);
6063 else if (element == EL_LIGHT_SWITCH_ACTIVE &&
6064 game.light_time_left == 0)
6066 Feld[x][y] = EL_LIGHT_SWITCH;
6067 TEST_DrawLevelField(x, y);
6069 else if (element == EL_EMC_DRIPPER &&
6070 game.light_time_left > 0)
6072 Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
6073 TEST_DrawLevelField(x, y);
6075 else if (element == EL_EMC_DRIPPER_ACTIVE &&
6076 game.light_time_left == 0)
6078 Feld[x][y] = EL_EMC_DRIPPER;
6079 TEST_DrawLevelField(x, y);
6081 else if (element == EL_INVISIBLE_STEELWALL ||
6082 element == EL_INVISIBLE_WALL ||
6083 element == EL_INVISIBLE_SAND)
6085 if (game.light_time_left > 0)
6086 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
6088 TEST_DrawLevelField(x, y);
6090 /* uncrumble neighbour fields, if needed */
6091 if (element == EL_INVISIBLE_SAND)
6092 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6094 else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6095 element == EL_INVISIBLE_WALL_ACTIVE ||
6096 element == EL_INVISIBLE_SAND_ACTIVE)
6098 if (game.light_time_left == 0)
6099 Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
6101 TEST_DrawLevelField(x, y);
6103 /* re-crumble neighbour fields, if needed */
6104 if (element == EL_INVISIBLE_SAND)
6105 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6110 static void RedrawAllInvisibleElementsForLenses()
6114 SCAN_PLAYFIELD(x, y)
6116 int element = Feld[x][y];
6118 if (element == EL_EMC_DRIPPER &&
6119 game.lenses_time_left > 0)
6121 Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
6122 TEST_DrawLevelField(x, y);
6124 else if (element == EL_EMC_DRIPPER_ACTIVE &&
6125 game.lenses_time_left == 0)
6127 Feld[x][y] = EL_EMC_DRIPPER;
6128 TEST_DrawLevelField(x, y);
6130 else if (element == EL_INVISIBLE_STEELWALL ||
6131 element == EL_INVISIBLE_WALL ||
6132 element == EL_INVISIBLE_SAND)
6134 if (game.lenses_time_left > 0)
6135 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
6137 TEST_DrawLevelField(x, y);
6139 /* uncrumble neighbour fields, if needed */
6140 if (element == EL_INVISIBLE_SAND)
6141 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6143 else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6144 element == EL_INVISIBLE_WALL_ACTIVE ||
6145 element == EL_INVISIBLE_SAND_ACTIVE)
6147 if (game.lenses_time_left == 0)
6148 Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
6150 TEST_DrawLevelField(x, y);
6152 /* re-crumble neighbour fields, if needed */
6153 if (element == EL_INVISIBLE_SAND)
6154 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6159 static void RedrawAllInvisibleElementsForMagnifier()
6163 SCAN_PLAYFIELD(x, y)
6165 int element = Feld[x][y];
6167 if (element == EL_EMC_FAKE_GRASS &&
6168 game.magnify_time_left > 0)
6170 Feld[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
6171 TEST_DrawLevelField(x, y);
6173 else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
6174 game.magnify_time_left == 0)
6176 Feld[x][y] = EL_EMC_FAKE_GRASS;
6177 TEST_DrawLevelField(x, y);
6179 else if (IS_GATE_GRAY(element) &&
6180 game.magnify_time_left > 0)
6182 Feld[x][y] = (IS_RND_GATE_GRAY(element) ?
6183 element - EL_GATE_1_GRAY + EL_GATE_1_GRAY_ACTIVE :
6184 IS_EM_GATE_GRAY(element) ?
6185 element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
6186 IS_EMC_GATE_GRAY(element) ?
6187 element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
6188 IS_DC_GATE_GRAY(element) ?
6189 EL_DC_GATE_WHITE_GRAY_ACTIVE :
6191 TEST_DrawLevelField(x, y);
6193 else if (IS_GATE_GRAY_ACTIVE(element) &&
6194 game.magnify_time_left == 0)
6196 Feld[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ?
6197 element - EL_GATE_1_GRAY_ACTIVE + EL_GATE_1_GRAY :
6198 IS_EM_GATE_GRAY_ACTIVE(element) ?
6199 element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
6200 IS_EMC_GATE_GRAY_ACTIVE(element) ?
6201 element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
6202 IS_DC_GATE_GRAY_ACTIVE(element) ?
6203 EL_DC_GATE_WHITE_GRAY :
6205 TEST_DrawLevelField(x, y);
6210 static void ToggleLightSwitch(int x, int y)
6212 int element = Feld[x][y];
6214 game.light_time_left =
6215 (element == EL_LIGHT_SWITCH ?
6216 level.time_light * FRAMES_PER_SECOND : 0);
6218 RedrawAllLightSwitchesAndInvisibleElements();
6221 static void ActivateTimegateSwitch(int x, int y)
6225 game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
6227 SCAN_PLAYFIELD(xx, yy)
6229 int element = Feld[xx][yy];
6231 if (element == EL_TIMEGATE_CLOSED ||
6232 element == EL_TIMEGATE_CLOSING)
6234 Feld[xx][yy] = EL_TIMEGATE_OPENING;
6235 PlayLevelSound(xx, yy, SND_CLASS_TIMEGATE_OPENING);
6239 else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
6241 Feld[xx][yy] = EL_TIMEGATE_SWITCH;
6242 TEST_DrawLevelField(xx, yy);
6248 Feld[x][y] = (Feld[x][y] == EL_TIMEGATE_SWITCH ? EL_TIMEGATE_SWITCH_ACTIVE :
6249 EL_DC_TIMEGATE_SWITCH_ACTIVE);
6252 void Impact(int x, int y)
6254 boolean last_line = (y == lev_fieldy - 1);
6255 boolean object_hit = FALSE;
6256 boolean impact = (last_line || object_hit);
6257 int element = Feld[x][y];
6258 int smashed = EL_STEELWALL;
6260 if (!last_line) /* check if element below was hit */
6262 if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
6265 object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
6266 MovDir[x][y + 1] != MV_DOWN ||
6267 MovPos[x][y + 1] <= TILEY / 2));
6269 /* do not smash moving elements that left the smashed field in time */
6270 if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
6271 ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
6274 #if USE_QUICKSAND_IMPACT_BUGFIX
6275 if (Feld[x][y + 1] == EL_QUICKSAND_EMPTYING && object_hit == FALSE)
6277 RemoveMovingField(x, y + 1);
6278 Feld[x][y + 1] = EL_QUICKSAND_EMPTY;
6279 Feld[x][y + 2] = EL_ROCK;
6280 TEST_DrawLevelField(x, y + 2);
6285 if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTYING && object_hit == FALSE)
6287 RemoveMovingField(x, y + 1);
6288 Feld[x][y + 1] = EL_QUICKSAND_FAST_EMPTY;
6289 Feld[x][y + 2] = EL_ROCK;
6290 TEST_DrawLevelField(x, y + 2);
6297 smashed = MovingOrBlocked2Element(x, y + 1);
6299 impact = (last_line || object_hit);
6302 if (!last_line && smashed == EL_ACID) /* element falls into acid */
6304 SplashAcid(x, y + 1);
6308 /* !!! not sufficient for all cases -- see EL_PEARL below !!! */
6309 /* only reset graphic animation if graphic really changes after impact */
6311 el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
6313 ResetGfxAnimation(x, y);
6314 TEST_DrawLevelField(x, y);
6317 if (impact && CAN_EXPLODE_IMPACT(element))
6322 else if (impact && element == EL_PEARL &&
6323 smashed != EL_DC_MAGIC_WALL && smashed != EL_DC_MAGIC_WALL_ACTIVE)
6325 ResetGfxAnimation(x, y);
6327 Feld[x][y] = EL_PEARL_BREAKING;
6328 PlayLevelSound(x, y, SND_PEARL_BREAKING);
6331 else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
6333 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6338 if (impact && element == EL_AMOEBA_DROP)
6340 if (object_hit && IS_PLAYER(x, y + 1))
6341 KillPlayerUnlessEnemyProtected(x, y + 1);
6342 else if (object_hit && smashed == EL_PENGUIN)
6346 Feld[x][y] = EL_AMOEBA_GROWING;
6347 Store[x][y] = EL_AMOEBA_WET;
6349 ResetRandomAnimationValue(x, y);
6354 if (object_hit) /* check which object was hit */
6356 if ((CAN_PASS_MAGIC_WALL(element) &&
6357 (smashed == EL_MAGIC_WALL ||
6358 smashed == EL_BD_MAGIC_WALL)) ||
6359 (CAN_PASS_DC_MAGIC_WALL(element) &&
6360 smashed == EL_DC_MAGIC_WALL))
6363 int activated_magic_wall =
6364 (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
6365 smashed == EL_BD_MAGIC_WALL ? EL_BD_MAGIC_WALL_ACTIVE :
6366 EL_DC_MAGIC_WALL_ACTIVE);
6368 /* activate magic wall / mill */
6369 SCAN_PLAYFIELD(xx, yy)
6371 if (Feld[xx][yy] == smashed)
6372 Feld[xx][yy] = activated_magic_wall;
6375 game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
6376 game.magic_wall_active = TRUE;
6378 PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
6379 SND_MAGIC_WALL_ACTIVATING :
6380 smashed == EL_BD_MAGIC_WALL ?
6381 SND_BD_MAGIC_WALL_ACTIVATING :
6382 SND_DC_MAGIC_WALL_ACTIVATING));
6385 if (IS_PLAYER(x, y + 1))
6387 if (CAN_SMASH_PLAYER(element))
6389 KillPlayerUnlessEnemyProtected(x, y + 1);
6393 else if (smashed == EL_PENGUIN)
6395 if (CAN_SMASH_PLAYER(element))
6401 else if (element == EL_BD_DIAMOND)
6403 if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
6409 else if (((element == EL_SP_INFOTRON ||
6410 element == EL_SP_ZONK) &&
6411 (smashed == EL_SP_SNIKSNAK ||
6412 smashed == EL_SP_ELECTRON ||
6413 smashed == EL_SP_DISK_ORANGE)) ||
6414 (element == EL_SP_INFOTRON &&
6415 smashed == EL_SP_DISK_YELLOW))
6420 else if (CAN_SMASH_EVERYTHING(element))
6422 if (IS_CLASSIC_ENEMY(smashed) ||
6423 CAN_EXPLODE_SMASHED(smashed))
6428 else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
6430 if (smashed == EL_LAMP ||
6431 smashed == EL_LAMP_ACTIVE)
6436 else if (smashed == EL_NUT)
6438 Feld[x][y + 1] = EL_NUT_BREAKING;
6439 PlayLevelSound(x, y, SND_NUT_BREAKING);
6440 RaiseScoreElement(EL_NUT);
6443 else if (smashed == EL_PEARL)
6445 ResetGfxAnimation(x, y);
6447 Feld[x][y + 1] = EL_PEARL_BREAKING;
6448 PlayLevelSound(x, y, SND_PEARL_BREAKING);
6451 else if (smashed == EL_DIAMOND)
6453 Feld[x][y + 1] = EL_DIAMOND_BREAKING;
6454 PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
6457 else if (IS_BELT_SWITCH(smashed))
6459 ToggleBeltSwitch(x, y + 1);
6461 else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
6462 smashed == EL_SWITCHGATE_SWITCH_DOWN ||
6463 smashed == EL_DC_SWITCHGATE_SWITCH_UP ||
6464 smashed == EL_DC_SWITCHGATE_SWITCH_DOWN)
6466 ToggleSwitchgateSwitch(x, y + 1);
6468 else if (smashed == EL_LIGHT_SWITCH ||
6469 smashed == EL_LIGHT_SWITCH_ACTIVE)
6471 ToggleLightSwitch(x, y + 1);
6475 CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6477 CheckElementChangeBySide(x, y + 1, smashed, element,
6478 CE_SWITCHED, CH_SIDE_TOP);
6479 CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
6485 CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6490 /* play sound of magic wall / mill */
6492 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
6493 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ||
6494 Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE))
6496 if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
6497 PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
6498 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
6499 PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
6500 else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
6501 PlayLevelSound(x, y, SND_DC_MAGIC_WALL_FILLING);
6506 /* play sound of object that hits the ground */
6507 if (last_line || object_hit)
6508 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6511 inline static void TurnRoundExt(int x, int y)
6523 { 0, 0 }, { 0, 0 }, { 0, 0 },
6528 int left, right, back;
6532 { MV_DOWN, MV_UP, MV_RIGHT },
6533 { MV_UP, MV_DOWN, MV_LEFT },
6535 { MV_LEFT, MV_RIGHT, MV_DOWN },
6539 { MV_RIGHT, MV_LEFT, MV_UP }
6542 int element = Feld[x][y];
6543 int move_pattern = element_info[element].move_pattern;
6545 int old_move_dir = MovDir[x][y];
6546 int left_dir = turn[old_move_dir].left;
6547 int right_dir = turn[old_move_dir].right;
6548 int back_dir = turn[old_move_dir].back;
6550 int left_dx = move_xy[left_dir].dx, left_dy = move_xy[left_dir].dy;
6551 int right_dx = move_xy[right_dir].dx, right_dy = move_xy[right_dir].dy;
6552 int move_dx = move_xy[old_move_dir].dx, move_dy = move_xy[old_move_dir].dy;
6553 int back_dx = move_xy[back_dir].dx, back_dy = move_xy[back_dir].dy;
6555 int left_x = x + left_dx, left_y = y + left_dy;
6556 int right_x = x + right_dx, right_y = y + right_dy;
6557 int move_x = x + move_dx, move_y = y + move_dy;
6561 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
6563 TestIfBadThingTouchesOtherBadThing(x, y);
6565 if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
6566 MovDir[x][y] = right_dir;
6567 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6568 MovDir[x][y] = left_dir;
6570 if (element == EL_BUG && MovDir[x][y] != old_move_dir)
6572 else if (element == EL_BD_BUTTERFLY) /* && MovDir[x][y] == left_dir) */
6575 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
6577 TestIfBadThingTouchesOtherBadThing(x, y);
6579 if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
6580 MovDir[x][y] = left_dir;
6581 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6582 MovDir[x][y] = right_dir;
6584 if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
6586 else if (element == EL_BD_FIREFLY) /* && MovDir[x][y] == right_dir) */
6589 else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
6591 TestIfBadThingTouchesOtherBadThing(x, y);
6593 if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
6594 MovDir[x][y] = left_dir;
6595 else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
6596 MovDir[x][y] = right_dir;
6598 if (MovDir[x][y] != old_move_dir)
6601 else if (element == EL_YAMYAM)
6603 boolean can_turn_left = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
6604 boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
6606 if (can_turn_left && can_turn_right)
6607 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6608 else if (can_turn_left)
6609 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6610 else if (can_turn_right)
6611 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6613 MovDir[x][y] = back_dir;
6615 MovDelay[x][y] = 16 + 16 * RND(3);
6617 else if (element == EL_DARK_YAMYAM)
6619 boolean can_turn_left = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6621 boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6624 if (can_turn_left && can_turn_right)
6625 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6626 else if (can_turn_left)
6627 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6628 else if (can_turn_right)
6629 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6631 MovDir[x][y] = back_dir;
6633 MovDelay[x][y] = 16 + 16 * RND(3);
6635 else if (element == EL_PACMAN)
6637 boolean can_turn_left = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
6638 boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
6640 if (can_turn_left && can_turn_right)
6641 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6642 else if (can_turn_left)
6643 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6644 else if (can_turn_right)
6645 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6647 MovDir[x][y] = back_dir;
6649 MovDelay[x][y] = 6 + RND(40);
6651 else if (element == EL_PIG)
6653 boolean can_turn_left = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
6654 boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
6655 boolean can_move_on = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
6656 boolean should_turn_left, should_turn_right, should_move_on;
6658 int rnd = RND(rnd_value);
6660 should_turn_left = (can_turn_left &&
6662 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
6663 y + back_dy + left_dy)));
6664 should_turn_right = (can_turn_right &&
6666 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
6667 y + back_dy + right_dy)));
6668 should_move_on = (can_move_on &&
6671 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
6672 y + move_dy + left_dy) ||
6673 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
6674 y + move_dy + right_dy)));
6676 if (should_turn_left || should_turn_right || should_move_on)
6678 if (should_turn_left && should_turn_right && should_move_on)
6679 MovDir[x][y] = (rnd < rnd_value / 3 ? left_dir :
6680 rnd < 2 * rnd_value / 3 ? right_dir :
6682 else if (should_turn_left && should_turn_right)
6683 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6684 else if (should_turn_left && should_move_on)
6685 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
6686 else if (should_turn_right && should_move_on)
6687 MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
6688 else if (should_turn_left)
6689 MovDir[x][y] = left_dir;
6690 else if (should_turn_right)
6691 MovDir[x][y] = right_dir;
6692 else if (should_move_on)
6693 MovDir[x][y] = old_move_dir;
6695 else if (can_move_on && rnd > rnd_value / 8)
6696 MovDir[x][y] = old_move_dir;
6697 else if (can_turn_left && can_turn_right)
6698 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6699 else if (can_turn_left && rnd > rnd_value / 8)
6700 MovDir[x][y] = left_dir;
6701 else if (can_turn_right && rnd > rnd_value/8)
6702 MovDir[x][y] = right_dir;
6704 MovDir[x][y] = back_dir;
6706 xx = x + move_xy[MovDir[x][y]].dx;
6707 yy = y + move_xy[MovDir[x][y]].dy;
6709 if (!IN_LEV_FIELD(xx, yy) ||
6710 (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy])))
6711 MovDir[x][y] = old_move_dir;
6715 else if (element == EL_DRAGON)
6717 boolean can_turn_left = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
6718 boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
6719 boolean can_move_on = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
6721 int rnd = RND(rnd_value);
6723 if (can_move_on && rnd > rnd_value / 8)
6724 MovDir[x][y] = old_move_dir;
6725 else if (can_turn_left && can_turn_right)
6726 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6727 else if (can_turn_left && rnd > rnd_value / 8)
6728 MovDir[x][y] = left_dir;
6729 else if (can_turn_right && rnd > rnd_value / 8)
6730 MovDir[x][y] = right_dir;
6732 MovDir[x][y] = back_dir;
6734 xx = x + move_xy[MovDir[x][y]].dx;
6735 yy = y + move_xy[MovDir[x][y]].dy;
6737 if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
6738 MovDir[x][y] = old_move_dir;
6742 else if (element == EL_MOLE)
6744 boolean can_move_on =
6745 (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
6746 IS_AMOEBOID(Feld[move_x][move_y]) ||
6747 Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
6750 boolean can_turn_left =
6751 (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
6752 IS_AMOEBOID(Feld[left_x][left_y])));
6754 boolean can_turn_right =
6755 (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
6756 IS_AMOEBOID(Feld[right_x][right_y])));
6758 if (can_turn_left && can_turn_right)
6759 MovDir[x][y] = (RND(2) ? left_dir : right_dir);
6760 else if (can_turn_left)
6761 MovDir[x][y] = left_dir;
6763 MovDir[x][y] = right_dir;
6766 if (MovDir[x][y] != old_move_dir)
6769 else if (element == EL_BALLOON)
6771 MovDir[x][y] = game.wind_direction;
6774 else if (element == EL_SPRING)
6776 if (MovDir[x][y] & MV_HORIZONTAL)
6778 if (SPRING_CAN_BUMP_FROM_FIELD(move_x, move_y) &&
6779 !SPRING_CAN_ENTER_FIELD(element, x, y + 1))
6781 Feld[move_x][move_y] = EL_EMC_SPRING_BUMPER_ACTIVE;
6782 ResetGfxAnimation(move_x, move_y);
6783 TEST_DrawLevelField(move_x, move_y);
6785 MovDir[x][y] = back_dir;
6787 else if (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
6788 SPRING_CAN_ENTER_FIELD(element, x, y + 1))
6789 MovDir[x][y] = MV_NONE;
6794 else if (element == EL_ROBOT ||
6795 element == EL_SATELLITE ||
6796 element == EL_PENGUIN ||
6797 element == EL_EMC_ANDROID)
6799 int attr_x = -1, attr_y = -1;
6810 for (i = 0; i < MAX_PLAYERS; i++)
6812 struct PlayerInfo *player = &stored_player[i];
6813 int jx = player->jx, jy = player->jy;
6815 if (!player->active)
6819 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
6827 if (element == EL_ROBOT && ZX >= 0 && ZY >= 0 &&
6828 (Feld[ZX][ZY] == EL_ROBOT_WHEEL_ACTIVE ||
6829 game.engine_version < VERSION_IDENT(3,1,0,0)))
6835 if (element == EL_PENGUIN)
6838 static int xy[4][2] =
6846 for (i = 0; i < NUM_DIRECTIONS; i++)
6848 int ex = x + xy[i][0];
6849 int ey = y + xy[i][1];
6851 if (IN_LEV_FIELD(ex, ey) && (Feld[ex][ey] == EL_EXIT_OPEN ||
6852 Feld[ex][ey] == EL_EM_EXIT_OPEN ||
6853 Feld[ex][ey] == EL_STEEL_EXIT_OPEN ||
6854 Feld[ex][ey] == EL_EM_STEEL_EXIT_OPEN))
6863 MovDir[x][y] = MV_NONE;
6865 MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
6866 else if (attr_x > x)
6867 MovDir[x][y] |= (AllPlayersGone ? MV_LEFT : MV_RIGHT);
6869 MovDir[x][y] |= (AllPlayersGone ? MV_DOWN : MV_UP);
6870 else if (attr_y > y)
6871 MovDir[x][y] |= (AllPlayersGone ? MV_UP : MV_DOWN);
6873 if (element == EL_ROBOT)
6877 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6878 MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
6879 Moving2Blocked(x, y, &newx, &newy);
6881 if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
6882 MovDelay[x][y] = 8 + 8 * !RND(3);
6884 MovDelay[x][y] = 16;
6886 else if (element == EL_PENGUIN)
6892 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6894 boolean first_horiz = RND(2);
6895 int new_move_dir = MovDir[x][y];
6898 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6899 Moving2Blocked(x, y, &newx, &newy);
6901 if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
6905 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6906 Moving2Blocked(x, y, &newx, &newy);
6908 if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
6911 MovDir[x][y] = old_move_dir;
6915 else if (element == EL_SATELLITE)
6921 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6923 boolean first_horiz = RND(2);
6924 int new_move_dir = MovDir[x][y];
6927 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6928 Moving2Blocked(x, y, &newx, &newy);
6930 if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
6934 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6935 Moving2Blocked(x, y, &newx, &newy);
6937 if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
6940 MovDir[x][y] = old_move_dir;
6944 else if (element == EL_EMC_ANDROID)
6946 static int check_pos[16] =
6948 -1, /* 0 => (invalid) */
6949 7, /* 1 => MV_LEFT */
6950 3, /* 2 => MV_RIGHT */
6951 -1, /* 3 => (invalid) */
6953 0, /* 5 => MV_LEFT | MV_UP */
6954 2, /* 6 => MV_RIGHT | MV_UP */
6955 -1, /* 7 => (invalid) */
6956 5, /* 8 => MV_DOWN */
6957 6, /* 9 => MV_LEFT | MV_DOWN */
6958 4, /* 10 => MV_RIGHT | MV_DOWN */
6959 -1, /* 11 => (invalid) */
6960 -1, /* 12 => (invalid) */
6961 -1, /* 13 => (invalid) */
6962 -1, /* 14 => (invalid) */
6963 -1, /* 15 => (invalid) */
6971 { -1, -1, MV_LEFT | MV_UP },
6973 { +1, -1, MV_RIGHT | MV_UP },
6974 { +1, 0, MV_RIGHT },
6975 { +1, +1, MV_RIGHT | MV_DOWN },
6977 { -1, +1, MV_LEFT | MV_DOWN },
6980 int start_pos, check_order;
6981 boolean can_clone = FALSE;
6984 /* check if there is any free field around current position */
6985 for (i = 0; i < 8; i++)
6987 int newx = x + check_xy[i].dx;
6988 int newy = y + check_xy[i].dy;
6990 if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
6998 if (can_clone) /* randomly find an element to clone */
7002 start_pos = check_pos[RND(8)];
7003 check_order = (RND(2) ? -1 : +1);
7005 for (i = 0; i < 8; i++)
7007 int pos_raw = start_pos + i * check_order;
7008 int pos = (pos_raw + 8) % 8;
7009 int newx = x + check_xy[pos].dx;
7010 int newy = y + check_xy[pos].dy;
7012 if (ANDROID_CAN_CLONE_FIELD(newx, newy))
7014 element_info[element].move_leave_type = LEAVE_TYPE_LIMITED;
7015 element_info[element].move_leave_element = EL_TRIGGER_ELEMENT;
7017 Store[x][y] = Feld[newx][newy];
7026 if (can_clone) /* randomly find a direction to move */
7030 start_pos = check_pos[RND(8)];
7031 check_order = (RND(2) ? -1 : +1);
7033 for (i = 0; i < 8; i++)
7035 int pos_raw = start_pos + i * check_order;
7036 int pos = (pos_raw + 8) % 8;
7037 int newx = x + check_xy[pos].dx;
7038 int newy = y + check_xy[pos].dy;
7039 int new_move_dir = check_xy[pos].dir;
7041 if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7043 MovDir[x][y] = new_move_dir;
7044 MovDelay[x][y] = level.android_clone_time * 8 + 1;
7053 if (can_clone) /* cloning and moving successful */
7056 /* cannot clone -- try to move towards player */
7058 start_pos = check_pos[MovDir[x][y] & 0x0f];
7059 check_order = (RND(2) ? -1 : +1);
7061 for (i = 0; i < 3; i++)
7063 /* first check start_pos, then previous/next or (next/previous) pos */
7064 int pos_raw = start_pos + (i < 2 ? i : -1) * check_order;
7065 int pos = (pos_raw + 8) % 8;
7066 int newx = x + check_xy[pos].dx;
7067 int newy = y + check_xy[pos].dy;
7068 int new_move_dir = check_xy[pos].dir;
7070 if (IS_PLAYER(newx, newy))
7073 if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7075 MovDir[x][y] = new_move_dir;
7076 MovDelay[x][y] = level.android_move_time * 8 + 1;
7083 else if (move_pattern == MV_TURNING_LEFT ||
7084 move_pattern == MV_TURNING_RIGHT ||
7085 move_pattern == MV_TURNING_LEFT_RIGHT ||
7086 move_pattern == MV_TURNING_RIGHT_LEFT ||
7087 move_pattern == MV_TURNING_RANDOM ||
7088 move_pattern == MV_ALL_DIRECTIONS)
7090 boolean can_turn_left =
7091 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
7092 boolean can_turn_right =
7093 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
7095 if (element_info[element].move_stepsize == 0) /* "not moving" */
7098 if (move_pattern == MV_TURNING_LEFT)
7099 MovDir[x][y] = left_dir;
7100 else if (move_pattern == MV_TURNING_RIGHT)
7101 MovDir[x][y] = right_dir;
7102 else if (move_pattern == MV_TURNING_LEFT_RIGHT)
7103 MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
7104 else if (move_pattern == MV_TURNING_RIGHT_LEFT)
7105 MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
7106 else if (move_pattern == MV_TURNING_RANDOM)
7107 MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
7108 can_turn_right && !can_turn_left ? right_dir :
7109 RND(2) ? left_dir : right_dir);
7110 else if (can_turn_left && can_turn_right)
7111 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7112 else if (can_turn_left)
7113 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7114 else if (can_turn_right)
7115 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7117 MovDir[x][y] = back_dir;
7119 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7121 else if (move_pattern == MV_HORIZONTAL ||
7122 move_pattern == MV_VERTICAL)
7124 if (move_pattern & old_move_dir)
7125 MovDir[x][y] = back_dir;
7126 else if (move_pattern == MV_HORIZONTAL)
7127 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
7128 else if (move_pattern == MV_VERTICAL)
7129 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
7131 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7133 else if (move_pattern & MV_ANY_DIRECTION)
7135 MovDir[x][y] = move_pattern;
7136 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7138 else if (move_pattern & MV_WIND_DIRECTION)
7140 MovDir[x][y] = game.wind_direction;
7141 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7143 else if (move_pattern == MV_ALONG_LEFT_SIDE)
7145 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
7146 MovDir[x][y] = left_dir;
7147 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7148 MovDir[x][y] = right_dir;
7150 if (MovDir[x][y] != old_move_dir)
7151 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7153 else if (move_pattern == MV_ALONG_RIGHT_SIDE)
7155 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
7156 MovDir[x][y] = right_dir;
7157 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7158 MovDir[x][y] = left_dir;
7160 if (MovDir[x][y] != old_move_dir)
7161 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7163 else if (move_pattern == MV_TOWARDS_PLAYER ||
7164 move_pattern == MV_AWAY_FROM_PLAYER)
7166 int attr_x = -1, attr_y = -1;
7168 boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
7179 for (i = 0; i < MAX_PLAYERS; i++)
7181 struct PlayerInfo *player = &stored_player[i];
7182 int jx = player->jx, jy = player->jy;
7184 if (!player->active)
7188 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7196 MovDir[x][y] = MV_NONE;
7198 MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
7199 else if (attr_x > x)
7200 MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
7202 MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
7203 else if (attr_y > y)
7204 MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
7206 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7208 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7210 boolean first_horiz = RND(2);
7211 int new_move_dir = MovDir[x][y];
7213 if (element_info[element].move_stepsize == 0) /* "not moving" */
7215 first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
7216 MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7222 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7223 Moving2Blocked(x, y, &newx, &newy);
7225 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7229 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7230 Moving2Blocked(x, y, &newx, &newy);
7232 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7235 MovDir[x][y] = old_move_dir;
7238 else if (move_pattern == MV_WHEN_PUSHED ||
7239 move_pattern == MV_WHEN_DROPPED)
7241 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7242 MovDir[x][y] = MV_NONE;
7246 else if (move_pattern & MV_MAZE_RUNNER_STYLE)
7248 static int test_xy[7][2] =
7258 static int test_dir[7] =
7268 boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
7269 int move_preference = -1000000; /* start with very low preference */
7270 int new_move_dir = MV_NONE;
7271 int start_test = RND(4);
7274 for (i = 0; i < NUM_DIRECTIONS; i++)
7276 int move_dir = test_dir[start_test + i];
7277 int move_dir_preference;
7279 xx = x + test_xy[start_test + i][0];
7280 yy = y + test_xy[start_test + i][1];
7282 if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
7283 (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
7285 new_move_dir = move_dir;
7290 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
7293 move_dir_preference = -1 * RunnerVisit[xx][yy];
7294 if (hunter_mode && PlayerVisit[xx][yy] > 0)
7295 move_dir_preference = PlayerVisit[xx][yy];
7297 if (move_dir_preference > move_preference)
7299 /* prefer field that has not been visited for the longest time */
7300 move_preference = move_dir_preference;
7301 new_move_dir = move_dir;
7303 else if (move_dir_preference == move_preference &&
7304 move_dir == old_move_dir)
7306 /* prefer last direction when all directions are preferred equally */
7307 move_preference = move_dir_preference;
7308 new_move_dir = move_dir;
7312 MovDir[x][y] = new_move_dir;
7313 if (old_move_dir != new_move_dir)
7314 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7318 static void TurnRound(int x, int y)
7320 int direction = MovDir[x][y];
7324 GfxDir[x][y] = MovDir[x][y];
7326 if (direction != MovDir[x][y])
7330 GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction);
7332 ResetGfxFrame(x, y);
7335 static boolean JustBeingPushed(int x, int y)
7339 for (i = 0; i < MAX_PLAYERS; i++)
7341 struct PlayerInfo *player = &stored_player[i];
7343 if (player->active && player->is_pushing && player->MovPos)
7345 int next_jx = player->jx + (player->jx - player->last_jx);
7346 int next_jy = player->jy + (player->jy - player->last_jy);
7348 if (x == next_jx && y == next_jy)
7356 void StartMoving(int x, int y)
7358 boolean started_moving = FALSE; /* some elements can fall _and_ move */
7359 int element = Feld[x][y];
7364 if (MovDelay[x][y] == 0)
7365 GfxAction[x][y] = ACTION_DEFAULT;
7367 if (CAN_FALL(element) && y < lev_fieldy - 1)
7369 if ((x > 0 && IS_PLAYER(x - 1, y)) ||
7370 (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
7371 if (JustBeingPushed(x, y))
7374 if (element == EL_QUICKSAND_FULL)
7376 if (IS_FREE(x, y + 1))
7378 InitMovingField(x, y, MV_DOWN);
7379 started_moving = TRUE;
7381 Feld[x][y] = EL_QUICKSAND_EMPTYING;
7382 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7383 if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7384 Store[x][y] = EL_ROCK;
7386 Store[x][y] = EL_ROCK;
7389 PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7391 else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7393 if (!MovDelay[x][y])
7395 MovDelay[x][y] = TILEY + 1;
7397 ResetGfxAnimation(x, y);
7398 ResetGfxAnimation(x, y + 1);
7403 DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7404 DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7411 Feld[x][y] = EL_QUICKSAND_EMPTY;
7412 Feld[x][y + 1] = EL_QUICKSAND_FULL;
7413 Store[x][y + 1] = Store[x][y];
7416 PlayLevelSoundAction(x, y, ACTION_FILLING);
7418 else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7420 if (!MovDelay[x][y])
7422 MovDelay[x][y] = TILEY + 1;
7424 ResetGfxAnimation(x, y);
7425 ResetGfxAnimation(x, y + 1);
7430 DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7431 DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7438 Feld[x][y] = EL_QUICKSAND_EMPTY;
7439 Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7440 Store[x][y + 1] = Store[x][y];
7443 PlayLevelSoundAction(x, y, ACTION_FILLING);
7446 else if (element == EL_QUICKSAND_FAST_FULL)
7448 if (IS_FREE(x, y + 1))
7450 InitMovingField(x, y, MV_DOWN);
7451 started_moving = TRUE;
7453 Feld[x][y] = EL_QUICKSAND_FAST_EMPTYING;
7454 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7455 if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7456 Store[x][y] = EL_ROCK;
7458 Store[x][y] = EL_ROCK;
7461 PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7463 else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7465 if (!MovDelay[x][y])
7467 MovDelay[x][y] = TILEY + 1;
7469 ResetGfxAnimation(x, y);
7470 ResetGfxAnimation(x, y + 1);
7475 DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7476 DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7483 Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
7484 Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7485 Store[x][y + 1] = Store[x][y];
7488 PlayLevelSoundAction(x, y, ACTION_FILLING);
7490 else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7492 if (!MovDelay[x][y])
7494 MovDelay[x][y] = TILEY + 1;
7496 ResetGfxAnimation(x, y);
7497 ResetGfxAnimation(x, y + 1);
7502 DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7503 DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7510 Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
7511 Feld[x][y + 1] = EL_QUICKSAND_FULL;
7512 Store[x][y + 1] = Store[x][y];
7515 PlayLevelSoundAction(x, y, ACTION_FILLING);
7518 else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7519 Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7521 InitMovingField(x, y, MV_DOWN);
7522 started_moving = TRUE;
7524 Feld[x][y] = EL_QUICKSAND_FILLING;
7525 Store[x][y] = element;
7527 PlayLevelSoundAction(x, y, ACTION_FILLING);
7529 else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7530 Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7532 InitMovingField(x, y, MV_DOWN);
7533 started_moving = TRUE;
7535 Feld[x][y] = EL_QUICKSAND_FAST_FILLING;
7536 Store[x][y] = element;
7538 PlayLevelSoundAction(x, y, ACTION_FILLING);
7540 else if (element == EL_MAGIC_WALL_FULL)
7542 if (IS_FREE(x, y + 1))
7544 InitMovingField(x, y, MV_DOWN);
7545 started_moving = TRUE;
7547 Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
7548 Store[x][y] = EL_CHANGED(Store[x][y]);
7550 else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
7552 if (!MovDelay[x][y])
7553 MovDelay[x][y] = TILEY / 4 + 1;
7562 Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
7563 Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
7564 Store[x][y + 1] = EL_CHANGED(Store[x][y]);
7568 else if (element == EL_BD_MAGIC_WALL_FULL)
7570 if (IS_FREE(x, y + 1))
7572 InitMovingField(x, y, MV_DOWN);
7573 started_moving = TRUE;
7575 Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
7576 Store[x][y] = EL_CHANGED_BD(Store[x][y]);
7578 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
7580 if (!MovDelay[x][y])
7581 MovDelay[x][y] = TILEY / 4 + 1;
7590 Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
7591 Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
7592 Store[x][y + 1] = EL_CHANGED_BD(Store[x][y]);
7596 else if (element == EL_DC_MAGIC_WALL_FULL)
7598 if (IS_FREE(x, y + 1))
7600 InitMovingField(x, y, MV_DOWN);
7601 started_moving = TRUE;
7603 Feld[x][y] = EL_DC_MAGIC_WALL_EMPTYING;
7604 Store[x][y] = EL_CHANGED_DC(Store[x][y]);
7606 else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
7608 if (!MovDelay[x][y])
7609 MovDelay[x][y] = TILEY / 4 + 1;
7618 Feld[x][y] = EL_DC_MAGIC_WALL_ACTIVE;
7619 Feld[x][y + 1] = EL_DC_MAGIC_WALL_FULL;
7620 Store[x][y + 1] = EL_CHANGED_DC(Store[x][y]);
7624 else if ((CAN_PASS_MAGIC_WALL(element) &&
7625 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
7626 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)) ||
7627 (CAN_PASS_DC_MAGIC_WALL(element) &&
7628 (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)))
7631 InitMovingField(x, y, MV_DOWN);
7632 started_moving = TRUE;
7635 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
7636 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ? EL_BD_MAGIC_WALL_FILLING :
7637 EL_DC_MAGIC_WALL_FILLING);
7638 Store[x][y] = element;
7640 else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
7642 SplashAcid(x, y + 1);
7644 InitMovingField(x, y, MV_DOWN);
7645 started_moving = TRUE;
7647 Store[x][y] = EL_ACID;
7650 (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7651 CheckImpact[x][y] && !IS_FREE(x, y + 1)) ||
7652 (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
7653 CAN_FALL(element) && WasJustFalling[x][y] &&
7654 (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
7656 (game.engine_version < VERSION_IDENT(2,2,0,7) &&
7657 CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
7658 (Feld[x][y + 1] == EL_BLOCKED)))
7660 /* this is needed for a special case not covered by calling "Impact()"
7661 from "ContinueMoving()": if an element moves to a tile directly below
7662 another element which was just falling on that tile (which was empty
7663 in the previous frame), the falling element above would just stop
7664 instead of smashing the element below (in previous version, the above
7665 element was just checked for "moving" instead of "falling", resulting
7666 in incorrect smashes caused by horizontal movement of the above
7667 element; also, the case of the player being the element to smash was
7668 simply not covered here... :-/ ) */
7670 CheckCollision[x][y] = 0;
7671 CheckImpact[x][y] = 0;
7675 else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
7677 if (MovDir[x][y] == MV_NONE)
7679 InitMovingField(x, y, MV_DOWN);
7680 started_moving = TRUE;
7683 else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
7685 if (WasJustFalling[x][y]) /* prevent animation from being restarted */
7686 MovDir[x][y] = MV_DOWN;
7688 InitMovingField(x, y, MV_DOWN);
7689 started_moving = TRUE;
7691 else if (element == EL_AMOEBA_DROP)
7693 Feld[x][y] = EL_AMOEBA_GROWING;
7694 Store[x][y] = EL_AMOEBA_WET;
7696 else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
7697 (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
7698 !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
7699 element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
7701 boolean can_fall_left = (x > 0 && IS_FREE(x - 1, y) &&
7702 (IS_FREE(x - 1, y + 1) ||
7703 Feld[x - 1][y + 1] == EL_ACID));
7704 boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
7705 (IS_FREE(x + 1, y + 1) ||
7706 Feld[x + 1][y + 1] == EL_ACID));
7707 boolean can_fall_any = (can_fall_left || can_fall_right);
7708 boolean can_fall_both = (can_fall_left && can_fall_right);
7709 int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
7711 if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
7713 if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
7714 can_fall_right = FALSE;
7715 else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
7716 can_fall_left = FALSE;
7717 else if (slippery_type == SLIPPERY_ONLY_LEFT)
7718 can_fall_right = FALSE;
7719 else if (slippery_type == SLIPPERY_ONLY_RIGHT)
7720 can_fall_left = FALSE;
7722 can_fall_any = (can_fall_left || can_fall_right);
7723 can_fall_both = FALSE;
7728 if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
7729 can_fall_right = FALSE; /* slip down on left side */
7731 can_fall_left = !(can_fall_right = RND(2));
7733 can_fall_both = FALSE;
7738 /* if not determined otherwise, prefer left side for slipping down */
7739 InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
7740 started_moving = TRUE;
7743 else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
7745 boolean left_is_free = (x > 0 && IS_FREE(x - 1, y));
7746 boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
7747 int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
7748 int belt_dir = game.belt_dir[belt_nr];
7750 if ((belt_dir == MV_LEFT && left_is_free) ||
7751 (belt_dir == MV_RIGHT && right_is_free))
7753 int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
7755 InitMovingField(x, y, belt_dir);
7756 started_moving = TRUE;
7758 Pushed[x][y] = TRUE;
7759 Pushed[nextx][y] = TRUE;
7761 GfxAction[x][y] = ACTION_DEFAULT;
7765 MovDir[x][y] = 0; /* if element was moving, stop it */
7770 /* not "else if" because of elements that can fall and move (EL_SPRING) */
7771 if (CAN_MOVE(element) && !started_moving)
7773 int move_pattern = element_info[element].move_pattern;
7776 Moving2Blocked(x, y, &newx, &newy);
7778 if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
7781 if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7782 CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7784 WasJustMoving[x][y] = 0;
7785 CheckCollision[x][y] = 0;
7787 TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
7789 if (Feld[x][y] != element) /* element has changed */
7793 if (!MovDelay[x][y]) /* start new movement phase */
7795 /* all objects that can change their move direction after each step
7796 (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall */
7798 if (element != EL_YAMYAM &&
7799 element != EL_DARK_YAMYAM &&
7800 element != EL_PACMAN &&
7801 !(move_pattern & MV_ANY_DIRECTION) &&
7802 move_pattern != MV_TURNING_LEFT &&
7803 move_pattern != MV_TURNING_RIGHT &&
7804 move_pattern != MV_TURNING_LEFT_RIGHT &&
7805 move_pattern != MV_TURNING_RIGHT_LEFT &&
7806 move_pattern != MV_TURNING_RANDOM)
7810 if (MovDelay[x][y] && (element == EL_BUG ||
7811 element == EL_SPACESHIP ||
7812 element == EL_SP_SNIKSNAK ||
7813 element == EL_SP_ELECTRON ||
7814 element == EL_MOLE))
7815 TEST_DrawLevelField(x, y);
7819 if (MovDelay[x][y]) /* wait some time before next movement */
7823 if (element == EL_ROBOT ||
7824 element == EL_YAMYAM ||
7825 element == EL_DARK_YAMYAM)
7827 DrawLevelElementAnimationIfNeeded(x, y, element);
7828 PlayLevelSoundAction(x, y, ACTION_WAITING);
7830 else if (element == EL_SP_ELECTRON)
7831 DrawLevelElementAnimationIfNeeded(x, y, element);
7832 else if (element == EL_DRAGON)
7835 int dir = MovDir[x][y];
7836 int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
7837 int dy = (dir == MV_UP ? -1 : dir == MV_DOWN ? +1 : 0);
7838 int graphic = (dir == MV_LEFT ? IMG_FLAMES_1_LEFT :
7839 dir == MV_RIGHT ? IMG_FLAMES_1_RIGHT :
7840 dir == MV_UP ? IMG_FLAMES_1_UP :
7841 dir == MV_DOWN ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
7842 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
7844 GfxAction[x][y] = ACTION_ATTACKING;
7846 if (IS_PLAYER(x, y))
7847 DrawPlayerField(x, y);
7849 TEST_DrawLevelField(x, y);
7851 PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
7853 for (i = 1; i <= 3; i++)
7855 int xx = x + i * dx;
7856 int yy = y + i * dy;
7857 int sx = SCREENX(xx);
7858 int sy = SCREENY(yy);
7859 int flame_graphic = graphic + (i - 1);
7861 if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
7866 int flamed = MovingOrBlocked2Element(xx, yy);
7868 if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
7871 RemoveMovingField(xx, yy);
7873 ChangeDelay[xx][yy] = 0;
7875 Feld[xx][yy] = EL_FLAMES;
7877 if (IN_SCR_FIELD(sx, sy))
7879 TEST_DrawLevelFieldCrumbled(xx, yy);
7880 DrawGraphic(sx, sy, flame_graphic, frame);
7885 if (Feld[xx][yy] == EL_FLAMES)
7886 Feld[xx][yy] = EL_EMPTY;
7887 TEST_DrawLevelField(xx, yy);
7892 if (MovDelay[x][y]) /* element still has to wait some time */
7894 PlayLevelSoundAction(x, y, ACTION_WAITING);
7900 /* now make next step */
7902 Moving2Blocked(x, y, &newx, &newy); /* get next screen position */
7904 if (DONT_COLLIDE_WITH(element) &&
7905 IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
7906 !PLAYER_ENEMY_PROTECTED(newx, newy))
7908 TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
7913 else if (CAN_MOVE_INTO_ACID(element) &&
7914 IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID &&
7915 !IS_MV_DIAGONAL(MovDir[x][y]) &&
7916 (MovDir[x][y] == MV_DOWN ||
7917 game.engine_version >= VERSION_IDENT(3,1,0,0)))
7919 SplashAcid(newx, newy);
7920 Store[x][y] = EL_ACID;
7922 else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
7924 if (Feld[newx][newy] == EL_EXIT_OPEN ||
7925 Feld[newx][newy] == EL_EM_EXIT_OPEN ||
7926 Feld[newx][newy] == EL_STEEL_EXIT_OPEN ||
7927 Feld[newx][newy] == EL_EM_STEEL_EXIT_OPEN)
7930 TEST_DrawLevelField(x, y);
7932 PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
7933 if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
7934 DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
7936 local_player->friends_still_needed--;
7937 if (!local_player->friends_still_needed &&
7938 !local_player->GameOver && AllPlayersGone)
7939 PlayerWins(local_player);
7943 else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
7945 if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MP_MOVING)
7946 TEST_DrawLevelField(newx, newy);
7948 GfxDir[x][y] = MovDir[x][y] = MV_NONE;
7950 else if (!IS_FREE(newx, newy))
7952 GfxAction[x][y] = ACTION_WAITING;
7954 if (IS_PLAYER(x, y))
7955 DrawPlayerField(x, y);
7957 TEST_DrawLevelField(x, y);
7962 else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
7964 if (IS_FOOD_PIG(Feld[newx][newy]))
7966 if (IS_MOVING(newx, newy))
7967 RemoveMovingField(newx, newy);
7970 Feld[newx][newy] = EL_EMPTY;
7971 TEST_DrawLevelField(newx, newy);
7974 PlayLevelSound(x, y, SND_PIG_DIGGING);
7976 else if (!IS_FREE(newx, newy))
7978 if (IS_PLAYER(x, y))
7979 DrawPlayerField(x, y);
7981 TEST_DrawLevelField(x, y);
7986 else if (element == EL_EMC_ANDROID && IN_LEV_FIELD(newx, newy))
7988 if (Store[x][y] != EL_EMPTY)
7990 boolean can_clone = FALSE;
7993 /* check if element to clone is still there */
7994 for (yy = y - 1; yy <= y + 1; yy++) for (xx = x - 1; xx <= x + 1; xx++)
7996 if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == Store[x][y])
8004 /* cannot clone or target field not free anymore -- do not clone */
8005 if (!can_clone || !ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8006 Store[x][y] = EL_EMPTY;
8009 if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8011 if (IS_MV_DIAGONAL(MovDir[x][y]))
8013 int diagonal_move_dir = MovDir[x][y];
8014 int stored = Store[x][y];
8015 int change_delay = 8;
8018 /* android is moving diagonally */
8020 CreateField(x, y, EL_DIAGONAL_SHRINKING);
8022 Store[x][y] = (stored == EL_ACID ? EL_EMPTY : stored);
8023 GfxElement[x][y] = EL_EMC_ANDROID;
8024 GfxAction[x][y] = ACTION_SHRINKING;
8025 GfxDir[x][y] = diagonal_move_dir;
8026 ChangeDelay[x][y] = change_delay;
8028 graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],
8031 DrawLevelGraphicAnimation(x, y, graphic);
8032 PlayLevelSoundAction(x, y, ACTION_SHRINKING);
8034 if (Feld[newx][newy] == EL_ACID)
8036 SplashAcid(newx, newy);
8041 CreateField(newx, newy, EL_DIAGONAL_GROWING);
8043 Store[newx][newy] = EL_EMC_ANDROID;
8044 GfxElement[newx][newy] = EL_EMC_ANDROID;
8045 GfxAction[newx][newy] = ACTION_GROWING;
8046 GfxDir[newx][newy] = diagonal_move_dir;
8047 ChangeDelay[newx][newy] = change_delay;
8049 graphic = el_act_dir2img(GfxElement[newx][newy],
8050 GfxAction[newx][newy], GfxDir[newx][newy]);
8052 DrawLevelGraphicAnimation(newx, newy, graphic);
8053 PlayLevelSoundAction(newx, newy, ACTION_GROWING);
8059 Feld[newx][newy] = EL_EMPTY;
8060 TEST_DrawLevelField(newx, newy);
8062 PlayLevelSoundAction(x, y, ACTION_DIGGING);
8065 else if (!IS_FREE(newx, newy))
8070 else if (IS_CUSTOM_ELEMENT(element) &&
8071 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
8073 if (!DigFieldByCE(newx, newy, element))
8076 if (move_pattern & MV_MAZE_RUNNER_STYLE)
8078 RunnerVisit[x][y] = FrameCounter;
8079 PlayerVisit[x][y] /= 8; /* expire player visit path */
8082 else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
8084 if (!IS_FREE(newx, newy))
8086 if (IS_PLAYER(x, y))
8087 DrawPlayerField(x, y);
8089 TEST_DrawLevelField(x, y);
8095 boolean wanna_flame = !RND(10);
8096 int dx = newx - x, dy = newy - y;
8097 int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
8098 int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
8099 int element1 = (IN_LEV_FIELD(newx1, newy1) ?
8100 MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
8101 int element2 = (IN_LEV_FIELD(newx2, newy2) ?
8102 MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
8105 IS_CLASSIC_ENEMY(element1) ||
8106 IS_CLASSIC_ENEMY(element2)) &&
8107 element1 != EL_DRAGON && element2 != EL_DRAGON &&
8108 element1 != EL_FLAMES && element2 != EL_FLAMES)
8110 ResetGfxAnimation(x, y);
8111 GfxAction[x][y] = ACTION_ATTACKING;
8113 if (IS_PLAYER(x, y))
8114 DrawPlayerField(x, y);
8116 TEST_DrawLevelField(x, y);
8118 PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
8120 MovDelay[x][y] = 50;
8122 Feld[newx][newy] = EL_FLAMES;
8123 if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
8124 Feld[newx1][newy1] = EL_FLAMES;
8125 if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
8126 Feld[newx2][newy2] = EL_FLAMES;
8132 else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8133 Feld[newx][newy] == EL_DIAMOND)
8135 if (IS_MOVING(newx, newy))
8136 RemoveMovingField(newx, newy);
8139 Feld[newx][newy] = EL_EMPTY;
8140 TEST_DrawLevelField(newx, newy);
8143 PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
8145 else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8146 IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
8148 if (AmoebaNr[newx][newy])
8150 AmoebaCnt2[AmoebaNr[newx][newy]]--;
8151 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
8152 Feld[newx][newy] == EL_BD_AMOEBA)
8153 AmoebaCnt[AmoebaNr[newx][newy]]--;
8156 if (IS_MOVING(newx, newy))
8158 RemoveMovingField(newx, newy);
8162 Feld[newx][newy] = EL_EMPTY;
8163 TEST_DrawLevelField(newx, newy);
8166 PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
8168 else if ((element == EL_PACMAN || element == EL_MOLE)
8169 && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
8171 if (AmoebaNr[newx][newy])
8173 AmoebaCnt2[AmoebaNr[newx][newy]]--;
8174 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
8175 Feld[newx][newy] == EL_BD_AMOEBA)
8176 AmoebaCnt[AmoebaNr[newx][newy]]--;
8179 if (element == EL_MOLE)
8181 Feld[newx][newy] = EL_AMOEBA_SHRINKING;
8182 PlayLevelSound(x, y, SND_MOLE_DIGGING);
8184 ResetGfxAnimation(x, y);
8185 GfxAction[x][y] = ACTION_DIGGING;
8186 TEST_DrawLevelField(x, y);
8188 MovDelay[newx][newy] = 0; /* start amoeba shrinking delay */
8190 return; /* wait for shrinking amoeba */
8192 else /* element == EL_PACMAN */
8194 Feld[newx][newy] = EL_EMPTY;
8195 TEST_DrawLevelField(newx, newy);
8196 PlayLevelSound(x, y, SND_PACMAN_DIGGING);
8199 else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
8200 (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
8201 (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
8203 /* wait for shrinking amoeba to completely disappear */
8206 else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
8208 /* object was running against a wall */
8212 if (GFX_ELEMENT(element) != EL_SAND) /* !!! FIX THIS (crumble) !!! */
8213 DrawLevelElementAnimation(x, y, element);
8215 if (DONT_TOUCH(element))
8216 TestIfBadThingTouchesPlayer(x, y);
8221 InitMovingField(x, y, MovDir[x][y]);
8223 PlayLevelSoundAction(x, y, ACTION_MOVING);
8227 ContinueMoving(x, y);
8230 void ContinueMoving(int x, int y)
8232 int element = Feld[x][y];
8233 struct ElementInfo *ei = &element_info[element];
8234 int direction = MovDir[x][y];
8235 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
8236 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
8237 int newx = x + dx, newy = y + dy;
8238 int stored = Store[x][y];
8239 int stored_new = Store[newx][newy];
8240 boolean pushed_by_player = (Pushed[x][y] && IS_PLAYER(x, y));
8241 boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
8242 boolean last_line = (newy == lev_fieldy - 1);
8244 MovPos[x][y] += getElementMoveStepsize(x, y);
8246 if (pushed_by_player) /* special case: moving object pushed by player */
8247 MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
8249 if (ABS(MovPos[x][y]) < TILEX)
8251 TEST_DrawLevelField(x, y);
8253 return; /* element is still moving */
8256 /* element reached destination field */
8258 Feld[x][y] = EL_EMPTY;
8259 Feld[newx][newy] = element;
8260 MovPos[x][y] = 0; /* force "not moving" for "crumbled sand" */
8262 if (Store[x][y] == EL_ACID) /* element is moving into acid pool */
8264 element = Feld[newx][newy] = EL_ACID;
8266 else if (element == EL_MOLE)
8268 Feld[x][y] = EL_SAND;
8270 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8272 else if (element == EL_QUICKSAND_FILLING)
8274 element = Feld[newx][newy] = get_next_element(element);
8275 Store[newx][newy] = Store[x][y];
8277 else if (element == EL_QUICKSAND_EMPTYING)
8279 Feld[x][y] = get_next_element(element);
8280 element = Feld[newx][newy] = Store[x][y];
8282 else if (element == EL_QUICKSAND_FAST_FILLING)
8284 element = Feld[newx][newy] = get_next_element(element);
8285 Store[newx][newy] = Store[x][y];
8287 else if (element == EL_QUICKSAND_FAST_EMPTYING)
8289 Feld[x][y] = get_next_element(element);
8290 element = Feld[newx][newy] = Store[x][y];
8292 else if (element == EL_MAGIC_WALL_FILLING)
8294 element = Feld[newx][newy] = get_next_element(element);
8295 if (!game.magic_wall_active)
8296 element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
8297 Store[newx][newy] = Store[x][y];
8299 else if (element == EL_MAGIC_WALL_EMPTYING)
8301 Feld[x][y] = get_next_element(element);
8302 if (!game.magic_wall_active)
8303 Feld[x][y] = EL_MAGIC_WALL_DEAD;
8304 element = Feld[newx][newy] = Store[x][y];
8306 InitField(newx, newy, FALSE);
8308 else if (element == EL_BD_MAGIC_WALL_FILLING)
8310 element = Feld[newx][newy] = get_next_element(element);
8311 if (!game.magic_wall_active)
8312 element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
8313 Store[newx][newy] = Store[x][y];
8315 else if (element == EL_BD_MAGIC_WALL_EMPTYING)
8317 Feld[x][y] = get_next_element(element);
8318 if (!game.magic_wall_active)
8319 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
8320 element = Feld[newx][newy] = Store[x][y];
8322 InitField(newx, newy, FALSE);
8324 else if (element == EL_DC_MAGIC_WALL_FILLING)
8326 element = Feld[newx][newy] = get_next_element(element);
8327 if (!game.magic_wall_active)
8328 element = Feld[newx][newy] = EL_DC_MAGIC_WALL_DEAD;
8329 Store[newx][newy] = Store[x][y];
8331 else if (element == EL_DC_MAGIC_WALL_EMPTYING)
8333 Feld[x][y] = get_next_element(element);
8334 if (!game.magic_wall_active)
8335 Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
8336 element = Feld[newx][newy] = Store[x][y];
8338 InitField(newx, newy, FALSE);
8340 else if (element == EL_AMOEBA_DROPPING)
8342 Feld[x][y] = get_next_element(element);
8343 element = Feld[newx][newy] = Store[x][y];
8345 else if (element == EL_SOKOBAN_OBJECT)
8348 Feld[x][y] = Back[x][y];
8350 if (Back[newx][newy])
8351 Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
8353 Back[x][y] = Back[newx][newy] = 0;
8356 Store[x][y] = EL_EMPTY;
8361 MovDelay[newx][newy] = 0;
8363 if (CAN_CHANGE_OR_HAS_ACTION(element))
8365 /* copy element change control values to new field */
8366 ChangeDelay[newx][newy] = ChangeDelay[x][y];
8367 ChangePage[newx][newy] = ChangePage[x][y];
8368 ChangeCount[newx][newy] = ChangeCount[x][y];
8369 ChangeEvent[newx][newy] = ChangeEvent[x][y];
8372 CustomValue[newx][newy] = CustomValue[x][y];
8374 ChangeDelay[x][y] = 0;
8375 ChangePage[x][y] = -1;
8376 ChangeCount[x][y] = 0;
8377 ChangeEvent[x][y] = -1;
8379 CustomValue[x][y] = 0;
8381 /* copy animation control values to new field */
8382 GfxFrame[newx][newy] = GfxFrame[x][y];
8383 GfxRandom[newx][newy] = GfxRandom[x][y]; /* keep same random value */
8384 GfxAction[newx][newy] = GfxAction[x][y]; /* keep action one frame */
8385 GfxDir[newx][newy] = GfxDir[x][y]; /* keep element direction */
8387 Pushed[x][y] = Pushed[newx][newy] = FALSE;
8389 /* some elements can leave other elements behind after moving */
8390 if (ei->move_leave_element != EL_EMPTY &&
8391 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
8392 (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
8394 int move_leave_element = ei->move_leave_element;
8396 /* this makes it possible to leave the removed element again */
8397 if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
8398 move_leave_element = (stored == EL_ACID ? EL_EMPTY : stored);
8400 Feld[x][y] = move_leave_element;
8402 if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
8403 MovDir[x][y] = direction;
8405 InitField(x, y, FALSE);
8407 if (GFX_CRUMBLED(Feld[x][y]))
8408 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8410 if (ELEM_IS_PLAYER(move_leave_element))
8411 RelocatePlayer(x, y, move_leave_element);
8414 /* do this after checking for left-behind element */
8415 ResetGfxAnimation(x, y); /* reset animation values for old field */
8417 if (!CAN_MOVE(element) ||
8418 (CAN_FALL(element) && direction == MV_DOWN &&
8419 (element == EL_SPRING ||
8420 element_info[element].move_pattern == MV_WHEN_PUSHED ||
8421 element_info[element].move_pattern == MV_WHEN_DROPPED)))
8422 GfxDir[x][y] = MovDir[newx][newy] = 0;
8424 TEST_DrawLevelField(x, y);
8425 TEST_DrawLevelField(newx, newy);
8427 Stop[newx][newy] = TRUE; /* ignore this element until the next frame */
8429 /* prevent pushed element from moving on in pushed direction */
8430 if (pushed_by_player && CAN_MOVE(element) &&
8431 element_info[element].move_pattern & MV_ANY_DIRECTION &&
8432 !(element_info[element].move_pattern & direction))
8433 TurnRound(newx, newy);
8435 /* prevent elements on conveyor belt from moving on in last direction */
8436 if (pushed_by_conveyor && CAN_FALL(element) &&
8437 direction & MV_HORIZONTAL)
8438 MovDir[newx][newy] = 0;
8440 if (!pushed_by_player)
8442 int nextx = newx + dx, nexty = newy + dy;
8443 boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
8445 WasJustMoving[newx][newy] = CHECK_DELAY_MOVING;
8447 if (CAN_FALL(element) && direction == MV_DOWN)
8448 WasJustFalling[newx][newy] = CHECK_DELAY_FALLING;
8450 if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
8451 CheckCollision[newx][newy] = CHECK_DELAY_COLLISION;
8453 if (CAN_FALL(element) && direction == MV_DOWN && check_collision_again)
8454 CheckImpact[newx][newy] = CHECK_DELAY_IMPACT;
8457 if (DONT_TOUCH(element)) /* object may be nasty to player or others */
8459 TestIfBadThingTouchesPlayer(newx, newy);
8460 TestIfBadThingTouchesFriend(newx, newy);
8462 if (!IS_CUSTOM_ELEMENT(element))
8463 TestIfBadThingTouchesOtherBadThing(newx, newy);
8465 else if (element == EL_PENGUIN)
8466 TestIfFriendTouchesBadThing(newx, newy);
8468 if (DONT_GET_HIT_BY(element))
8470 TestIfGoodThingGetsHitByBadThing(newx, newy, direction);
8473 /* give the player one last chance (one more frame) to move away */
8474 if (CAN_FALL(element) && direction == MV_DOWN &&
8475 (last_line || (!IS_FREE(x, newy + 1) &&
8476 (!IS_PLAYER(x, newy + 1) ||
8477 game.engine_version < VERSION_IDENT(3,1,1,0)))))
8480 if (pushed_by_player && !game.use_change_when_pushing_bug)
8482 int push_side = MV_DIR_OPPOSITE(direction);
8483 struct PlayerInfo *player = PLAYERINFO(x, y);
8485 CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
8486 player->index_bit, push_side);
8487 CheckTriggeredElementChangeByPlayer(newx,newy, element, CE_PLAYER_PUSHES_X,
8488 player->index_bit, push_side);
8491 if (element == EL_EMC_ANDROID && pushed_by_player) /* make another move */
8492 MovDelay[newx][newy] = 1;
8494 CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
8496 TestIfElementTouchesCustomElement(x, y); /* empty or new element */
8497 TestIfElementHitsCustomElement(newx, newy, direction);
8498 TestIfPlayerTouchesCustomElement(newx, newy);
8499 TestIfElementTouchesCustomElement(newx, newy);
8501 if (IS_CUSTOM_ELEMENT(element) && ei->move_enter_element != EL_EMPTY &&
8502 IS_EQUAL_OR_IN_GROUP(stored_new, ei->move_enter_element))
8503 CheckElementChangeBySide(newx, newy, element, stored_new, CE_DIGGING_X,
8504 MV_DIR_OPPOSITE(direction));
8507 int AmoebeNachbarNr(int ax, int ay)
8510 int element = Feld[ax][ay];
8512 static int xy[4][2] =
8520 for (i = 0; i < NUM_DIRECTIONS; i++)
8522 int x = ax + xy[i][0];
8523 int y = ay + xy[i][1];
8525 if (!IN_LEV_FIELD(x, y))
8528 if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
8529 group_nr = AmoebaNr[x][y];
8535 void AmoebenVereinigen(int ax, int ay)
8537 int i, x, y, xx, yy;
8538 int new_group_nr = AmoebaNr[ax][ay];
8539 static int xy[4][2] =
8547 if (new_group_nr == 0)
8550 for (i = 0; i < NUM_DIRECTIONS; i++)
8555 if (!IN_LEV_FIELD(x, y))
8558 if ((Feld[x][y] == EL_AMOEBA_FULL ||
8559 Feld[x][y] == EL_BD_AMOEBA ||
8560 Feld[x][y] == EL_AMOEBA_DEAD) &&
8561 AmoebaNr[x][y] != new_group_nr)
8563 int old_group_nr = AmoebaNr[x][y];
8565 if (old_group_nr == 0)
8568 AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
8569 AmoebaCnt[old_group_nr] = 0;
8570 AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
8571 AmoebaCnt2[old_group_nr] = 0;
8573 SCAN_PLAYFIELD(xx, yy)
8575 if (AmoebaNr[xx][yy] == old_group_nr)
8576 AmoebaNr[xx][yy] = new_group_nr;
8582 void AmoebeUmwandeln(int ax, int ay)
8586 if (Feld[ax][ay] == EL_AMOEBA_DEAD)
8588 int group_nr = AmoebaNr[ax][ay];
8593 printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
8594 printf("AmoebeUmwandeln(): This should never happen!\n");
8599 SCAN_PLAYFIELD(x, y)
8601 if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
8604 Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
8608 PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
8609 SND_AMOEBA_TURNING_TO_GEM :
8610 SND_AMOEBA_TURNING_TO_ROCK));
8615 static int xy[4][2] =
8623 for (i = 0; i < NUM_DIRECTIONS; i++)
8628 if (!IN_LEV_FIELD(x, y))
8631 if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
8633 PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
8634 SND_AMOEBA_TURNING_TO_GEM :
8635 SND_AMOEBA_TURNING_TO_ROCK));
8642 void AmoebeUmwandelnBD(int ax, int ay, int new_element)
8645 int group_nr = AmoebaNr[ax][ay];
8646 boolean done = FALSE;
8651 printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
8652 printf("AmoebeUmwandelnBD(): This should never happen!\n");
8657 SCAN_PLAYFIELD(x, y)
8659 if (AmoebaNr[x][y] == group_nr &&
8660 (Feld[x][y] == EL_AMOEBA_DEAD ||
8661 Feld[x][y] == EL_BD_AMOEBA ||
8662 Feld[x][y] == EL_AMOEBA_GROWING))
8665 Feld[x][y] = new_element;
8666 InitField(x, y, FALSE);
8667 TEST_DrawLevelField(x, y);
8673 PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
8674 SND_BD_AMOEBA_TURNING_TO_ROCK :
8675 SND_BD_AMOEBA_TURNING_TO_GEM));
8678 void AmoebeWaechst(int x, int y)
8680 static unsigned int sound_delay = 0;
8681 static unsigned int sound_delay_value = 0;
8683 if (!MovDelay[x][y]) /* start new growing cycle */
8687 if (DelayReached(&sound_delay, sound_delay_value))
8689 PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
8690 sound_delay_value = 30;
8694 if (MovDelay[x][y]) /* wait some time before growing bigger */
8697 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
8699 int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
8700 6 - MovDelay[x][y]);
8702 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
8705 if (!MovDelay[x][y])
8707 Feld[x][y] = Store[x][y];
8709 TEST_DrawLevelField(x, y);
8714 void AmoebaDisappearing(int x, int y)
8716 static unsigned int sound_delay = 0;
8717 static unsigned int sound_delay_value = 0;
8719 if (!MovDelay[x][y]) /* start new shrinking cycle */
8723 if (DelayReached(&sound_delay, sound_delay_value))
8724 sound_delay_value = 30;
8727 if (MovDelay[x][y]) /* wait some time before shrinking */
8730 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
8732 int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
8733 6 - MovDelay[x][y]);
8735 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
8738 if (!MovDelay[x][y])
8740 Feld[x][y] = EL_EMPTY;
8741 TEST_DrawLevelField(x, y);
8743 /* don't let mole enter this field in this cycle;
8744 (give priority to objects falling to this field from above) */
8750 void AmoebeAbleger(int ax, int ay)
8753 int element = Feld[ax][ay];
8754 int graphic = el2img(element);
8755 int newax = ax, neway = ay;
8756 boolean can_drop = (element == EL_AMOEBA_WET || element == EL_EMC_DRIPPER);
8757 static int xy[4][2] =
8765 if (!level.amoeba_speed && element != EL_EMC_DRIPPER)
8767 Feld[ax][ay] = EL_AMOEBA_DEAD;
8768 TEST_DrawLevelField(ax, ay);
8772 if (IS_ANIMATED(graphic))
8773 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
8775 if (!MovDelay[ax][ay]) /* start making new amoeba field */
8776 MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
8778 if (MovDelay[ax][ay]) /* wait some time before making new amoeba */
8781 if (MovDelay[ax][ay])
8785 if (can_drop) /* EL_AMOEBA_WET or EL_EMC_DRIPPER */
8788 int x = ax + xy[start][0];
8789 int y = ay + xy[start][1];
8791 if (!IN_LEV_FIELD(x, y))
8794 if (IS_FREE(x, y) ||
8795 CAN_GROW_INTO(Feld[x][y]) ||
8796 Feld[x][y] == EL_QUICKSAND_EMPTY ||
8797 Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
8803 if (newax == ax && neway == ay)
8806 else /* normal or "filled" (BD style) amoeba */
8809 boolean waiting_for_player = FALSE;
8811 for (i = 0; i < NUM_DIRECTIONS; i++)
8813 int j = (start + i) % 4;
8814 int x = ax + xy[j][0];
8815 int y = ay + xy[j][1];
8817 if (!IN_LEV_FIELD(x, y))
8820 if (IS_FREE(x, y) ||
8821 CAN_GROW_INTO(Feld[x][y]) ||
8822 Feld[x][y] == EL_QUICKSAND_EMPTY ||
8823 Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
8829 else if (IS_PLAYER(x, y))
8830 waiting_for_player = TRUE;
8833 if (newax == ax && neway == ay) /* amoeba cannot grow */
8835 if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
8837 Feld[ax][ay] = EL_AMOEBA_DEAD;
8838 TEST_DrawLevelField(ax, ay);
8839 AmoebaCnt[AmoebaNr[ax][ay]]--;
8841 if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0) /* amoeba is completely dead */
8843 if (element == EL_AMOEBA_FULL)
8844 AmoebeUmwandeln(ax, ay);
8845 else if (element == EL_BD_AMOEBA)
8846 AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
8851 else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
8853 /* amoeba gets larger by growing in some direction */
8855 int new_group_nr = AmoebaNr[ax][ay];
8858 if (new_group_nr == 0)
8860 printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
8861 printf("AmoebeAbleger(): This should never happen!\n");
8866 AmoebaNr[newax][neway] = new_group_nr;
8867 AmoebaCnt[new_group_nr]++;
8868 AmoebaCnt2[new_group_nr]++;
8870 /* if amoeba touches other amoeba(s) after growing, unify them */
8871 AmoebenVereinigen(newax, neway);
8873 if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
8875 AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
8881 if (!can_drop || neway < ay || !IS_FREE(newax, neway) ||
8882 (neway == lev_fieldy - 1 && newax != ax))
8884 Feld[newax][neway] = EL_AMOEBA_GROWING; /* creation of new amoeba */
8885 Store[newax][neway] = element;
8887 else if (neway == ay || element == EL_EMC_DRIPPER)
8889 Feld[newax][neway] = EL_AMOEBA_DROP; /* drop left/right of amoeba */
8891 PlayLevelSoundAction(newax, neway, ACTION_GROWING);
8895 InitMovingField(ax, ay, MV_DOWN); /* drop dripping from amoeba */
8896 Feld[ax][ay] = EL_AMOEBA_DROPPING;
8897 Store[ax][ay] = EL_AMOEBA_DROP;
8898 ContinueMoving(ax, ay);
8902 TEST_DrawLevelField(newax, neway);
8905 void Life(int ax, int ay)
8909 int element = Feld[ax][ay];
8910 int graphic = el2img(element);
8911 int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
8913 boolean changed = FALSE;
8915 if (IS_ANIMATED(graphic))
8916 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
8921 if (!MovDelay[ax][ay]) /* start new "game of life" cycle */
8922 MovDelay[ax][ay] = life_time;
8924 if (MovDelay[ax][ay]) /* wait some time before next cycle */
8927 if (MovDelay[ax][ay])
8931 for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
8933 int xx = ax+x1, yy = ay+y1;
8936 if (!IN_LEV_FIELD(xx, yy))
8939 for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
8941 int x = xx+x2, y = yy+y2;
8943 if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
8946 if (((Feld[x][y] == element ||
8947 (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y))) &&
8949 (IS_FREE(x, y) && Stop[x][y]))
8953 if (xx == ax && yy == ay) /* field in the middle */
8955 if (nachbarn < life_parameter[0] ||
8956 nachbarn > life_parameter[1])
8958 Feld[xx][yy] = EL_EMPTY;
8960 TEST_DrawLevelField(xx, yy);
8961 Stop[xx][yy] = TRUE;
8965 else if (IS_FREE(xx, yy) || CAN_GROW_INTO(Feld[xx][yy]))
8966 { /* free border field */
8967 if (nachbarn >= life_parameter[2] &&
8968 nachbarn <= life_parameter[3])
8970 Feld[xx][yy] = element;
8971 MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
8973 TEST_DrawLevelField(xx, yy);
8974 Stop[xx][yy] = TRUE;
8981 PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
8982 SND_GAME_OF_LIFE_GROWING);
8985 static void InitRobotWheel(int x, int y)
8987 ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
8990 static void RunRobotWheel(int x, int y)
8992 PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
8995 static void StopRobotWheel(int x, int y)
8997 if (ZX == x && ZY == y)
9001 game.robot_wheel_active = FALSE;
9005 static void InitTimegateWheel(int x, int y)
9007 ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
9010 static void RunTimegateWheel(int x, int y)
9012 PlayLevelSound(x, y, SND_CLASS_TIMEGATE_SWITCH_ACTIVE);
9015 static void InitMagicBallDelay(int x, int y)
9017 ChangeDelay[x][y] = (level.ball_time + 1) * 8 + 1;
9020 static void ActivateMagicBall(int bx, int by)
9024 if (level.ball_random)
9026 int pos_border = RND(8); /* select one of the eight border elements */
9027 int pos_content = (pos_border > 3 ? pos_border + 1 : pos_border);
9028 int xx = pos_content % 3;
9029 int yy = pos_content / 3;
9034 if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
9035 CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9039 for (y = by - 1; y <= by + 1; y++) for (x = bx - 1; x <= bx + 1; x++)
9041 int xx = x - bx + 1;
9042 int yy = y - by + 1;
9044 if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
9045 CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9049 game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
9052 void CheckExit(int x, int y)
9054 if (local_player->gems_still_needed > 0 ||
9055 local_player->sokobanfields_still_needed > 0 ||
9056 local_player->lights_still_needed > 0)
9058 int element = Feld[x][y];
9059 int graphic = el2img(element);
9061 if (IS_ANIMATED(graphic))
9062 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9067 if (AllPlayersGone) /* do not re-open exit door closed after last player */
9070 Feld[x][y] = EL_EXIT_OPENING;
9072 PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
9075 void CheckExitEM(int x, int y)
9077 if (local_player->gems_still_needed > 0 ||
9078 local_player->sokobanfields_still_needed > 0 ||
9079 local_player->lights_still_needed > 0)
9081 int element = Feld[x][y];
9082 int graphic = el2img(element);
9084 if (IS_ANIMATED(graphic))
9085 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9090 if (AllPlayersGone) /* do not re-open exit door closed after last player */
9093 Feld[x][y] = EL_EM_EXIT_OPENING;
9095 PlayLevelSoundNearest(x, y, SND_CLASS_EM_EXIT_OPENING);
9098 void CheckExitSteel(int x, int y)
9100 if (local_player->gems_still_needed > 0 ||
9101 local_player->sokobanfields_still_needed > 0 ||
9102 local_player->lights_still_needed > 0)
9104 int element = Feld[x][y];
9105 int graphic = el2img(element);
9107 if (IS_ANIMATED(graphic))
9108 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9113 if (AllPlayersGone) /* do not re-open exit door closed after last player */
9116 Feld[x][y] = EL_STEEL_EXIT_OPENING;
9118 PlayLevelSoundNearest(x, y, SND_CLASS_STEEL_EXIT_OPENING);
9121 void CheckExitSteelEM(int x, int y)
9123 if (local_player->gems_still_needed > 0 ||
9124 local_player->sokobanfields_still_needed > 0 ||
9125 local_player->lights_still_needed > 0)
9127 int element = Feld[x][y];
9128 int graphic = el2img(element);
9130 if (IS_ANIMATED(graphic))
9131 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9136 if (AllPlayersGone) /* do not re-open exit door closed after last player */
9139 Feld[x][y] = EL_EM_STEEL_EXIT_OPENING;
9141 PlayLevelSoundNearest(x, y, SND_CLASS_EM_STEEL_EXIT_OPENING);
9144 void CheckExitSP(int x, int y)
9146 if (local_player->gems_still_needed > 0)
9148 int element = Feld[x][y];
9149 int graphic = el2img(element);
9151 if (IS_ANIMATED(graphic))
9152 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9157 if (AllPlayersGone) /* do not re-open exit door closed after last player */
9160 Feld[x][y] = EL_SP_EXIT_OPENING;
9162 PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
9165 static void CloseAllOpenTimegates()
9169 SCAN_PLAYFIELD(x, y)
9171 int element = Feld[x][y];
9173 if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
9175 Feld[x][y] = EL_TIMEGATE_CLOSING;
9177 PlayLevelSoundAction(x, y, ACTION_CLOSING);
9182 void DrawTwinkleOnField(int x, int y)
9184 if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
9187 if (Feld[x][y] == EL_BD_DIAMOND)
9190 if (MovDelay[x][y] == 0) /* next animation frame */
9191 MovDelay[x][y] = 11 * !GetSimpleRandom(500);
9193 if (MovDelay[x][y] != 0) /* wait some time before next frame */
9197 DrawLevelElementAnimation(x, y, Feld[x][y]);
9199 if (MovDelay[x][y] != 0)
9201 int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
9202 10 - MovDelay[x][y]);
9204 DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
9209 void MauerWaechst(int x, int y)
9213 if (!MovDelay[x][y]) /* next animation frame */
9214 MovDelay[x][y] = 3 * delay;
9216 if (MovDelay[x][y]) /* wait some time before next frame */
9220 if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9222 int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
9223 int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
9225 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
9228 if (!MovDelay[x][y])
9230 if (MovDir[x][y] == MV_LEFT)
9232 if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
9233 TEST_DrawLevelField(x - 1, y);
9235 else if (MovDir[x][y] == MV_RIGHT)
9237 if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
9238 TEST_DrawLevelField(x + 1, y);
9240 else if (MovDir[x][y] == MV_UP)
9242 if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
9243 TEST_DrawLevelField(x, y - 1);
9247 if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
9248 TEST_DrawLevelField(x, y + 1);
9251 Feld[x][y] = Store[x][y];
9253 GfxDir[x][y] = MovDir[x][y] = MV_NONE;
9254 TEST_DrawLevelField(x, y);
9259 void MauerAbleger(int ax, int ay)
9261 int element = Feld[ax][ay];
9262 int graphic = el2img(element);
9263 boolean oben_frei = FALSE, unten_frei = FALSE;
9264 boolean links_frei = FALSE, rechts_frei = FALSE;
9265 boolean oben_massiv = FALSE, unten_massiv = FALSE;
9266 boolean links_massiv = FALSE, rechts_massiv = FALSE;
9267 boolean new_wall = FALSE;
9269 if (IS_ANIMATED(graphic))
9270 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9272 if (!MovDelay[ax][ay]) /* start building new wall */
9273 MovDelay[ax][ay] = 6;
9275 if (MovDelay[ax][ay]) /* wait some time before building new wall */
9278 if (MovDelay[ax][ay])
9282 if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9284 if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9286 if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9288 if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9291 if (element == EL_EXPANDABLE_WALL_VERTICAL ||
9292 element == EL_EXPANDABLE_WALL_ANY)
9296 Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
9297 Store[ax][ay-1] = element;
9298 GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9299 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9300 DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9301 IMG_EXPANDABLE_WALL_GROWING_UP, 0);
9306 Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
9307 Store[ax][ay+1] = element;
9308 GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9309 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9310 DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9311 IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
9316 if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9317 element == EL_EXPANDABLE_WALL_ANY ||
9318 element == EL_EXPANDABLE_WALL ||
9319 element == EL_BD_EXPANDABLE_WALL)
9323 Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
9324 Store[ax-1][ay] = element;
9325 GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9326 if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9327 DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9328 IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
9334 Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
9335 Store[ax+1][ay] = element;
9336 GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9337 if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9338 DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9339 IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
9344 if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
9345 TEST_DrawLevelField(ax, ay);
9347 if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
9349 if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
9350 unten_massiv = TRUE;
9351 if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
9352 links_massiv = TRUE;
9353 if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
9354 rechts_massiv = TRUE;
9356 if (((oben_massiv && unten_massiv) ||
9357 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9358 element == EL_EXPANDABLE_WALL) &&
9359 ((links_massiv && rechts_massiv) ||
9360 element == EL_EXPANDABLE_WALL_VERTICAL))
9361 Feld[ax][ay] = EL_WALL;
9364 PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9367 void MauerAblegerStahl(int ax, int ay)
9369 int element = Feld[ax][ay];
9370 int graphic = el2img(element);
9371 boolean oben_frei = FALSE, unten_frei = FALSE;
9372 boolean links_frei = FALSE, rechts_frei = FALSE;
9373 boolean oben_massiv = FALSE, unten_massiv = FALSE;
9374 boolean links_massiv = FALSE, rechts_massiv = FALSE;
9375 boolean new_wall = FALSE;
9377 if (IS_ANIMATED(graphic))
9378 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9380 if (!MovDelay[ax][ay]) /* start building new wall */
9381 MovDelay[ax][ay] = 6;
9383 if (MovDelay[ax][ay]) /* wait some time before building new wall */
9386 if (MovDelay[ax][ay])
9390 if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9392 if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9394 if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9396 if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9399 if (element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
9400 element == EL_EXPANDABLE_STEELWALL_ANY)
9404 Feld[ax][ay-1] = EL_EXPANDABLE_STEELWALL_GROWING;
9405 Store[ax][ay-1] = element;
9406 GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9407 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9408 DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9409 IMG_EXPANDABLE_STEELWALL_GROWING_UP, 0);
9414 Feld[ax][ay+1] = EL_EXPANDABLE_STEELWALL_GROWING;
9415 Store[ax][ay+1] = element;
9416 GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9417 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9418 DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9419 IMG_EXPANDABLE_STEELWALL_GROWING_DOWN, 0);
9424 if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
9425 element == EL_EXPANDABLE_STEELWALL_ANY)
9429 Feld[ax-1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9430 Store[ax-1][ay] = element;
9431 GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9432 if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9433 DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9434 IMG_EXPANDABLE_STEELWALL_GROWING_LEFT, 0);
9440 Feld[ax+1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9441 Store[ax+1][ay] = element;
9442 GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9443 if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9444 DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9445 IMG_EXPANDABLE_STEELWALL_GROWING_RIGHT, 0);
9450 if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
9452 if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
9453 unten_massiv = TRUE;
9454 if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
9455 links_massiv = TRUE;
9456 if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
9457 rechts_massiv = TRUE;
9459 if (((oben_massiv && unten_massiv) ||
9460 element == EL_EXPANDABLE_STEELWALL_HORIZONTAL) &&
9461 ((links_massiv && rechts_massiv) ||
9462 element == EL_EXPANDABLE_STEELWALL_VERTICAL))
9463 Feld[ax][ay] = EL_STEELWALL;
9466 PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9469 void CheckForDragon(int x, int y)
9472 boolean dragon_found = FALSE;
9473 static int xy[4][2] =
9481 for (i = 0; i < NUM_DIRECTIONS; i++)
9483 for (j = 0; j < 4; j++)
9485 int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9487 if (IN_LEV_FIELD(xx, yy) &&
9488 (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
9490 if (Feld[xx][yy] == EL_DRAGON)
9491 dragon_found = TRUE;
9500 for (i = 0; i < NUM_DIRECTIONS; i++)
9502 for (j = 0; j < 3; j++)
9504 int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9506 if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
9508 Feld[xx][yy] = EL_EMPTY;
9509 TEST_DrawLevelField(xx, yy);
9518 static void InitBuggyBase(int x, int y)
9520 int element = Feld[x][y];
9521 int activating_delay = FRAMES_PER_SECOND / 4;
9524 (element == EL_SP_BUGGY_BASE ?
9525 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
9526 element == EL_SP_BUGGY_BASE_ACTIVATING ?
9528 element == EL_SP_BUGGY_BASE_ACTIVE ?
9529 1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
9532 static void WarnBuggyBase(int x, int y)
9535 static int xy[4][2] =
9543 for (i = 0; i < NUM_DIRECTIONS; i++)
9545 int xx = x + xy[i][0];
9546 int yy = y + xy[i][1];
9548 if (IN_LEV_FIELD(xx, yy) && IS_PLAYER(xx, yy))
9550 PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
9557 static void InitTrap(int x, int y)
9559 ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
9562 static void ActivateTrap(int x, int y)
9564 PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
9567 static void ChangeActiveTrap(int x, int y)
9569 int graphic = IMG_TRAP_ACTIVE;
9571 /* if new animation frame was drawn, correct crumbled sand border */
9572 if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
9573 TEST_DrawLevelFieldCrumbled(x, y);
9576 static int getSpecialActionElement(int element, int number, int base_element)
9578 return (element != EL_EMPTY ? element :
9579 number != -1 ? base_element + number - 1 :
9583 static int getModifiedActionNumber(int value_old, int operator, int operand,
9584 int value_min, int value_max)
9586 int value_new = (operator == CA_MODE_SET ? operand :
9587 operator == CA_MODE_ADD ? value_old + operand :
9588 operator == CA_MODE_SUBTRACT ? value_old - operand :
9589 operator == CA_MODE_MULTIPLY ? value_old * operand :
9590 operator == CA_MODE_DIVIDE ? value_old / MAX(1, operand) :
9591 operator == CA_MODE_MODULO ? value_old % MAX(1, operand) :
9594 return (value_new < value_min ? value_min :
9595 value_new > value_max ? value_max :
9599 static void ExecuteCustomElementAction(int x, int y, int element, int page)
9601 struct ElementInfo *ei = &element_info[element];
9602 struct ElementChangeInfo *change = &ei->change_page[page];
9603 int target_element = change->target_element;
9604 int action_type = change->action_type;
9605 int action_mode = change->action_mode;
9606 int action_arg = change->action_arg;
9607 int action_element = change->action_element;
9610 if (!change->has_action)
9613 /* ---------- determine action paramater values -------------------------- */
9615 int level_time_value =
9616 (level.time > 0 ? TimeLeft :
9619 int action_arg_element_raw =
9620 (action_arg == CA_ARG_PLAYER_TRIGGER ? change->actual_trigger_player :
9621 action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
9622 action_arg == CA_ARG_ELEMENT_TARGET ? change->target_element :
9623 action_arg == CA_ARG_ELEMENT_ACTION ? change->action_element :
9624 action_arg == CA_ARG_INVENTORY_RM_TRIGGER ? change->actual_trigger_element:
9625 action_arg == CA_ARG_INVENTORY_RM_TARGET ? change->target_element :
9626 action_arg == CA_ARG_INVENTORY_RM_ACTION ? change->action_element :
9628 int action_arg_element = GetElementFromGroupElement(action_arg_element_raw);
9630 int action_arg_direction =
9631 (action_arg >= CA_ARG_DIRECTION_LEFT &&
9632 action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
9633 action_arg == CA_ARG_DIRECTION_TRIGGER ?
9634 change->actual_trigger_side :
9635 action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
9636 MV_DIR_OPPOSITE(change->actual_trigger_side) :
9639 int action_arg_number_min =
9640 (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
9643 int action_arg_number_max =
9644 (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
9645 action_type == CA_SET_LEVEL_GEMS ? 999 :
9646 action_type == CA_SET_LEVEL_TIME ? 9999 :
9647 action_type == CA_SET_LEVEL_SCORE ? 99999 :
9648 action_type == CA_SET_CE_VALUE ? 9999 :
9649 action_type == CA_SET_CE_SCORE ? 9999 :
9652 int action_arg_number_reset =
9653 (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize[0] :
9654 action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
9655 action_type == CA_SET_LEVEL_TIME ? level.time :
9656 action_type == CA_SET_LEVEL_SCORE ? 0 :
9657 action_type == CA_SET_CE_VALUE ? GET_NEW_CE_VALUE(element) :
9658 action_type == CA_SET_CE_SCORE ? 0 :
9661 int action_arg_number =
9662 (action_arg <= CA_ARG_MAX ? action_arg :
9663 action_arg >= CA_ARG_SPEED_NOT_MOVING &&
9664 action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
9665 action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
9666 action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
9667 action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
9668 action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
9669 action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
9670 action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
9671 action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
9672 action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
9673 action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? local_player->gems_still_needed :
9674 action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? local_player->score :
9675 action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CE_VALUE(target_element):
9676 action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
9677 action_arg == CA_ARG_ELEMENT_CV_ACTION ? GET_NEW_CE_VALUE(action_element):
9678 action_arg == CA_ARG_ELEMENT_CS_TARGET ? GET_CE_SCORE(target_element) :
9679 action_arg == CA_ARG_ELEMENT_CS_TRIGGER ? change->actual_trigger_ce_score:
9680 action_arg == CA_ARG_ELEMENT_CS_ACTION ? GET_CE_SCORE(action_element) :
9681 action_arg == CA_ARG_ELEMENT_NR_TARGET ? change->target_element :
9682 action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
9683 action_arg == CA_ARG_ELEMENT_NR_ACTION ? change->action_element :
9686 int action_arg_number_old =
9687 (action_type == CA_SET_LEVEL_GEMS ? local_player->gems_still_needed :
9688 action_type == CA_SET_LEVEL_TIME ? TimeLeft :
9689 action_type == CA_SET_LEVEL_SCORE ? local_player->score :
9690 action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
9691 action_type == CA_SET_CE_SCORE ? ei->collect_score :
9694 int action_arg_number_new =
9695 getModifiedActionNumber(action_arg_number_old,
9696 action_mode, action_arg_number,
9697 action_arg_number_min, action_arg_number_max);
9699 int trigger_player_bits =
9700 (change->actual_trigger_player_bits != CH_PLAYER_NONE ?
9701 change->actual_trigger_player_bits : change->trigger_player);
9703 int action_arg_player_bits =
9704 (action_arg >= CA_ARG_PLAYER_1 &&
9705 action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
9706 action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
9707 action_arg == CA_ARG_PLAYER_ACTION ? 1 << GET_PLAYER_NR(action_element) :
9710 /* ---------- execute action -------------------------------------------- */
9712 switch (action_type)
9719 /* ---------- level actions ------------------------------------------- */
9721 case CA_RESTART_LEVEL:
9723 game.restart_level = TRUE;
9728 case CA_SHOW_ENVELOPE:
9730 int element = getSpecialActionElement(action_arg_element,
9731 action_arg_number, EL_ENVELOPE_1);
9733 if (IS_ENVELOPE(element))
9734 local_player->show_envelope = element;
9739 case CA_SET_LEVEL_TIME:
9741 if (level.time > 0) /* only modify limited time value */
9743 TimeLeft = action_arg_number_new;
9745 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
9747 DisplayGameControlValues();
9749 if (!TimeLeft && setup.time_limit)
9750 for (i = 0; i < MAX_PLAYERS; i++)
9751 KillPlayer(&stored_player[i]);
9757 case CA_SET_LEVEL_SCORE:
9759 local_player->score = action_arg_number_new;
9761 game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
9763 DisplayGameControlValues();
9768 case CA_SET_LEVEL_GEMS:
9770 local_player->gems_still_needed = action_arg_number_new;
9772 game.snapshot.collected_item = TRUE;
9774 game_panel_controls[GAME_PANEL_GEMS].value =
9775 local_player->gems_still_needed;
9777 DisplayGameControlValues();
9782 case CA_SET_LEVEL_WIND:
9784 game.wind_direction = action_arg_direction;
9789 case CA_SET_LEVEL_RANDOM_SEED:
9791 /* ensure that setting a new random seed while playing is predictable */
9792 InitRND(action_arg_number_new ? action_arg_number_new : RND(1000000) + 1);
9797 /* ---------- player actions ------------------------------------------ */
9799 case CA_MOVE_PLAYER:
9801 /* automatically move to the next field in specified direction */
9802 for (i = 0; i < MAX_PLAYERS; i++)
9803 if (trigger_player_bits & (1 << i))
9804 stored_player[i].programmed_action = action_arg_direction;
9809 case CA_EXIT_PLAYER:
9811 for (i = 0; i < MAX_PLAYERS; i++)
9812 if (action_arg_player_bits & (1 << i))
9813 PlayerWins(&stored_player[i]);
9818 case CA_KILL_PLAYER:
9820 for (i = 0; i < MAX_PLAYERS; i++)
9821 if (action_arg_player_bits & (1 << i))
9822 KillPlayer(&stored_player[i]);
9827 case CA_SET_PLAYER_KEYS:
9829 int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
9830 int element = getSpecialActionElement(action_arg_element,
9831 action_arg_number, EL_KEY_1);
9833 if (IS_KEY(element))
9835 for (i = 0; i < MAX_PLAYERS; i++)
9837 if (trigger_player_bits & (1 << i))
9839 stored_player[i].key[KEY_NR(element)] = key_state;
9841 DrawGameDoorValues();
9849 case CA_SET_PLAYER_SPEED:
9851 for (i = 0; i < MAX_PLAYERS; i++)
9853 if (trigger_player_bits & (1 << i))
9855 int move_stepsize = TILEX / stored_player[i].move_delay_value;
9857 if (action_arg == CA_ARG_SPEED_FASTER &&
9858 stored_player[i].cannot_move)
9860 action_arg_number = STEPSIZE_VERY_SLOW;
9862 else if (action_arg == CA_ARG_SPEED_SLOWER ||
9863 action_arg == CA_ARG_SPEED_FASTER)
9865 action_arg_number = 2;
9866 action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
9869 else if (action_arg == CA_ARG_NUMBER_RESET)
9871 action_arg_number = level.initial_player_stepsize[i];
9875 getModifiedActionNumber(move_stepsize,
9878 action_arg_number_min,
9879 action_arg_number_max);
9881 SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
9888 case CA_SET_PLAYER_SHIELD:
9890 for (i = 0; i < MAX_PLAYERS; i++)
9892 if (trigger_player_bits & (1 << i))
9894 if (action_arg == CA_ARG_SHIELD_OFF)
9896 stored_player[i].shield_normal_time_left = 0;
9897 stored_player[i].shield_deadly_time_left = 0;
9899 else if (action_arg == CA_ARG_SHIELD_NORMAL)
9901 stored_player[i].shield_normal_time_left = 999999;
9903 else if (action_arg == CA_ARG_SHIELD_DEADLY)
9905 stored_player[i].shield_normal_time_left = 999999;
9906 stored_player[i].shield_deadly_time_left = 999999;
9914 case CA_SET_PLAYER_GRAVITY:
9916 for (i = 0; i < MAX_PLAYERS; i++)
9918 if (trigger_player_bits & (1 << i))
9920 stored_player[i].gravity =
9921 (action_arg == CA_ARG_GRAVITY_OFF ? FALSE :
9922 action_arg == CA_ARG_GRAVITY_ON ? TRUE :
9923 action_arg == CA_ARG_GRAVITY_TOGGLE ? !stored_player[i].gravity :
9924 stored_player[i].gravity);
9931 case CA_SET_PLAYER_ARTWORK:
9933 for (i = 0; i < MAX_PLAYERS; i++)
9935 if (trigger_player_bits & (1 << i))
9937 int artwork_element = action_arg_element;
9939 if (action_arg == CA_ARG_ELEMENT_RESET)
9941 (level.use_artwork_element[i] ? level.artwork_element[i] :
9942 stored_player[i].element_nr);
9944 if (stored_player[i].artwork_element != artwork_element)
9945 stored_player[i].Frame = 0;
9947 stored_player[i].artwork_element = artwork_element;
9949 SetPlayerWaiting(&stored_player[i], FALSE);
9951 /* set number of special actions for bored and sleeping animation */
9952 stored_player[i].num_special_action_bored =
9953 get_num_special_action(artwork_element,
9954 ACTION_BORING_1, ACTION_BORING_LAST);
9955 stored_player[i].num_special_action_sleeping =
9956 get_num_special_action(artwork_element,
9957 ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
9964 case CA_SET_PLAYER_INVENTORY:
9966 for (i = 0; i < MAX_PLAYERS; i++)
9968 struct PlayerInfo *player = &stored_player[i];
9971 if (trigger_player_bits & (1 << i))
9973 int inventory_element = action_arg_element;
9975 if (action_arg == CA_ARG_ELEMENT_TARGET ||
9976 action_arg == CA_ARG_ELEMENT_TRIGGER ||
9977 action_arg == CA_ARG_ELEMENT_ACTION)
9979 int element = inventory_element;
9980 int collect_count = element_info[element].collect_count_initial;
9982 if (!IS_CUSTOM_ELEMENT(element))
9985 if (collect_count == 0)
9986 player->inventory_infinite_element = element;
9988 for (k = 0; k < collect_count; k++)
9989 if (player->inventory_size < MAX_INVENTORY_SIZE)
9990 player->inventory_element[player->inventory_size++] =
9993 else if (action_arg == CA_ARG_INVENTORY_RM_TARGET ||
9994 action_arg == CA_ARG_INVENTORY_RM_TRIGGER ||
9995 action_arg == CA_ARG_INVENTORY_RM_ACTION)
9997 if (player->inventory_infinite_element != EL_UNDEFINED &&
9998 IS_EQUAL_OR_IN_GROUP(player->inventory_infinite_element,
9999 action_arg_element_raw))
10000 player->inventory_infinite_element = EL_UNDEFINED;
10002 for (k = 0, j = 0; j < player->inventory_size; j++)
10004 if (!IS_EQUAL_OR_IN_GROUP(player->inventory_element[j],
10005 action_arg_element_raw))
10006 player->inventory_element[k++] = player->inventory_element[j];
10009 player->inventory_size = k;
10011 else if (action_arg == CA_ARG_INVENTORY_RM_FIRST)
10013 if (player->inventory_size > 0)
10015 for (j = 0; j < player->inventory_size - 1; j++)
10016 player->inventory_element[j] = player->inventory_element[j + 1];
10018 player->inventory_size--;
10021 else if (action_arg == CA_ARG_INVENTORY_RM_LAST)
10023 if (player->inventory_size > 0)
10024 player->inventory_size--;
10026 else if (action_arg == CA_ARG_INVENTORY_RM_ALL)
10028 player->inventory_infinite_element = EL_UNDEFINED;
10029 player->inventory_size = 0;
10031 else if (action_arg == CA_ARG_INVENTORY_RESET)
10033 player->inventory_infinite_element = EL_UNDEFINED;
10034 player->inventory_size = 0;
10036 if (level.use_initial_inventory[i])
10038 for (j = 0; j < level.initial_inventory_size[i]; j++)
10040 int element = level.initial_inventory_content[i][j];
10041 int collect_count = element_info[element].collect_count_initial;
10043 if (!IS_CUSTOM_ELEMENT(element))
10046 if (collect_count == 0)
10047 player->inventory_infinite_element = element;
10049 for (k = 0; k < collect_count; k++)
10050 if (player->inventory_size < MAX_INVENTORY_SIZE)
10051 player->inventory_element[player->inventory_size++] =
10062 /* ---------- CE actions ---------------------------------------------- */
10064 case CA_SET_CE_VALUE:
10066 int last_ce_value = CustomValue[x][y];
10068 CustomValue[x][y] = action_arg_number_new;
10070 if (CustomValue[x][y] != last_ce_value)
10072 CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_CHANGES);
10073 CheckTriggeredElementChange(x, y, element, CE_VALUE_CHANGES_OF_X);
10075 if (CustomValue[x][y] == 0)
10077 CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
10078 CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
10085 case CA_SET_CE_SCORE:
10087 int last_ce_score = ei->collect_score;
10089 ei->collect_score = action_arg_number_new;
10091 if (ei->collect_score != last_ce_score)
10093 CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_CHANGES);
10094 CheckTriggeredElementChange(x, y, element, CE_SCORE_CHANGES_OF_X);
10096 if (ei->collect_score == 0)
10100 CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_GETS_ZERO);
10101 CheckTriggeredElementChange(x, y, element, CE_SCORE_GETS_ZERO_OF_X);
10104 This is a very special case that seems to be a mixture between
10105 CheckElementChange() and CheckTriggeredElementChange(): while
10106 the first one only affects single elements that are triggered
10107 directly, the second one affects multiple elements in the playfield
10108 that are triggered indirectly by another element. This is a third
10109 case: Changing the CE score always affects multiple identical CEs,
10110 so every affected CE must be checked, not only the single CE for
10111 which the CE score was changed in the first place (as every instance
10112 of that CE shares the same CE score, and therefore also can change)!
10114 SCAN_PLAYFIELD(xx, yy)
10116 if (Feld[xx][yy] == element)
10117 CheckElementChange(xx, yy, element, EL_UNDEFINED,
10118 CE_SCORE_GETS_ZERO);
10126 case CA_SET_CE_ARTWORK:
10128 int artwork_element = action_arg_element;
10129 boolean reset_frame = FALSE;
10132 if (action_arg == CA_ARG_ELEMENT_RESET)
10133 artwork_element = (ei->use_gfx_element ? ei->gfx_element_initial :
10136 if (ei->gfx_element != artwork_element)
10137 reset_frame = TRUE;
10139 ei->gfx_element = artwork_element;
10141 SCAN_PLAYFIELD(xx, yy)
10143 if (Feld[xx][yy] == element)
10147 ResetGfxAnimation(xx, yy);
10148 ResetRandomAnimationValue(xx, yy);
10151 TEST_DrawLevelField(xx, yy);
10158 /* ---------- engine actions ------------------------------------------ */
10160 case CA_SET_ENGINE_SCAN_MODE:
10162 InitPlayfieldScanMode(action_arg);
10172 static void CreateFieldExt(int x, int y, int element, boolean is_change)
10174 int old_element = Feld[x][y];
10175 int new_element = GetElementFromGroupElement(element);
10176 int previous_move_direction = MovDir[x][y];
10177 int last_ce_value = CustomValue[x][y];
10178 boolean player_explosion_protected = PLAYER_EXPLOSION_PROTECTED(x, y);
10179 boolean new_element_is_player = ELEM_IS_PLAYER(new_element);
10180 boolean add_player_onto_element = (new_element_is_player &&
10181 new_element != EL_SOKOBAN_FIELD_PLAYER &&
10182 IS_WALKABLE(old_element));
10184 if (!add_player_onto_element)
10186 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
10187 RemoveMovingField(x, y);
10191 Feld[x][y] = new_element;
10193 if (element_info[new_element].move_direction_initial == MV_START_PREVIOUS)
10194 MovDir[x][y] = previous_move_direction;
10196 if (element_info[new_element].use_last_ce_value)
10197 CustomValue[x][y] = last_ce_value;
10199 InitField_WithBug1(x, y, FALSE);
10201 new_element = Feld[x][y]; /* element may have changed */
10203 ResetGfxAnimation(x, y);
10204 ResetRandomAnimationValue(x, y);
10206 TEST_DrawLevelField(x, y);
10208 if (GFX_CRUMBLED(new_element))
10209 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
10212 /* check if element under the player changes from accessible to unaccessible
10213 (needed for special case of dropping element which then changes) */
10214 /* (must be checked after creating new element for walkable group elements) */
10215 if (IS_PLAYER(x, y) && !player_explosion_protected &&
10216 IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
10223 /* "ChangeCount" not set yet to allow "entered by player" change one time */
10224 if (new_element_is_player)
10225 RelocatePlayer(x, y, new_element);
10228 ChangeCount[x][y]++; /* count number of changes in the same frame */
10230 TestIfBadThingTouchesPlayer(x, y);
10231 TestIfPlayerTouchesCustomElement(x, y);
10232 TestIfElementTouchesCustomElement(x, y);
10235 static void CreateField(int x, int y, int element)
10237 CreateFieldExt(x, y, element, FALSE);
10240 static void CreateElementFromChange(int x, int y, int element)
10242 element = GET_VALID_RUNTIME_ELEMENT(element);
10244 if (game.engine_version >= VERSION_IDENT(3,2,0,7))
10246 int old_element = Feld[x][y];
10248 /* prevent changed element from moving in same engine frame
10249 unless both old and new element can either fall or move */
10250 if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
10251 (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
10255 CreateFieldExt(x, y, element, TRUE);
10258 static boolean ChangeElement(int x, int y, int element, int page)
10260 struct ElementInfo *ei = &element_info[element];
10261 struct ElementChangeInfo *change = &ei->change_page[page];
10262 int ce_value = CustomValue[x][y];
10263 int ce_score = ei->collect_score;
10264 int target_element;
10265 int old_element = Feld[x][y];
10267 /* always use default change event to prevent running into a loop */
10268 if (ChangeEvent[x][y] == -1)
10269 ChangeEvent[x][y] = CE_DELAY;
10271 if (ChangeEvent[x][y] == CE_DELAY)
10273 /* reset actual trigger element, trigger player and action element */
10274 change->actual_trigger_element = EL_EMPTY;
10275 change->actual_trigger_player = EL_EMPTY;
10276 change->actual_trigger_player_bits = CH_PLAYER_NONE;
10277 change->actual_trigger_side = CH_SIDE_NONE;
10278 change->actual_trigger_ce_value = 0;
10279 change->actual_trigger_ce_score = 0;
10282 /* do not change elements more than a specified maximum number of changes */
10283 if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
10286 ChangeCount[x][y]++; /* count number of changes in the same frame */
10288 if (change->explode)
10295 if (change->use_target_content)
10297 boolean complete_replace = TRUE;
10298 boolean can_replace[3][3];
10301 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10304 boolean is_walkable;
10305 boolean is_diggable;
10306 boolean is_collectible;
10307 boolean is_removable;
10308 boolean is_destructible;
10309 int ex = x + xx - 1;
10310 int ey = y + yy - 1;
10311 int content_element = change->target_content.e[xx][yy];
10314 can_replace[xx][yy] = TRUE;
10316 if (ex == x && ey == y) /* do not check changing element itself */
10319 if (content_element == EL_EMPTY_SPACE)
10321 can_replace[xx][yy] = FALSE; /* do not replace border with space */
10326 if (!IN_LEV_FIELD(ex, ey))
10328 can_replace[xx][yy] = FALSE;
10329 complete_replace = FALSE;
10336 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10337 e = MovingOrBlocked2Element(ex, ey);
10339 is_empty = (IS_FREE(ex, ey) ||
10340 (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
10342 is_walkable = (is_empty || IS_WALKABLE(e));
10343 is_diggable = (is_empty || IS_DIGGABLE(e));
10344 is_collectible = (is_empty || IS_COLLECTIBLE(e));
10345 is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
10346 is_removable = (is_diggable || is_collectible);
10348 can_replace[xx][yy] =
10349 (((change->replace_when == CP_WHEN_EMPTY && is_empty) ||
10350 (change->replace_when == CP_WHEN_WALKABLE && is_walkable) ||
10351 (change->replace_when == CP_WHEN_DIGGABLE && is_diggable) ||
10352 (change->replace_when == CP_WHEN_COLLECTIBLE && is_collectible) ||
10353 (change->replace_when == CP_WHEN_REMOVABLE && is_removable) ||
10354 (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
10355 !(IS_PLAYER(ex, ey) && ELEM_IS_PLAYER(content_element)));
10357 if (!can_replace[xx][yy])
10358 complete_replace = FALSE;
10361 if (!change->only_if_complete || complete_replace)
10363 boolean something_has_changed = FALSE;
10365 if (change->only_if_complete && change->use_random_replace &&
10366 RND(100) < change->random_percentage)
10369 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10371 int ex = x + xx - 1;
10372 int ey = y + yy - 1;
10373 int content_element;
10375 if (can_replace[xx][yy] && (!change->use_random_replace ||
10376 RND(100) < change->random_percentage))
10378 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10379 RemoveMovingField(ex, ey);
10381 ChangeEvent[ex][ey] = ChangeEvent[x][y];
10383 content_element = change->target_content.e[xx][yy];
10384 target_element = GET_TARGET_ELEMENT(element, content_element, change,
10385 ce_value, ce_score);
10387 CreateElementFromChange(ex, ey, target_element);
10389 something_has_changed = TRUE;
10391 /* for symmetry reasons, freeze newly created border elements */
10392 if (ex != x || ey != y)
10393 Stop[ex][ey] = TRUE; /* no more moving in this frame */
10397 if (something_has_changed)
10399 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10400 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10406 target_element = GET_TARGET_ELEMENT(element, change->target_element, change,
10407 ce_value, ce_score);
10409 if (element == EL_DIAGONAL_GROWING ||
10410 element == EL_DIAGONAL_SHRINKING)
10412 target_element = Store[x][y];
10414 Store[x][y] = EL_EMPTY;
10417 CreateElementFromChange(x, y, target_element);
10419 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10420 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10423 /* this uses direct change before indirect change */
10424 CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
10429 static void HandleElementChange(int x, int y, int page)
10431 int element = MovingOrBlocked2Element(x, y);
10432 struct ElementInfo *ei = &element_info[element];
10433 struct ElementChangeInfo *change = &ei->change_page[page];
10434 boolean handle_action_before_change = FALSE;
10437 if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
10438 !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
10441 printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
10442 x, y, element, element_info[element].token_name);
10443 printf("HandleElementChange(): This should never happen!\n");
10448 /* this can happen with classic bombs on walkable, changing elements */
10449 if (!CAN_CHANGE_OR_HAS_ACTION(element))
10454 if (ChangeDelay[x][y] == 0) /* initialize element change */
10456 ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
10458 if (change->can_change)
10460 /* !!! not clear why graphic animation should be reset at all here !!! */
10461 /* !!! UPDATE: but is needed for correct Snake Bite tail animation !!! */
10462 /* !!! SOLUTION: do not reset if graphics engine set to 4 or above !!! */
10465 GRAPHICAL BUG ADDRESSED BY CHECKING GRAPHICS ENGINE VERSION:
10467 When using an animation frame delay of 1 (this only happens with
10468 "sp_zonk.moving.left/right" in the classic graphics), the default
10469 (non-moving) animation shows wrong animation frames (while the
10470 moving animation, like "sp_zonk.moving.left/right", is correct,
10471 so this graphical bug never shows up with the classic graphics).
10472 For an animation with 4 frames, this causes wrong frames 0,0,1,2
10473 be drawn instead of the correct frames 0,1,2,3. This is caused by
10474 "GfxFrame[][]" being reset *twice* (in two successive frames) after
10475 an element change: First when the change delay ("ChangeDelay[][]")
10476 counter has reached zero after decrementing, then a second time in
10477 the next frame (after "GfxFrame[][]" was already incremented) when
10478 "ChangeDelay[][]" is reset to the initial delay value again.
10480 This causes frame 0 to be drawn twice, while the last frame won't
10481 be drawn anymore, resulting in the wrong frame sequence 0,0,1,2.
10483 As some animations may already be cleverly designed around this bug
10484 (at least the "Snake Bite" snake tail animation does this), it cannot
10485 simply be fixed here without breaking such existing animations.
10486 Unfortunately, it cannot easily be detected if a graphics set was
10487 designed "before" or "after" the bug was fixed. As a workaround,
10488 a new graphics set option "game.graphics_engine_version" was added
10489 to be able to specify the game's major release version for which the
10490 graphics set was designed, which can then be used to decide if the
10491 bugfix should be used (version 4 and above) or not (version 3 or
10492 below, or if no version was specified at all, as with old sets).
10494 (The wrong/fixed animation frames can be tested with the test level set
10495 "test_gfxframe" and level "000", which contains a specially prepared
10496 custom element at level position (x/y) == (11/9) which uses the zonk
10497 animation mentioned above. Using "game.graphics_engine_version: 4"
10498 fixes the wrong animation frames, showing the correct frames 0,1,2,3.
10499 This can also be seen from the debug output for this test element.)
10502 /* when a custom element is about to change (for example by change delay),
10503 do not reset graphic animation when the custom element is moving */
10504 if (game.graphics_engine_version < 4 &&
10507 ResetGfxAnimation(x, y);
10508 ResetRandomAnimationValue(x, y);
10511 if (change->pre_change_function)
10512 change->pre_change_function(x, y);
10516 ChangeDelay[x][y]--;
10518 if (ChangeDelay[x][y] != 0) /* continue element change */
10520 if (change->can_change)
10522 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
10524 if (IS_ANIMATED(graphic))
10525 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10527 if (change->change_function)
10528 change->change_function(x, y);
10531 else /* finish element change */
10533 if (ChangePage[x][y] != -1) /* remember page from delayed change */
10535 page = ChangePage[x][y];
10536 ChangePage[x][y] = -1;
10538 change = &ei->change_page[page];
10541 if (IS_MOVING(x, y)) /* never change a running system ;-) */
10543 ChangeDelay[x][y] = 1; /* try change after next move step */
10544 ChangePage[x][y] = page; /* remember page to use for change */
10549 /* special case: set new level random seed before changing element */
10550 if (change->has_action && change->action_type == CA_SET_LEVEL_RANDOM_SEED)
10551 handle_action_before_change = TRUE;
10553 if (change->has_action && handle_action_before_change)
10554 ExecuteCustomElementAction(x, y, element, page);
10556 if (change->can_change)
10558 if (ChangeElement(x, y, element, page))
10560 if (change->post_change_function)
10561 change->post_change_function(x, y);
10565 if (change->has_action && !handle_action_before_change)
10566 ExecuteCustomElementAction(x, y, element, page);
10570 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
10571 int trigger_element,
10573 int trigger_player,
10577 boolean change_done_any = FALSE;
10578 int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
10581 if (!(trigger_events[trigger_element][trigger_event]))
10584 RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
10586 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
10588 int element = EL_CUSTOM_START + i;
10589 boolean change_done = FALSE;
10592 if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
10593 !HAS_ANY_CHANGE_EVENT(element, trigger_event))
10596 for (p = 0; p < element_info[element].num_change_pages; p++)
10598 struct ElementChangeInfo *change = &element_info[element].change_page[p];
10600 if (change->can_change_or_has_action &&
10601 change->has_event[trigger_event] &&
10602 change->trigger_side & trigger_side &&
10603 change->trigger_player & trigger_player &&
10604 change->trigger_page & trigger_page_bits &&
10605 IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
10607 change->actual_trigger_element = trigger_element;
10608 change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
10609 change->actual_trigger_player_bits = trigger_player;
10610 change->actual_trigger_side = trigger_side;
10611 change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
10612 change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10614 if ((change->can_change && !change_done) || change->has_action)
10618 SCAN_PLAYFIELD(x, y)
10620 if (Feld[x][y] == element)
10622 if (change->can_change && !change_done)
10624 /* if element already changed in this frame, not only prevent
10625 another element change (checked in ChangeElement()), but
10626 also prevent additional element actions for this element */
10628 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
10629 !level.use_action_after_change_bug)
10632 ChangeDelay[x][y] = 1;
10633 ChangeEvent[x][y] = trigger_event;
10635 HandleElementChange(x, y, p);
10637 else if (change->has_action)
10639 /* if element already changed in this frame, not only prevent
10640 another element change (checked in ChangeElement()), but
10641 also prevent additional element actions for this element */
10643 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
10644 !level.use_action_after_change_bug)
10647 ExecuteCustomElementAction(x, y, element, p);
10648 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
10653 if (change->can_change)
10655 change_done = TRUE;
10656 change_done_any = TRUE;
10663 RECURSION_LOOP_DETECTION_END();
10665 return change_done_any;
10668 static boolean CheckElementChangeExt(int x, int y,
10670 int trigger_element,
10672 int trigger_player,
10675 boolean change_done = FALSE;
10678 if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
10679 !HAS_ANY_CHANGE_EVENT(element, trigger_event))
10682 if (Feld[x][y] == EL_BLOCKED)
10684 Blocked2Moving(x, y, &x, &y);
10685 element = Feld[x][y];
10688 /* check if element has already changed or is about to change after moving */
10689 if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
10690 Feld[x][y] != element) ||
10692 (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
10693 (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
10694 ChangePage[x][y] != -1)))
10697 RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
10699 for (p = 0; p < element_info[element].num_change_pages; p++)
10701 struct ElementChangeInfo *change = &element_info[element].change_page[p];
10703 /* check trigger element for all events where the element that is checked
10704 for changing interacts with a directly adjacent element -- this is
10705 different to element changes that affect other elements to change on the
10706 whole playfield (which is handeld by CheckTriggeredElementChangeExt()) */
10707 boolean check_trigger_element =
10708 (trigger_event == CE_TOUCHING_X ||
10709 trigger_event == CE_HITTING_X ||
10710 trigger_event == CE_HIT_BY_X ||
10711 trigger_event == CE_DIGGING_X); /* this one was forgotten until 3.2.3 */
10713 if (change->can_change_or_has_action &&
10714 change->has_event[trigger_event] &&
10715 change->trigger_side & trigger_side &&
10716 change->trigger_player & trigger_player &&
10717 (!check_trigger_element ||
10718 IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
10720 change->actual_trigger_element = trigger_element;
10721 change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
10722 change->actual_trigger_player_bits = trigger_player;
10723 change->actual_trigger_side = trigger_side;
10724 change->actual_trigger_ce_value = CustomValue[x][y];
10725 change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10727 /* special case: trigger element not at (x,y) position for some events */
10728 if (check_trigger_element)
10740 { 0, 0 }, { 0, 0 }, { 0, 0 },
10744 int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
10745 int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
10747 change->actual_trigger_ce_value = CustomValue[xx][yy];
10748 change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10751 if (change->can_change && !change_done)
10753 ChangeDelay[x][y] = 1;
10754 ChangeEvent[x][y] = trigger_event;
10756 HandleElementChange(x, y, p);
10758 change_done = TRUE;
10760 else if (change->has_action)
10762 ExecuteCustomElementAction(x, y, element, p);
10763 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
10768 RECURSION_LOOP_DETECTION_END();
10770 return change_done;
10773 static void PlayPlayerSound(struct PlayerInfo *player)
10775 int jx = player->jx, jy = player->jy;
10776 int sound_element = player->artwork_element;
10777 int last_action = player->last_action_waiting;
10778 int action = player->action_waiting;
10780 if (player->is_waiting)
10782 if (action != last_action)
10783 PlayLevelSoundElementAction(jx, jy, sound_element, action);
10785 PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
10789 if (action != last_action)
10790 StopSound(element_info[sound_element].sound[last_action]);
10792 if (last_action == ACTION_SLEEPING)
10793 PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
10797 static void PlayAllPlayersSound()
10801 for (i = 0; i < MAX_PLAYERS; i++)
10802 if (stored_player[i].active)
10803 PlayPlayerSound(&stored_player[i]);
10806 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
10808 boolean last_waiting = player->is_waiting;
10809 int move_dir = player->MovDir;
10811 player->dir_waiting = move_dir;
10812 player->last_action_waiting = player->action_waiting;
10816 if (!last_waiting) /* not waiting -> waiting */
10818 player->is_waiting = TRUE;
10820 player->frame_counter_bored =
10822 game.player_boring_delay_fixed +
10823 GetSimpleRandom(game.player_boring_delay_random);
10824 player->frame_counter_sleeping =
10826 game.player_sleeping_delay_fixed +
10827 GetSimpleRandom(game.player_sleeping_delay_random);
10829 InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
10832 if (game.player_sleeping_delay_fixed +
10833 game.player_sleeping_delay_random > 0 &&
10834 player->anim_delay_counter == 0 &&
10835 player->post_delay_counter == 0 &&
10836 FrameCounter >= player->frame_counter_sleeping)
10837 player->is_sleeping = TRUE;
10838 else if (game.player_boring_delay_fixed +
10839 game.player_boring_delay_random > 0 &&
10840 FrameCounter >= player->frame_counter_bored)
10841 player->is_bored = TRUE;
10843 player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
10844 player->is_bored ? ACTION_BORING :
10847 if (player->is_sleeping && player->use_murphy)
10849 /* special case for sleeping Murphy when leaning against non-free tile */
10851 if (!IN_LEV_FIELD(player->jx - 1, player->jy) ||
10852 (Feld[player->jx - 1][player->jy] != EL_EMPTY &&
10853 !IS_MOVING(player->jx - 1, player->jy)))
10854 move_dir = MV_LEFT;
10855 else if (!IN_LEV_FIELD(player->jx + 1, player->jy) ||
10856 (Feld[player->jx + 1][player->jy] != EL_EMPTY &&
10857 !IS_MOVING(player->jx + 1, player->jy)))
10858 move_dir = MV_RIGHT;
10860 player->is_sleeping = FALSE;
10862 player->dir_waiting = move_dir;
10865 if (player->is_sleeping)
10867 if (player->num_special_action_sleeping > 0)
10869 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
10871 int last_special_action = player->special_action_sleeping;
10872 int num_special_action = player->num_special_action_sleeping;
10873 int special_action =
10874 (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
10875 last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
10876 last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
10877 last_special_action + 1 : ACTION_SLEEPING);
10878 int special_graphic =
10879 el_act_dir2img(player->artwork_element, special_action, move_dir);
10881 player->anim_delay_counter =
10882 graphic_info[special_graphic].anim_delay_fixed +
10883 GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
10884 player->post_delay_counter =
10885 graphic_info[special_graphic].post_delay_fixed +
10886 GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
10888 player->special_action_sleeping = special_action;
10891 if (player->anim_delay_counter > 0)
10893 player->action_waiting = player->special_action_sleeping;
10894 player->anim_delay_counter--;
10896 else if (player->post_delay_counter > 0)
10898 player->post_delay_counter--;
10902 else if (player->is_bored)
10904 if (player->num_special_action_bored > 0)
10906 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
10908 int special_action =
10909 ACTION_BORING_1 + GetSimpleRandom(player->num_special_action_bored);
10910 int special_graphic =
10911 el_act_dir2img(player->artwork_element, special_action, move_dir);
10913 player->anim_delay_counter =
10914 graphic_info[special_graphic].anim_delay_fixed +
10915 GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
10916 player->post_delay_counter =
10917 graphic_info[special_graphic].post_delay_fixed +
10918 GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
10920 player->special_action_bored = special_action;
10923 if (player->anim_delay_counter > 0)
10925 player->action_waiting = player->special_action_bored;
10926 player->anim_delay_counter--;
10928 else if (player->post_delay_counter > 0)
10930 player->post_delay_counter--;
10935 else if (last_waiting) /* waiting -> not waiting */
10937 player->is_waiting = FALSE;
10938 player->is_bored = FALSE;
10939 player->is_sleeping = FALSE;
10941 player->frame_counter_bored = -1;
10942 player->frame_counter_sleeping = -1;
10944 player->anim_delay_counter = 0;
10945 player->post_delay_counter = 0;
10947 player->dir_waiting = player->MovDir;
10948 player->action_waiting = ACTION_DEFAULT;
10950 player->special_action_bored = ACTION_DEFAULT;
10951 player->special_action_sleeping = ACTION_DEFAULT;
10955 static void CheckSaveEngineSnapshot(struct PlayerInfo *player)
10957 if ((!player->is_moving && player->was_moving) ||
10958 (player->MovPos == 0 && player->was_moving) ||
10959 (player->is_snapping && !player->was_snapping) ||
10960 (player->is_dropping && !player->was_dropping))
10962 if (!CheckSaveEngineSnapshotToList())
10965 player->was_moving = FALSE;
10966 player->was_snapping = TRUE;
10967 player->was_dropping = TRUE;
10971 if (player->is_moving)
10972 player->was_moving = TRUE;
10974 if (!player->is_snapping)
10975 player->was_snapping = FALSE;
10977 if (!player->is_dropping)
10978 player->was_dropping = FALSE;
10982 static void CheckSingleStepMode(struct PlayerInfo *player)
10984 if (tape.single_step && tape.recording && !tape.pausing)
10986 /* as it is called "single step mode", just return to pause mode when the
10987 player stopped moving after one tile (or never starts moving at all) */
10988 if (!player->is_moving &&
10989 !player->is_pushing &&
10990 !player->is_dropping_pressed)
10992 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
10993 SnapField(player, 0, 0); /* stop snapping */
10997 CheckSaveEngineSnapshot(player);
11000 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
11002 int left = player_action & JOY_LEFT;
11003 int right = player_action & JOY_RIGHT;
11004 int up = player_action & JOY_UP;
11005 int down = player_action & JOY_DOWN;
11006 int button1 = player_action & JOY_BUTTON_1;
11007 int button2 = player_action & JOY_BUTTON_2;
11008 int dx = (left ? -1 : right ? 1 : 0);
11009 int dy = (up ? -1 : down ? 1 : 0);
11011 if (!player->active || tape.pausing)
11017 SnapField(player, dx, dy);
11021 DropElement(player);
11023 MovePlayer(player, dx, dy);
11026 CheckSingleStepMode(player);
11028 SetPlayerWaiting(player, FALSE);
11030 return player_action;
11034 /* no actions for this player (no input at player's configured device) */
11036 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
11037 SnapField(player, 0, 0);
11038 CheckGravityMovementWhenNotMoving(player);
11040 if (player->MovPos == 0)
11041 SetPlayerWaiting(player, TRUE);
11043 if (player->MovPos == 0) /* needed for tape.playing */
11044 player->is_moving = FALSE;
11046 player->is_dropping = FALSE;
11047 player->is_dropping_pressed = FALSE;
11048 player->drop_pressed_delay = 0;
11050 CheckSingleStepMode(player);
11056 static void SetMouseActionFromTapeAction(struct MouseActionInfo *mouse_action,
11059 if (!tape.use_mouse)
11062 mouse_action->lx = tape_action[TAPE_ACTION_LX];
11063 mouse_action->ly = tape_action[TAPE_ACTION_LY];
11064 mouse_action->button = tape_action[TAPE_ACTION_BUTTON];
11067 static void SetTapeActionFromMouseAction(byte *tape_action,
11068 struct MouseActionInfo *mouse_action)
11070 if (!tape.use_mouse)
11073 tape_action[TAPE_ACTION_LX] = mouse_action->lx;
11074 tape_action[TAPE_ACTION_LY] = mouse_action->ly;
11075 tape_action[TAPE_ACTION_BUTTON] = mouse_action->button;
11078 static void CheckLevelTime()
11082 /* !!! SAME CODE AS IN "GameActions()" -- FIX THIS !!! */
11083 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11085 if (level.native_em_level->lev->home == 0) /* all players at home */
11087 PlayerWins(local_player);
11089 AllPlayersGone = TRUE;
11091 level.native_em_level->lev->home = -1;
11094 if (level.native_em_level->ply[0]->alive == 0 &&
11095 level.native_em_level->ply[1]->alive == 0 &&
11096 level.native_em_level->ply[2]->alive == 0 &&
11097 level.native_em_level->ply[3]->alive == 0) /* all dead */
11098 AllPlayersGone = TRUE;
11100 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11102 if (game_sp.LevelSolved &&
11103 !game_sp.GameOver) /* game won */
11105 PlayerWins(local_player);
11107 game_sp.GameOver = TRUE;
11109 AllPlayersGone = TRUE;
11112 if (game_sp.GameOver) /* game lost */
11113 AllPlayersGone = TRUE;
11115 else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
11117 if (game_mm.level_solved &&
11118 !game_mm.game_over) /* game won */
11120 PlayerWins(local_player);
11122 game_mm.game_over = TRUE;
11124 AllPlayersGone = TRUE;
11127 if (game_mm.game_over) /* game lost */
11128 AllPlayersGone = TRUE;
11131 if (TimeFrames >= FRAMES_PER_SECOND)
11136 for (i = 0; i < MAX_PLAYERS; i++)
11138 struct PlayerInfo *player = &stored_player[i];
11140 if (SHIELD_ON(player))
11142 player->shield_normal_time_left--;
11144 if (player->shield_deadly_time_left > 0)
11145 player->shield_deadly_time_left--;
11149 if (!local_player->LevelSolved && !level.use_step_counter)
11157 if (TimeLeft <= 10 && setup.time_limit)
11158 PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
11160 /* this does not make sense: game_panel_controls[GAME_PANEL_TIME].value
11161 is reset from other values in UpdateGameDoorValues() -- FIX THIS */
11163 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
11165 if (!TimeLeft && setup.time_limit)
11167 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11168 level.native_em_level->lev->killed_out_of_time = TRUE;
11170 for (i = 0; i < MAX_PLAYERS; i++)
11171 KillPlayer(&stored_player[i]);
11174 else if (game.no_time_limit && !AllPlayersGone) /* level w/o time limit */
11176 game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
11179 level.native_em_level->lev->time =
11180 (game.no_time_limit ? TimePlayed : TimeLeft);
11183 if (tape.recording || tape.playing)
11184 DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
11187 if (tape.recording || tape.playing)
11188 DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
11190 UpdateAndDisplayGameControlValues();
11193 void AdvanceFrameAndPlayerCounters(int player_nr)
11197 /* advance frame counters (global frame counter and time frame counter) */
11201 /* advance player counters (counters for move delay, move animation etc.) */
11202 for (i = 0; i < MAX_PLAYERS; i++)
11204 boolean advance_player_counters = (player_nr == -1 || player_nr == i);
11205 int move_delay_value = stored_player[i].move_delay_value;
11206 int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
11208 if (!advance_player_counters) /* not all players may be affected */
11211 if (move_frames == 0) /* less than one move per game frame */
11213 int stepsize = TILEX / move_delay_value;
11214 int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
11215 int count = (stored_player[i].is_moving ?
11216 ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
11218 if (count % delay == 0)
11222 stored_player[i].Frame += move_frames;
11224 if (stored_player[i].MovPos != 0)
11225 stored_player[i].StepFrame += move_frames;
11227 if (stored_player[i].move_delay > 0)
11228 stored_player[i].move_delay--;
11230 /* due to bugs in previous versions, counter must count up, not down */
11231 if (stored_player[i].push_delay != -1)
11232 stored_player[i].push_delay++;
11234 if (stored_player[i].drop_delay > 0)
11235 stored_player[i].drop_delay--;
11237 if (stored_player[i].is_dropping_pressed)
11238 stored_player[i].drop_pressed_delay++;
11242 void StartGameActions(boolean init_network_game, boolean record_tape,
11245 unsigned int new_random_seed = InitRND(random_seed);
11248 TapeStartRecording(new_random_seed);
11250 #if defined(NETWORK_AVALIABLE)
11251 if (init_network_game)
11253 SendToServer_StartPlaying();
11262 void GameActionsExt()
11265 static unsigned int game_frame_delay = 0;
11267 unsigned int game_frame_delay_value;
11268 byte *recorded_player_action;
11269 byte summarized_player_action = 0;
11270 byte tape_action[MAX_PLAYERS];
11273 /* detect endless loops, caused by custom element programming */
11274 if (recursion_loop_detected && recursion_loop_depth == 0)
11276 char *message = getStringCat3("Internal Error! Element ",
11277 EL_NAME(recursion_loop_element),
11278 " caused endless loop! Quit the game?");
11280 Error(ERR_WARN, "element '%s' caused endless loop in game engine",
11281 EL_NAME(recursion_loop_element));
11283 RequestQuitGameExt(FALSE, level_editor_test_game, message);
11285 recursion_loop_detected = FALSE; /* if game should be continued */
11292 if (game.restart_level)
11293 StartGameActions(options.network, setup.autorecord, level.random_seed);
11295 /* !!! SAME CODE AS IN "CheckLevelTime()" -- FIX THIS !!! */
11296 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11298 if (level.native_em_level->lev->home == 0) /* all players at home */
11300 PlayerWins(local_player);
11302 AllPlayersGone = TRUE;
11304 level.native_em_level->lev->home = -1;
11307 if (level.native_em_level->ply[0]->alive == 0 &&
11308 level.native_em_level->ply[1]->alive == 0 &&
11309 level.native_em_level->ply[2]->alive == 0 &&
11310 level.native_em_level->ply[3]->alive == 0) /* all dead */
11311 AllPlayersGone = TRUE;
11313 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11315 if (game_sp.LevelSolved &&
11316 !game_sp.GameOver) /* game won */
11318 PlayerWins(local_player);
11320 game_sp.GameOver = TRUE;
11322 AllPlayersGone = TRUE;
11325 if (game_sp.GameOver) /* game lost */
11326 AllPlayersGone = TRUE;
11328 else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
11330 if (game_mm.level_solved &&
11331 !game_mm.game_over) /* game won */
11333 PlayerWins(local_player);
11335 game_mm.game_over = TRUE;
11337 AllPlayersGone = TRUE;
11340 if (game_mm.game_over) /* game lost */
11341 AllPlayersGone = TRUE;
11344 if (local_player->LevelSolved && !local_player->LevelSolved_GameEnd)
11347 if (AllPlayersGone && !TAPE_IS_STOPPED(tape))
11350 if (game_status != GAME_MODE_PLAYING) /* status might have changed */
11353 game_frame_delay_value =
11354 (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
11356 if (tape.playing && tape.warp_forward && !tape.pausing)
11357 game_frame_delay_value = 0;
11359 SetVideoFrameDelay(game_frame_delay_value);
11363 /* ---------- main game synchronization point ---------- */
11365 int skip = WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11367 printf("::: skip == %d\n", skip);
11370 /* ---------- main game synchronization point ---------- */
11372 WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11376 if (network_playing && !network_player_action_received)
11378 /* try to get network player actions in time */
11380 #if defined(NETWORK_AVALIABLE)
11381 /* last chance to get network player actions without main loop delay */
11382 HandleNetworking();
11385 /* game was quit by network peer */
11386 if (game_status != GAME_MODE_PLAYING)
11389 if (!network_player_action_received)
11390 return; /* failed to get network player actions in time */
11392 /* do not yet reset "network_player_action_received" (for tape.pausing) */
11398 /* at this point we know that we really continue executing the game */
11400 network_player_action_received = FALSE;
11402 /* when playing tape, read previously recorded player input from tape data */
11403 recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
11405 local_player->effective_mouse_action = local_player->mouse_action;
11407 if (recorded_player_action != NULL)
11408 SetMouseActionFromTapeAction(&local_player->effective_mouse_action,
11409 recorded_player_action);
11411 /* TapePlayAction() may return NULL when toggling to "pause before death" */
11415 if (tape.set_centered_player)
11417 game.centered_player_nr_next = tape.centered_player_nr_next;
11418 game.set_centered_player = TRUE;
11421 for (i = 0; i < MAX_PLAYERS; i++)
11423 summarized_player_action |= stored_player[i].action;
11425 if (!network_playing && (game.team_mode || tape.playing))
11426 stored_player[i].effective_action = stored_player[i].action;
11429 #if defined(NETWORK_AVALIABLE)
11430 if (network_playing)
11431 SendToServer_MovePlayer(summarized_player_action);
11434 // summarize all actions at local players mapped input device position
11435 // (this allows using different input devices in single player mode)
11436 if (!options.network && !game.team_mode)
11437 stored_player[map_player_action[local_player->index_nr]].effective_action =
11438 summarized_player_action;
11440 if (tape.recording &&
11442 setup.input_on_focus &&
11443 game.centered_player_nr != -1)
11445 for (i = 0; i < MAX_PLAYERS; i++)
11446 stored_player[i].effective_action =
11447 (i == game.centered_player_nr ? summarized_player_action : 0);
11450 if (recorded_player_action != NULL)
11451 for (i = 0; i < MAX_PLAYERS; i++)
11452 stored_player[i].effective_action = recorded_player_action[i];
11454 for (i = 0; i < MAX_PLAYERS; i++)
11456 tape_action[i] = stored_player[i].effective_action;
11458 /* (this may happen in the RND game engine if a player was not present on
11459 the playfield on level start, but appeared later from a custom element */
11460 if (setup.team_mode &&
11463 !tape.player_participates[i])
11464 tape.player_participates[i] = TRUE;
11467 SetTapeActionFromMouseAction(tape_action,
11468 &local_player->effective_mouse_action);
11470 /* only record actions from input devices, but not programmed actions */
11471 if (tape.recording)
11472 TapeRecordAction(tape_action);
11474 #if USE_NEW_PLAYER_ASSIGNMENTS
11475 // !!! also map player actions in single player mode !!!
11476 // if (game.team_mode)
11479 byte mapped_action[MAX_PLAYERS];
11481 #if DEBUG_PLAYER_ACTIONS
11483 for (i = 0; i < MAX_PLAYERS; i++)
11484 printf(" %d, ", stored_player[i].effective_action);
11487 for (i = 0; i < MAX_PLAYERS; i++)
11488 mapped_action[i] = stored_player[map_player_action[i]].effective_action;
11490 for (i = 0; i < MAX_PLAYERS; i++)
11491 stored_player[i].effective_action = mapped_action[i];
11493 #if DEBUG_PLAYER_ACTIONS
11495 for (i = 0; i < MAX_PLAYERS; i++)
11496 printf(" %d, ", stored_player[i].effective_action);
11500 #if DEBUG_PLAYER_ACTIONS
11504 for (i = 0; i < MAX_PLAYERS; i++)
11505 printf(" %d, ", stored_player[i].effective_action);
11511 for (i = 0; i < MAX_PLAYERS; i++)
11513 // allow engine snapshot in case of changed movement attempt
11514 if ((game.snapshot.last_action[i] & KEY_MOTION) !=
11515 (stored_player[i].effective_action & KEY_MOTION))
11516 game.snapshot.changed_action = TRUE;
11518 // allow engine snapshot in case of snapping/dropping attempt
11519 if ((game.snapshot.last_action[i] & KEY_BUTTON) == 0 &&
11520 (stored_player[i].effective_action & KEY_BUTTON) != 0)
11521 game.snapshot.changed_action = TRUE;
11523 game.snapshot.last_action[i] = stored_player[i].effective_action;
11526 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11528 GameActions_EM_Main();
11530 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11532 GameActions_SP_Main();
11534 else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
11536 GameActions_MM_Main();
11540 GameActions_RND_Main();
11543 BlitScreenToBitmap(backbuffer);
11547 AdvanceFrameAndPlayerCounters(-1); /* advance counters for all players */
11549 if (global.show_frames_per_second)
11551 static unsigned int fps_counter = 0;
11552 static int fps_frames = 0;
11553 unsigned int fps_delay_ms = Counter() - fps_counter;
11557 if (fps_delay_ms >= 500) /* calculate FPS every 0.5 seconds */
11559 global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
11562 fps_counter = Counter();
11564 /* always draw FPS to screen after FPS value was updated */
11565 redraw_mask |= REDRAW_FPS;
11568 /* only draw FPS if no screen areas are deactivated (invisible warp mode) */
11569 if (GetDrawDeactivationMask() == REDRAW_NONE)
11570 redraw_mask |= REDRAW_FPS;
11574 static void GameActions_CheckSaveEngineSnapshot()
11576 if (!game.snapshot.save_snapshot)
11579 // clear flag for saving snapshot _before_ saving snapshot
11580 game.snapshot.save_snapshot = FALSE;
11582 SaveEngineSnapshotToList();
11589 GameActions_CheckSaveEngineSnapshot();
11592 void GameActions_EM_Main()
11594 byte effective_action[MAX_PLAYERS];
11595 boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11598 for (i = 0; i < MAX_PLAYERS; i++)
11599 effective_action[i] = stored_player[i].effective_action;
11601 GameActions_EM(effective_action, warp_mode);
11604 void GameActions_SP_Main()
11606 byte effective_action[MAX_PLAYERS];
11607 boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11610 for (i = 0; i < MAX_PLAYERS; i++)
11611 effective_action[i] = stored_player[i].effective_action;
11613 GameActions_SP(effective_action, warp_mode);
11615 for (i = 0; i < MAX_PLAYERS; i++)
11617 if (stored_player[i].force_dropping)
11618 stored_player[i].action |= KEY_BUTTON_DROP;
11620 stored_player[i].force_dropping = FALSE;
11624 void GameActions_MM_Main()
11626 boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11628 GameActions_MM(local_player->effective_mouse_action, warp_mode);
11631 void GameActions_RND_Main()
11636 void GameActions_RND()
11638 int magic_wall_x = 0, magic_wall_y = 0;
11639 int i, x, y, element, graphic, last_gfx_frame;
11641 InitPlayfieldScanModeVars();
11643 if (game.engine_version >= VERSION_IDENT(3,2,0,7))
11645 SCAN_PLAYFIELD(x, y)
11647 ChangeCount[x][y] = 0;
11648 ChangeEvent[x][y] = -1;
11652 if (game.set_centered_player)
11654 boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
11656 /* switching to "all players" only possible if all players fit to screen */
11657 if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
11659 game.centered_player_nr_next = game.centered_player_nr;
11660 game.set_centered_player = FALSE;
11663 /* do not switch focus to non-existing (or non-active) player */
11664 if (game.centered_player_nr_next >= 0 &&
11665 !stored_player[game.centered_player_nr_next].active)
11667 game.centered_player_nr_next = game.centered_player_nr;
11668 game.set_centered_player = FALSE;
11672 if (game.set_centered_player &&
11673 ScreenMovPos == 0) /* screen currently aligned at tile position */
11677 if (game.centered_player_nr_next == -1)
11679 setScreenCenteredToAllPlayers(&sx, &sy);
11683 sx = stored_player[game.centered_player_nr_next].jx;
11684 sy = stored_player[game.centered_player_nr_next].jy;
11687 game.centered_player_nr = game.centered_player_nr_next;
11688 game.set_centered_player = FALSE;
11690 DrawRelocateScreen(0, 0, sx, sy, MV_NONE, TRUE, setup.quick_switch);
11691 DrawGameDoorValues();
11694 for (i = 0; i < MAX_PLAYERS; i++)
11696 int actual_player_action = stored_player[i].effective_action;
11699 /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
11700 - rnd_equinox_tetrachloride 048
11701 - rnd_equinox_tetrachloride_ii 096
11702 - rnd_emanuel_schmieg 002
11703 - doctor_sloan_ww 001, 020
11705 if (stored_player[i].MovPos == 0)
11706 CheckGravityMovement(&stored_player[i]);
11709 /* overwrite programmed action with tape action */
11710 if (stored_player[i].programmed_action)
11711 actual_player_action = stored_player[i].programmed_action;
11713 PlayerActions(&stored_player[i], actual_player_action);
11715 ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
11718 ScrollScreen(NULL, SCROLL_GO_ON);
11720 /* for backwards compatibility, the following code emulates a fixed bug that
11721 occured when pushing elements (causing elements that just made their last
11722 pushing step to already (if possible) make their first falling step in the
11723 same game frame, which is bad); this code is also needed to use the famous
11724 "spring push bug" which is used in older levels and might be wanted to be
11725 used also in newer levels, but in this case the buggy pushing code is only
11726 affecting the "spring" element and no other elements */
11728 if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
11730 for (i = 0; i < MAX_PLAYERS; i++)
11732 struct PlayerInfo *player = &stored_player[i];
11733 int x = player->jx;
11734 int y = player->jy;
11736 if (player->active && player->is_pushing && player->is_moving &&
11738 (game.engine_version < VERSION_IDENT(2,2,0,7) ||
11739 Feld[x][y] == EL_SPRING))
11741 ContinueMoving(x, y);
11743 /* continue moving after pushing (this is actually a bug) */
11744 if (!IS_MOVING(x, y))
11745 Stop[x][y] = FALSE;
11750 SCAN_PLAYFIELD(x, y)
11752 ChangeCount[x][y] = 0;
11753 ChangeEvent[x][y] = -1;
11755 /* this must be handled before main playfield loop */
11756 if (Feld[x][y] == EL_PLAYER_IS_LEAVING)
11759 if (MovDelay[x][y] <= 0)
11763 if (Feld[x][y] == EL_ELEMENT_SNAPPING)
11766 if (MovDelay[x][y] <= 0)
11769 TEST_DrawLevelField(x, y);
11771 TestIfElementTouchesCustomElement(x, y); /* for empty space */
11776 if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
11778 printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
11779 printf("GameActions(): This should never happen!\n");
11781 ChangePage[x][y] = -1;
11785 Stop[x][y] = FALSE;
11786 if (WasJustMoving[x][y] > 0)
11787 WasJustMoving[x][y]--;
11788 if (WasJustFalling[x][y] > 0)
11789 WasJustFalling[x][y]--;
11790 if (CheckCollision[x][y] > 0)
11791 CheckCollision[x][y]--;
11792 if (CheckImpact[x][y] > 0)
11793 CheckImpact[x][y]--;
11797 /* reset finished pushing action (not done in ContinueMoving() to allow
11798 continuous pushing animation for elements with zero push delay) */
11799 if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
11801 ResetGfxAnimation(x, y);
11802 TEST_DrawLevelField(x, y);
11806 if (IS_BLOCKED(x, y))
11810 Blocked2Moving(x, y, &oldx, &oldy);
11811 if (!IS_MOVING(oldx, oldy))
11813 printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
11814 printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
11815 printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
11816 printf("GameActions(): This should never happen!\n");
11822 SCAN_PLAYFIELD(x, y)
11824 element = Feld[x][y];
11825 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11826 last_gfx_frame = GfxFrame[x][y];
11828 ResetGfxFrame(x, y);
11830 if (GfxFrame[x][y] != last_gfx_frame && !Stop[x][y])
11831 DrawLevelGraphicAnimation(x, y, graphic);
11833 if (ANIM_MODE(graphic) == ANIM_RANDOM &&
11834 IS_NEXT_FRAME(GfxFrame[x][y], graphic))
11835 ResetRandomAnimationValue(x, y);
11837 SetRandomAnimationValue(x, y);
11839 PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
11841 if (IS_INACTIVE(element))
11843 if (IS_ANIMATED(graphic))
11844 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11849 /* this may take place after moving, so 'element' may have changed */
11850 if (IS_CHANGING(x, y) &&
11851 (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
11853 int page = element_info[element].event_page_nr[CE_DELAY];
11855 HandleElementChange(x, y, page);
11857 element = Feld[x][y];
11858 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11861 if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
11865 element = Feld[x][y];
11866 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11868 if (IS_ANIMATED(graphic) &&
11869 !IS_MOVING(x, y) &&
11871 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11873 if (IS_GEM(element) || element == EL_SP_INFOTRON)
11874 TEST_DrawTwinkleOnField(x, y);
11876 else if (element == EL_ACID)
11879 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11881 else if ((element == EL_EXIT_OPEN ||
11882 element == EL_EM_EXIT_OPEN ||
11883 element == EL_SP_EXIT_OPEN ||
11884 element == EL_STEEL_EXIT_OPEN ||
11885 element == EL_EM_STEEL_EXIT_OPEN ||
11886 element == EL_SP_TERMINAL ||
11887 element == EL_SP_TERMINAL_ACTIVE ||
11888 element == EL_EXTRA_TIME ||
11889 element == EL_SHIELD_NORMAL ||
11890 element == EL_SHIELD_DEADLY) &&
11891 IS_ANIMATED(graphic))
11892 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11893 else if (IS_MOVING(x, y))
11894 ContinueMoving(x, y);
11895 else if (IS_ACTIVE_BOMB(element))
11896 CheckDynamite(x, y);
11897 else if (element == EL_AMOEBA_GROWING)
11898 AmoebeWaechst(x, y);
11899 else if (element == EL_AMOEBA_SHRINKING)
11900 AmoebaDisappearing(x, y);
11902 #if !USE_NEW_AMOEBA_CODE
11903 else if (IS_AMOEBALIVE(element))
11904 AmoebeAbleger(x, y);
11907 else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
11909 else if (element == EL_EXIT_CLOSED)
11911 else if (element == EL_EM_EXIT_CLOSED)
11913 else if (element == EL_STEEL_EXIT_CLOSED)
11914 CheckExitSteel(x, y);
11915 else if (element == EL_EM_STEEL_EXIT_CLOSED)
11916 CheckExitSteelEM(x, y);
11917 else if (element == EL_SP_EXIT_CLOSED)
11919 else if (element == EL_EXPANDABLE_WALL_GROWING ||
11920 element == EL_EXPANDABLE_STEELWALL_GROWING)
11921 MauerWaechst(x, y);
11922 else if (element == EL_EXPANDABLE_WALL ||
11923 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
11924 element == EL_EXPANDABLE_WALL_VERTICAL ||
11925 element == EL_EXPANDABLE_WALL_ANY ||
11926 element == EL_BD_EXPANDABLE_WALL)
11927 MauerAbleger(x, y);
11928 else if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
11929 element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
11930 element == EL_EXPANDABLE_STEELWALL_ANY)
11931 MauerAblegerStahl(x, y);
11932 else if (element == EL_FLAMES)
11933 CheckForDragon(x, y);
11934 else if (element == EL_EXPLOSION)
11935 ; /* drawing of correct explosion animation is handled separately */
11936 else if (element == EL_ELEMENT_SNAPPING ||
11937 element == EL_DIAGONAL_SHRINKING ||
11938 element == EL_DIAGONAL_GROWING)
11940 graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
11942 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11944 else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
11945 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11947 if (IS_BELT_ACTIVE(element))
11948 PlayLevelSoundAction(x, y, ACTION_ACTIVE);
11950 if (game.magic_wall_active)
11952 int jx = local_player->jx, jy = local_player->jy;
11954 /* play the element sound at the position nearest to the player */
11955 if ((element == EL_MAGIC_WALL_FULL ||
11956 element == EL_MAGIC_WALL_ACTIVE ||
11957 element == EL_MAGIC_WALL_EMPTYING ||
11958 element == EL_BD_MAGIC_WALL_FULL ||
11959 element == EL_BD_MAGIC_WALL_ACTIVE ||
11960 element == EL_BD_MAGIC_WALL_EMPTYING ||
11961 element == EL_DC_MAGIC_WALL_FULL ||
11962 element == EL_DC_MAGIC_WALL_ACTIVE ||
11963 element == EL_DC_MAGIC_WALL_EMPTYING) &&
11964 ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
11972 #if USE_NEW_AMOEBA_CODE
11973 /* new experimental amoeba growth stuff */
11974 if (!(FrameCounter % 8))
11976 static unsigned int random = 1684108901;
11978 for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
11980 x = RND(lev_fieldx);
11981 y = RND(lev_fieldy);
11982 element = Feld[x][y];
11984 if (!IS_PLAYER(x,y) &&
11985 (element == EL_EMPTY ||
11986 CAN_GROW_INTO(element) ||
11987 element == EL_QUICKSAND_EMPTY ||
11988 element == EL_QUICKSAND_FAST_EMPTY ||
11989 element == EL_ACID_SPLASH_LEFT ||
11990 element == EL_ACID_SPLASH_RIGHT))
11992 if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
11993 (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
11994 (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
11995 (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
11996 Feld[x][y] = EL_AMOEBA_DROP;
11999 random = random * 129 + 1;
12004 game.explosions_delayed = FALSE;
12006 SCAN_PLAYFIELD(x, y)
12008 element = Feld[x][y];
12010 if (ExplodeField[x][y])
12011 Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
12012 else if (element == EL_EXPLOSION)
12013 Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
12015 ExplodeField[x][y] = EX_TYPE_NONE;
12018 game.explosions_delayed = TRUE;
12020 if (game.magic_wall_active)
12022 if (!(game.magic_wall_time_left % 4))
12024 int element = Feld[magic_wall_x][magic_wall_y];
12026 if (element == EL_BD_MAGIC_WALL_FULL ||
12027 element == EL_BD_MAGIC_WALL_ACTIVE ||
12028 element == EL_BD_MAGIC_WALL_EMPTYING)
12029 PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
12030 else if (element == EL_DC_MAGIC_WALL_FULL ||
12031 element == EL_DC_MAGIC_WALL_ACTIVE ||
12032 element == EL_DC_MAGIC_WALL_EMPTYING)
12033 PlayLevelSound(magic_wall_x, magic_wall_y, SND_DC_MAGIC_WALL_ACTIVE);
12035 PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
12038 if (game.magic_wall_time_left > 0)
12040 game.magic_wall_time_left--;
12042 if (!game.magic_wall_time_left)
12044 SCAN_PLAYFIELD(x, y)
12046 element = Feld[x][y];
12048 if (element == EL_MAGIC_WALL_ACTIVE ||
12049 element == EL_MAGIC_WALL_FULL)
12051 Feld[x][y] = EL_MAGIC_WALL_DEAD;
12052 TEST_DrawLevelField(x, y);
12054 else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
12055 element == EL_BD_MAGIC_WALL_FULL)
12057 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
12058 TEST_DrawLevelField(x, y);
12060 else if (element == EL_DC_MAGIC_WALL_ACTIVE ||
12061 element == EL_DC_MAGIC_WALL_FULL)
12063 Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
12064 TEST_DrawLevelField(x, y);
12068 game.magic_wall_active = FALSE;
12073 if (game.light_time_left > 0)
12075 game.light_time_left--;
12077 if (game.light_time_left == 0)
12078 RedrawAllLightSwitchesAndInvisibleElements();
12081 if (game.timegate_time_left > 0)
12083 game.timegate_time_left--;
12085 if (game.timegate_time_left == 0)
12086 CloseAllOpenTimegates();
12089 if (game.lenses_time_left > 0)
12091 game.lenses_time_left--;
12093 if (game.lenses_time_left == 0)
12094 RedrawAllInvisibleElementsForLenses();
12097 if (game.magnify_time_left > 0)
12099 game.magnify_time_left--;
12101 if (game.magnify_time_left == 0)
12102 RedrawAllInvisibleElementsForMagnifier();
12105 for (i = 0; i < MAX_PLAYERS; i++)
12107 struct PlayerInfo *player = &stored_player[i];
12109 if (SHIELD_ON(player))
12111 if (player->shield_deadly_time_left)
12112 PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
12113 else if (player->shield_normal_time_left)
12114 PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
12118 #if USE_DELAYED_GFX_REDRAW
12119 SCAN_PLAYFIELD(x, y)
12121 if (GfxRedraw[x][y] != GFX_REDRAW_NONE)
12123 /* !!! PROBLEM: THIS REDRAWS THE PLAYFIELD _AFTER_ THE SCAN, BUT TILES
12124 !!! MAY HAVE CHANGED AFTER BEING DRAWN DURING PLAYFIELD SCAN !!! */
12126 if (GfxRedraw[x][y] & GFX_REDRAW_TILE)
12127 DrawLevelField(x, y);
12129 if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED)
12130 DrawLevelFieldCrumbled(x, y);
12132 if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS)
12133 DrawLevelFieldCrumbledNeighbours(x, y);
12135 if (GfxRedraw[x][y] & GFX_REDRAW_TILE_TWINKLED)
12136 DrawTwinkleOnField(x, y);
12139 GfxRedraw[x][y] = GFX_REDRAW_NONE;
12144 PlayAllPlayersSound();
12146 if (local_player->show_envelope != 0 && local_player->MovPos == 0)
12148 ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
12150 local_player->show_envelope = 0;
12153 /* use random number generator in every frame to make it less predictable */
12154 if (game.engine_version >= VERSION_IDENT(3,1,1,0))
12158 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
12160 int min_x = x, min_y = y, max_x = x, max_y = y;
12163 for (i = 0; i < MAX_PLAYERS; i++)
12165 int jx = stored_player[i].jx, jy = stored_player[i].jy;
12167 if (!stored_player[i].active || &stored_player[i] == player)
12170 min_x = MIN(min_x, jx);
12171 min_y = MIN(min_y, jy);
12172 max_x = MAX(max_x, jx);
12173 max_y = MAX(max_y, jy);
12176 return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
12179 static boolean AllPlayersInVisibleScreen()
12183 for (i = 0; i < MAX_PLAYERS; i++)
12185 int jx = stored_player[i].jx, jy = stored_player[i].jy;
12187 if (!stored_player[i].active)
12190 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12197 void ScrollLevel(int dx, int dy)
12199 int scroll_offset = 2 * TILEX_VAR;
12202 BlitBitmap(drawto_field, drawto_field,
12203 FX + TILEX_VAR * (dx == -1) - scroll_offset,
12204 FY + TILEY_VAR * (dy == -1) - scroll_offset,
12205 SXSIZE - TILEX_VAR * (dx != 0) + 2 * scroll_offset,
12206 SYSIZE - TILEY_VAR * (dy != 0) + 2 * scroll_offset,
12207 FX + TILEX_VAR * (dx == 1) - scroll_offset,
12208 FY + TILEY_VAR * (dy == 1) - scroll_offset);
12212 x = (dx == 1 ? BX1 : BX2);
12213 for (y = BY1; y <= BY2; y++)
12214 DrawScreenField(x, y);
12219 y = (dy == 1 ? BY1 : BY2);
12220 for (x = BX1; x <= BX2; x++)
12221 DrawScreenField(x, y);
12224 redraw_mask |= REDRAW_FIELD;
12227 static boolean canFallDown(struct PlayerInfo *player)
12229 int jx = player->jx, jy = player->jy;
12231 return (IN_LEV_FIELD(jx, jy + 1) &&
12232 (IS_FREE(jx, jy + 1) ||
12233 (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
12234 IS_WALKABLE_FROM(Feld[jx][jy], MV_DOWN) &&
12235 !IS_WALKABLE_INSIDE(Feld[jx][jy]));
12238 static boolean canPassField(int x, int y, int move_dir)
12240 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
12241 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
12242 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
12243 int nextx = x + dx;
12244 int nexty = y + dy;
12245 int element = Feld[x][y];
12247 return (IS_PASSABLE_FROM(element, opposite_dir) &&
12248 !CAN_MOVE(element) &&
12249 IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
12250 IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
12251 (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
12254 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
12256 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
12257 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
12258 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
12262 return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
12263 IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
12264 (IS_DIGGABLE(Feld[newx][newy]) ||
12265 IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
12266 canPassField(newx, newy, move_dir)));
12269 static void CheckGravityMovement(struct PlayerInfo *player)
12271 if (player->gravity && !player->programmed_action)
12273 int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
12274 int move_dir_vertical = player->effective_action & MV_VERTICAL;
12275 boolean player_is_snapping = (player->effective_action & JOY_BUTTON_1);
12276 int jx = player->jx, jy = player->jy;
12277 boolean player_is_moving_to_valid_field =
12278 (!player_is_snapping &&
12279 (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
12280 canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
12281 boolean player_can_fall_down = canFallDown(player);
12283 if (player_can_fall_down &&
12284 !player_is_moving_to_valid_field)
12285 player->programmed_action = MV_DOWN;
12289 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
12291 return CheckGravityMovement(player);
12293 if (player->gravity && !player->programmed_action)
12295 int jx = player->jx, jy = player->jy;
12296 boolean field_under_player_is_free =
12297 (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
12298 boolean player_is_standing_on_valid_field =
12299 (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
12300 (IS_WALKABLE(Feld[jx][jy]) &&
12301 !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
12303 if (field_under_player_is_free && !player_is_standing_on_valid_field)
12304 player->programmed_action = MV_DOWN;
12309 MovePlayerOneStep()
12310 -----------------------------------------------------------------------------
12311 dx, dy: direction (non-diagonal) to try to move the player to
12312 real_dx, real_dy: direction as read from input device (can be diagonal)
12315 boolean MovePlayerOneStep(struct PlayerInfo *player,
12316 int dx, int dy, int real_dx, int real_dy)
12318 int jx = player->jx, jy = player->jy;
12319 int new_jx = jx + dx, new_jy = jy + dy;
12321 boolean player_can_move = !player->cannot_move;
12323 if (!player->active || (!dx && !dy))
12324 return MP_NO_ACTION;
12326 player->MovDir = (dx < 0 ? MV_LEFT :
12327 dx > 0 ? MV_RIGHT :
12329 dy > 0 ? MV_DOWN : MV_NONE);
12331 if (!IN_LEV_FIELD(new_jx, new_jy))
12332 return MP_NO_ACTION;
12334 if (!player_can_move)
12336 if (player->MovPos == 0)
12338 player->is_moving = FALSE;
12339 player->is_digging = FALSE;
12340 player->is_collecting = FALSE;
12341 player->is_snapping = FALSE;
12342 player->is_pushing = FALSE;
12346 if (!options.network && game.centered_player_nr == -1 &&
12347 !AllPlayersInSight(player, new_jx, new_jy))
12348 return MP_NO_ACTION;
12350 can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
12351 if (can_move != MP_MOVING)
12354 /* check if DigField() has caused relocation of the player */
12355 if (player->jx != jx || player->jy != jy)
12356 return MP_NO_ACTION; /* <-- !!! CHECK THIS [-> MP_ACTION ?] !!! */
12358 StorePlayer[jx][jy] = 0;
12359 player->last_jx = jx;
12360 player->last_jy = jy;
12361 player->jx = new_jx;
12362 player->jy = new_jy;
12363 StorePlayer[new_jx][new_jy] = player->element_nr;
12365 if (player->move_delay_value_next != -1)
12367 player->move_delay_value = player->move_delay_value_next;
12368 player->move_delay_value_next = -1;
12372 (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
12374 player->step_counter++;
12376 PlayerVisit[jx][jy] = FrameCounter;
12378 player->is_moving = TRUE;
12381 /* should better be called in MovePlayer(), but this breaks some tapes */
12382 ScrollPlayer(player, SCROLL_INIT);
12388 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
12390 int jx = player->jx, jy = player->jy;
12391 int old_jx = jx, old_jy = jy;
12392 int moved = MP_NO_ACTION;
12394 if (!player->active)
12399 if (player->MovPos == 0)
12401 player->is_moving = FALSE;
12402 player->is_digging = FALSE;
12403 player->is_collecting = FALSE;
12404 player->is_snapping = FALSE;
12405 player->is_pushing = FALSE;
12411 if (player->move_delay > 0)
12414 player->move_delay = -1; /* set to "uninitialized" value */
12416 /* store if player is automatically moved to next field */
12417 player->is_auto_moving = (player->programmed_action != MV_NONE);
12419 /* remove the last programmed player action */
12420 player->programmed_action = 0;
12422 if (player->MovPos)
12424 /* should only happen if pre-1.2 tape recordings are played */
12425 /* this is only for backward compatibility */
12427 int original_move_delay_value = player->move_delay_value;
12430 printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%d]\n",
12434 /* scroll remaining steps with finest movement resolution */
12435 player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
12437 while (player->MovPos)
12439 ScrollPlayer(player, SCROLL_GO_ON);
12440 ScrollScreen(NULL, SCROLL_GO_ON);
12442 AdvanceFrameAndPlayerCounters(player->index_nr);
12445 BackToFront_WithFrameDelay(0);
12448 player->move_delay_value = original_move_delay_value;
12451 player->is_active = FALSE;
12453 if (player->last_move_dir & MV_HORIZONTAL)
12455 if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
12456 moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
12460 if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
12461 moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
12464 if (!moved && !player->is_active)
12466 player->is_moving = FALSE;
12467 player->is_digging = FALSE;
12468 player->is_collecting = FALSE;
12469 player->is_snapping = FALSE;
12470 player->is_pushing = FALSE;
12476 if (moved & MP_MOVING && !ScreenMovPos &&
12477 (player->index_nr == game.centered_player_nr ||
12478 game.centered_player_nr == -1))
12480 int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
12481 int offset = game.scroll_delay_value;
12483 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12485 /* actual player has left the screen -- scroll in that direction */
12486 if (jx != old_jx) /* player has moved horizontally */
12487 scroll_x += (jx - old_jx);
12488 else /* player has moved vertically */
12489 scroll_y += (jy - old_jy);
12493 if (jx != old_jx) /* player has moved horizontally */
12495 if ((player->MovDir == MV_LEFT && scroll_x > jx - MIDPOSX + offset) ||
12496 (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
12497 scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
12499 /* don't scroll over playfield boundaries */
12500 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
12501 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
12503 /* don't scroll more than one field at a time */
12504 scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
12506 /* don't scroll against the player's moving direction */
12507 if ((player->MovDir == MV_LEFT && scroll_x > old_scroll_x) ||
12508 (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
12509 scroll_x = old_scroll_x;
12511 else /* player has moved vertically */
12513 if ((player->MovDir == MV_UP && scroll_y > jy - MIDPOSY + offset) ||
12514 (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
12515 scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
12517 /* don't scroll over playfield boundaries */
12518 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
12519 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
12521 /* don't scroll more than one field at a time */
12522 scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
12524 /* don't scroll against the player's moving direction */
12525 if ((player->MovDir == MV_UP && scroll_y > old_scroll_y) ||
12526 (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
12527 scroll_y = old_scroll_y;
12531 if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
12533 if (!options.network && game.centered_player_nr == -1 &&
12534 !AllPlayersInVisibleScreen())
12536 scroll_x = old_scroll_x;
12537 scroll_y = old_scroll_y;
12541 ScrollScreen(player, SCROLL_INIT);
12542 ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
12547 player->StepFrame = 0;
12549 if (moved & MP_MOVING)
12551 if (old_jx != jx && old_jy == jy)
12552 player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
12553 else if (old_jx == jx && old_jy != jy)
12554 player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
12556 TEST_DrawLevelField(jx, jy); /* for "crumbled sand" */
12558 player->last_move_dir = player->MovDir;
12559 player->is_moving = TRUE;
12560 player->is_snapping = FALSE;
12561 player->is_switching = FALSE;
12562 player->is_dropping = FALSE;
12563 player->is_dropping_pressed = FALSE;
12564 player->drop_pressed_delay = 0;
12567 /* should better be called here than above, but this breaks some tapes */
12568 ScrollPlayer(player, SCROLL_INIT);
12573 CheckGravityMovementWhenNotMoving(player);
12575 player->is_moving = FALSE;
12577 /* at this point, the player is allowed to move, but cannot move right now
12578 (e.g. because of something blocking the way) -- ensure that the player
12579 is also allowed to move in the next frame (in old versions before 3.1.1,
12580 the player was forced to wait again for eight frames before next try) */
12582 if (game.engine_version >= VERSION_IDENT(3,1,1,0))
12583 player->move_delay = 0; /* allow direct movement in the next frame */
12586 if (player->move_delay == -1) /* not yet initialized by DigField() */
12587 player->move_delay = player->move_delay_value;
12589 if (game.engine_version < VERSION_IDENT(3,0,7,0))
12591 TestIfPlayerTouchesBadThing(jx, jy);
12592 TestIfPlayerTouchesCustomElement(jx, jy);
12595 if (!player->active)
12596 RemovePlayer(player);
12601 void ScrollPlayer(struct PlayerInfo *player, int mode)
12603 int jx = player->jx, jy = player->jy;
12604 int last_jx = player->last_jx, last_jy = player->last_jy;
12605 int move_stepsize = TILEX / player->move_delay_value;
12607 if (!player->active)
12610 if (player->MovPos == 0 && mode == SCROLL_GO_ON) /* player not moving */
12613 if (mode == SCROLL_INIT)
12615 player->actual_frame_counter = FrameCounter;
12616 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
12618 if ((player->block_last_field || player->block_delay_adjustment > 0) &&
12619 Feld[last_jx][last_jy] == EL_EMPTY)
12621 int last_field_block_delay = 0; /* start with no blocking at all */
12622 int block_delay_adjustment = player->block_delay_adjustment;
12624 /* if player blocks last field, add delay for exactly one move */
12625 if (player->block_last_field)
12627 last_field_block_delay += player->move_delay_value;
12629 /* when blocking enabled, prevent moving up despite gravity */
12630 if (player->gravity && player->MovDir == MV_UP)
12631 block_delay_adjustment = -1;
12634 /* add block delay adjustment (also possible when not blocking) */
12635 last_field_block_delay += block_delay_adjustment;
12637 Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
12638 MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
12641 if (player->MovPos != 0) /* player has not yet reached destination */
12644 else if (!FrameReached(&player->actual_frame_counter, 1))
12647 if (player->MovPos != 0)
12649 player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
12650 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
12652 /* before DrawPlayer() to draw correct player graphic for this case */
12653 if (player->MovPos == 0)
12654 CheckGravityMovement(player);
12657 if (player->MovPos == 0) /* player reached destination field */
12659 if (player->move_delay_reset_counter > 0)
12661 player->move_delay_reset_counter--;
12663 if (player->move_delay_reset_counter == 0)
12665 /* continue with normal speed after quickly moving through gate */
12666 HALVE_PLAYER_SPEED(player);
12668 /* be able to make the next move without delay */
12669 player->move_delay = 0;
12673 player->last_jx = jx;
12674 player->last_jy = jy;
12676 if (Feld[jx][jy] == EL_EXIT_OPEN ||
12677 Feld[jx][jy] == EL_EM_EXIT_OPEN ||
12678 Feld[jx][jy] == EL_EM_EXIT_OPENING ||
12679 Feld[jx][jy] == EL_STEEL_EXIT_OPEN ||
12680 Feld[jx][jy] == EL_EM_STEEL_EXIT_OPEN ||
12681 Feld[jx][jy] == EL_EM_STEEL_EXIT_OPENING ||
12682 Feld[jx][jy] == EL_SP_EXIT_OPEN ||
12683 Feld[jx][jy] == EL_SP_EXIT_OPENING) /* <-- special case */
12685 DrawPlayer(player); /* needed here only to cleanup last field */
12686 RemovePlayer(player);
12688 if (local_player->friends_still_needed == 0 ||
12689 IS_SP_ELEMENT(Feld[jx][jy]))
12690 PlayerWins(player);
12693 /* this breaks one level: "machine", level 000 */
12695 int move_direction = player->MovDir;
12696 int enter_side = MV_DIR_OPPOSITE(move_direction);
12697 int leave_side = move_direction;
12698 int old_jx = last_jx;
12699 int old_jy = last_jy;
12700 int old_element = Feld[old_jx][old_jy];
12701 int new_element = Feld[jx][jy];
12703 if (IS_CUSTOM_ELEMENT(old_element))
12704 CheckElementChangeByPlayer(old_jx, old_jy, old_element,
12706 player->index_bit, leave_side);
12708 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
12709 CE_PLAYER_LEAVES_X,
12710 player->index_bit, leave_side);
12712 if (IS_CUSTOM_ELEMENT(new_element))
12713 CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
12714 player->index_bit, enter_side);
12716 CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
12717 CE_PLAYER_ENTERS_X,
12718 player->index_bit, enter_side);
12720 CheckTriggeredElementChangeBySide(jx, jy, player->initial_element,
12721 CE_MOVE_OF_X, move_direction);
12724 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
12726 TestIfPlayerTouchesBadThing(jx, jy);
12727 TestIfPlayerTouchesCustomElement(jx, jy);
12729 /* needed because pushed element has not yet reached its destination,
12730 so it would trigger a change event at its previous field location */
12731 if (!player->is_pushing)
12732 TestIfElementTouchesCustomElement(jx, jy); /* for empty space */
12734 if (!player->active)
12735 RemovePlayer(player);
12738 if (!local_player->LevelSolved && level.use_step_counter)
12748 if (TimeLeft <= 10 && setup.time_limit)
12749 PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
12751 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
12753 DisplayGameControlValues();
12755 if (!TimeLeft && setup.time_limit)
12756 for (i = 0; i < MAX_PLAYERS; i++)
12757 KillPlayer(&stored_player[i]);
12759 else if (game.no_time_limit && !AllPlayersGone) /* level w/o time limit */
12761 game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
12763 DisplayGameControlValues();
12767 if (tape.single_step && tape.recording && !tape.pausing &&
12768 !player->programmed_action)
12769 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
12771 if (!player->programmed_action)
12772 CheckSaveEngineSnapshot(player);
12776 void ScrollScreen(struct PlayerInfo *player, int mode)
12778 static unsigned int screen_frame_counter = 0;
12780 if (mode == SCROLL_INIT)
12782 /* set scrolling step size according to actual player's moving speed */
12783 ScrollStepSize = TILEX / player->move_delay_value;
12785 screen_frame_counter = FrameCounter;
12786 ScreenMovDir = player->MovDir;
12787 ScreenMovPos = player->MovPos;
12788 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
12791 else if (!FrameReached(&screen_frame_counter, 1))
12796 ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
12797 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
12798 redraw_mask |= REDRAW_FIELD;
12801 ScreenMovDir = MV_NONE;
12804 void TestIfPlayerTouchesCustomElement(int x, int y)
12806 static int xy[4][2] =
12813 static int trigger_sides[4][2] =
12815 /* center side border side */
12816 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
12817 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
12818 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
12819 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
12821 static int touch_dir[4] =
12823 MV_LEFT | MV_RIGHT,
12828 int center_element = Feld[x][y]; /* should always be non-moving! */
12831 for (i = 0; i < NUM_DIRECTIONS; i++)
12833 int xx = x + xy[i][0];
12834 int yy = y + xy[i][1];
12835 int center_side = trigger_sides[i][0];
12836 int border_side = trigger_sides[i][1];
12837 int border_element;
12839 if (!IN_LEV_FIELD(xx, yy))
12842 if (IS_PLAYER(x, y)) /* player found at center element */
12844 struct PlayerInfo *player = PLAYERINFO(x, y);
12846 if (game.engine_version < VERSION_IDENT(3,0,7,0))
12847 border_element = Feld[xx][yy]; /* may be moving! */
12848 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
12849 border_element = Feld[xx][yy];
12850 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
12851 border_element = MovingOrBlocked2Element(xx, yy);
12853 continue; /* center and border element do not touch */
12855 CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
12856 player->index_bit, border_side);
12857 CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
12858 CE_PLAYER_TOUCHES_X,
12859 player->index_bit, border_side);
12862 /* use player element that is initially defined in the level playfield,
12863 not the player element that corresponds to the runtime player number
12864 (example: a level that contains EL_PLAYER_3 as the only player would
12865 incorrectly give EL_PLAYER_1 for "player->element_nr") */
12866 int player_element = PLAYERINFO(x, y)->initial_element;
12868 CheckElementChangeBySide(xx, yy, border_element, player_element,
12869 CE_TOUCHING_X, border_side);
12872 else if (IS_PLAYER(xx, yy)) /* player found at border element */
12874 struct PlayerInfo *player = PLAYERINFO(xx, yy);
12876 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
12878 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
12879 continue; /* center and border element do not touch */
12882 CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
12883 player->index_bit, center_side);
12884 CheckTriggeredElementChangeByPlayer(x, y, center_element,
12885 CE_PLAYER_TOUCHES_X,
12886 player->index_bit, center_side);
12889 /* use player element that is initially defined in the level playfield,
12890 not the player element that corresponds to the runtime player number
12891 (example: a level that contains EL_PLAYER_3 as the only player would
12892 incorrectly give EL_PLAYER_1 for "player->element_nr") */
12893 int player_element = PLAYERINFO(xx, yy)->initial_element;
12895 CheckElementChangeBySide(x, y, center_element, player_element,
12896 CE_TOUCHING_X, center_side);
12904 void TestIfElementTouchesCustomElement(int x, int y)
12906 static int xy[4][2] =
12913 static int trigger_sides[4][2] =
12915 /* center side border side */
12916 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
12917 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
12918 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
12919 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
12921 static int touch_dir[4] =
12923 MV_LEFT | MV_RIGHT,
12928 boolean change_center_element = FALSE;
12929 int center_element = Feld[x][y]; /* should always be non-moving! */
12930 int border_element_old[NUM_DIRECTIONS];
12933 for (i = 0; i < NUM_DIRECTIONS; i++)
12935 int xx = x + xy[i][0];
12936 int yy = y + xy[i][1];
12937 int border_element;
12939 border_element_old[i] = -1;
12941 if (!IN_LEV_FIELD(xx, yy))
12944 if (game.engine_version < VERSION_IDENT(3,0,7,0))
12945 border_element = Feld[xx][yy]; /* may be moving! */
12946 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
12947 border_element = Feld[xx][yy];
12948 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
12949 border_element = MovingOrBlocked2Element(xx, yy);
12951 continue; /* center and border element do not touch */
12953 border_element_old[i] = border_element;
12956 for (i = 0; i < NUM_DIRECTIONS; i++)
12958 int xx = x + xy[i][0];
12959 int yy = y + xy[i][1];
12960 int center_side = trigger_sides[i][0];
12961 int border_element = border_element_old[i];
12963 if (border_element == -1)
12966 /* check for change of border element */
12967 CheckElementChangeBySide(xx, yy, border_element, center_element,
12968 CE_TOUCHING_X, center_side);
12970 /* (center element cannot be player, so we dont have to check this here) */
12973 for (i = 0; i < NUM_DIRECTIONS; i++)
12975 int xx = x + xy[i][0];
12976 int yy = y + xy[i][1];
12977 int border_side = trigger_sides[i][1];
12978 int border_element = border_element_old[i];
12980 if (border_element == -1)
12983 /* check for change of center element (but change it only once) */
12984 if (!change_center_element)
12985 change_center_element =
12986 CheckElementChangeBySide(x, y, center_element, border_element,
12987 CE_TOUCHING_X, border_side);
12989 if (IS_PLAYER(xx, yy))
12991 /* use player element that is initially defined in the level playfield,
12992 not the player element that corresponds to the runtime player number
12993 (example: a level that contains EL_PLAYER_3 as the only player would
12994 incorrectly give EL_PLAYER_1 for "player->element_nr") */
12995 int player_element = PLAYERINFO(xx, yy)->initial_element;
12997 CheckElementChangeBySide(x, y, center_element, player_element,
12998 CE_TOUCHING_X, border_side);
13003 void TestIfElementHitsCustomElement(int x, int y, int direction)
13005 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
13006 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
13007 int hitx = x + dx, hity = y + dy;
13008 int hitting_element = Feld[x][y];
13009 int touched_element;
13011 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
13014 touched_element = (IN_LEV_FIELD(hitx, hity) ?
13015 MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
13017 if (IN_LEV_FIELD(hitx, hity))
13019 int opposite_direction = MV_DIR_OPPOSITE(direction);
13020 int hitting_side = direction;
13021 int touched_side = opposite_direction;
13022 boolean object_hit = (!IS_MOVING(hitx, hity) ||
13023 MovDir[hitx][hity] != direction ||
13024 ABS(MovPos[hitx][hity]) <= TILEY / 2);
13030 CheckElementChangeBySide(x, y, hitting_element, touched_element,
13031 CE_HITTING_X, touched_side);
13033 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13034 CE_HIT_BY_X, hitting_side);
13036 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13037 CE_HIT_BY_SOMETHING, opposite_direction);
13039 if (IS_PLAYER(hitx, hity))
13041 /* use player element that is initially defined in the level playfield,
13042 not the player element that corresponds to the runtime player number
13043 (example: a level that contains EL_PLAYER_3 as the only player would
13044 incorrectly give EL_PLAYER_1 for "player->element_nr") */
13045 int player_element = PLAYERINFO(hitx, hity)->initial_element;
13047 CheckElementChangeBySide(x, y, hitting_element, player_element,
13048 CE_HITTING_X, touched_side);
13053 /* "hitting something" is also true when hitting the playfield border */
13054 CheckElementChangeBySide(x, y, hitting_element, touched_element,
13055 CE_HITTING_SOMETHING, direction);
13058 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
13060 int i, kill_x = -1, kill_y = -1;
13062 int bad_element = -1;
13063 static int test_xy[4][2] =
13070 static int test_dir[4] =
13078 for (i = 0; i < NUM_DIRECTIONS; i++)
13080 int test_x, test_y, test_move_dir, test_element;
13082 test_x = good_x + test_xy[i][0];
13083 test_y = good_y + test_xy[i][1];
13085 if (!IN_LEV_FIELD(test_x, test_y))
13089 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13091 test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
13093 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
13094 2nd case: DONT_TOUCH style bad thing does not move away from good thing
13096 if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
13097 (DONT_TOUCH(test_element) && test_move_dir != test_dir[i]))
13101 bad_element = test_element;
13107 if (kill_x != -1 || kill_y != -1)
13109 if (IS_PLAYER(good_x, good_y))
13111 struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
13113 if (player->shield_deadly_time_left > 0 &&
13114 !IS_INDESTRUCTIBLE(bad_element))
13115 Bang(kill_x, kill_y);
13116 else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
13117 KillPlayer(player);
13120 Bang(good_x, good_y);
13124 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
13126 int i, kill_x = -1, kill_y = -1;
13127 int bad_element = Feld[bad_x][bad_y];
13128 static int test_xy[4][2] =
13135 static int touch_dir[4] =
13137 MV_LEFT | MV_RIGHT,
13142 static int test_dir[4] =
13150 if (bad_element == EL_EXPLOSION) /* skip just exploding bad things */
13153 for (i = 0; i < NUM_DIRECTIONS; i++)
13155 int test_x, test_y, test_move_dir, test_element;
13157 test_x = bad_x + test_xy[i][0];
13158 test_y = bad_y + test_xy[i][1];
13160 if (!IN_LEV_FIELD(test_x, test_y))
13164 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13166 test_element = Feld[test_x][test_y];
13168 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
13169 2nd case: DONT_TOUCH style bad thing does not move away from good thing
13171 if ((DONT_RUN_INTO(bad_element) && bad_move_dir == test_dir[i]) ||
13172 (DONT_TOUCH(bad_element) && test_move_dir != test_dir[i]))
13174 /* good thing is player or penguin that does not move away */
13175 if (IS_PLAYER(test_x, test_y))
13177 struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
13179 if (bad_element == EL_ROBOT && player->is_moving)
13180 continue; /* robot does not kill player if he is moving */
13182 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13184 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
13185 continue; /* center and border element do not touch */
13193 else if (test_element == EL_PENGUIN)
13203 if (kill_x != -1 || kill_y != -1)
13205 if (IS_PLAYER(kill_x, kill_y))
13207 struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
13209 if (player->shield_deadly_time_left > 0 &&
13210 !IS_INDESTRUCTIBLE(bad_element))
13211 Bang(bad_x, bad_y);
13212 else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
13213 KillPlayer(player);
13216 Bang(kill_x, kill_y);
13220 void TestIfGoodThingGetsHitByBadThing(int bad_x, int bad_y, int bad_move_dir)
13222 int bad_element = Feld[bad_x][bad_y];
13223 int dx = (bad_move_dir == MV_LEFT ? -1 : bad_move_dir == MV_RIGHT ? +1 : 0);
13224 int dy = (bad_move_dir == MV_UP ? -1 : bad_move_dir == MV_DOWN ? +1 : 0);
13225 int test_x = bad_x + dx, test_y = bad_y + dy;
13226 int test_move_dir, test_element;
13227 int kill_x = -1, kill_y = -1;
13229 if (!IN_LEV_FIELD(test_x, test_y))
13233 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13235 test_element = Feld[test_x][test_y];
13237 if (test_move_dir != bad_move_dir)
13239 /* good thing can be player or penguin that does not move away */
13240 if (IS_PLAYER(test_x, test_y))
13242 struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
13244 /* (note: in comparison to DONT_RUN_TO and DONT_TOUCH, also handle the
13245 player as being hit when he is moving towards the bad thing, because
13246 the "get hit by" condition would be lost after the player stops) */
13247 if (player->MovPos != 0 && player->MovDir == bad_move_dir)
13248 return; /* player moves away from bad thing */
13253 else if (test_element == EL_PENGUIN)
13260 if (kill_x != -1 || kill_y != -1)
13262 if (IS_PLAYER(kill_x, kill_y))
13264 struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
13266 if (player->shield_deadly_time_left > 0 &&
13267 !IS_INDESTRUCTIBLE(bad_element))
13268 Bang(bad_x, bad_y);
13269 else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
13270 KillPlayer(player);
13273 Bang(kill_x, kill_y);
13277 void TestIfPlayerTouchesBadThing(int x, int y)
13279 TestIfGoodThingHitsBadThing(x, y, MV_NONE);
13282 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
13284 TestIfGoodThingHitsBadThing(x, y, move_dir);
13287 void TestIfBadThingTouchesPlayer(int x, int y)
13289 TestIfBadThingHitsGoodThing(x, y, MV_NONE);
13292 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
13294 TestIfBadThingHitsGoodThing(x, y, move_dir);
13297 void TestIfFriendTouchesBadThing(int x, int y)
13299 TestIfGoodThingHitsBadThing(x, y, MV_NONE);
13302 void TestIfBadThingTouchesFriend(int x, int y)
13304 TestIfBadThingHitsGoodThing(x, y, MV_NONE);
13307 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
13309 int i, kill_x = bad_x, kill_y = bad_y;
13310 static int xy[4][2] =
13318 for (i = 0; i < NUM_DIRECTIONS; i++)
13322 x = bad_x + xy[i][0];
13323 y = bad_y + xy[i][1];
13324 if (!IN_LEV_FIELD(x, y))
13327 element = Feld[x][y];
13328 if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
13329 element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
13337 if (kill_x != bad_x || kill_y != bad_y)
13338 Bang(bad_x, bad_y);
13341 void KillPlayer(struct PlayerInfo *player)
13343 int jx = player->jx, jy = player->jy;
13345 if (!player->active)
13349 printf("::: 0: killed == %d, active == %d, reanimated == %d\n",
13350 player->killed, player->active, player->reanimated);
13353 /* the following code was introduced to prevent an infinite loop when calling
13355 -> CheckTriggeredElementChangeExt()
13356 -> ExecuteCustomElementAction()
13358 -> (infinitely repeating the above sequence of function calls)
13359 which occurs when killing the player while having a CE with the setting
13360 "kill player X when explosion of <player X>"; the solution using a new
13361 field "player->killed" was chosen for backwards compatibility, although
13362 clever use of the fields "player->active" etc. would probably also work */
13364 if (player->killed)
13368 player->killed = TRUE;
13370 /* remove accessible field at the player's position */
13371 Feld[jx][jy] = EL_EMPTY;
13373 /* deactivate shield (else Bang()/Explode() would not work right) */
13374 player->shield_normal_time_left = 0;
13375 player->shield_deadly_time_left = 0;
13378 printf("::: 1: killed == %d, active == %d, reanimated == %d\n",
13379 player->killed, player->active, player->reanimated);
13385 printf("::: 2: killed == %d, active == %d, reanimated == %d\n",
13386 player->killed, player->active, player->reanimated);
13389 if (player->reanimated) /* killed player may have been reanimated */
13390 player->killed = player->reanimated = FALSE;
13392 BuryPlayer(player);
13395 static void KillPlayerUnlessEnemyProtected(int x, int y)
13397 if (!PLAYER_ENEMY_PROTECTED(x, y))
13398 KillPlayer(PLAYERINFO(x, y));
13401 static void KillPlayerUnlessExplosionProtected(int x, int y)
13403 if (!PLAYER_EXPLOSION_PROTECTED(x, y))
13404 KillPlayer(PLAYERINFO(x, y));
13407 void BuryPlayer(struct PlayerInfo *player)
13409 int jx = player->jx, jy = player->jy;
13411 if (!player->active)
13414 PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
13415 PlayLevelSound(jx, jy, SND_GAME_LOSING);
13417 player->GameOver = TRUE;
13418 RemovePlayer(player);
13421 void RemovePlayer(struct PlayerInfo *player)
13423 int jx = player->jx, jy = player->jy;
13424 int i, found = FALSE;
13426 player->present = FALSE;
13427 player->active = FALSE;
13429 if (!ExplodeField[jx][jy])
13430 StorePlayer[jx][jy] = 0;
13432 if (player->is_moving)
13433 TEST_DrawLevelField(player->last_jx, player->last_jy);
13435 for (i = 0; i < MAX_PLAYERS; i++)
13436 if (stored_player[i].active)
13440 AllPlayersGone = TRUE;
13446 static void setFieldForSnapping(int x, int y, int element, int direction)
13448 struct ElementInfo *ei = &element_info[element];
13449 int direction_bit = MV_DIR_TO_BIT(direction);
13450 int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
13451 int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
13452 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
13454 Feld[x][y] = EL_ELEMENT_SNAPPING;
13455 MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
13457 ResetGfxAnimation(x, y);
13459 GfxElement[x][y] = element;
13460 GfxAction[x][y] = action;
13461 GfxDir[x][y] = direction;
13462 GfxFrame[x][y] = -1;
13466 =============================================================================
13467 checkDiagonalPushing()
13468 -----------------------------------------------------------------------------
13469 check if diagonal input device direction results in pushing of object
13470 (by checking if the alternative direction is walkable, diggable, ...)
13471 =============================================================================
13474 static boolean checkDiagonalPushing(struct PlayerInfo *player,
13475 int x, int y, int real_dx, int real_dy)
13477 int jx, jy, dx, dy, xx, yy;
13479 if (real_dx == 0 || real_dy == 0) /* no diagonal direction => push */
13482 /* diagonal direction: check alternative direction */
13487 xx = jx + (dx == 0 ? real_dx : 0);
13488 yy = jy + (dy == 0 ? real_dy : 0);
13490 return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
13494 =============================================================================
13496 -----------------------------------------------------------------------------
13497 x, y: field next to player (non-diagonal) to try to dig to
13498 real_dx, real_dy: direction as read from input device (can be diagonal)
13499 =============================================================================
13502 static int DigField(struct PlayerInfo *player,
13503 int oldx, int oldy, int x, int y,
13504 int real_dx, int real_dy, int mode)
13506 boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
13507 boolean player_was_pushing = player->is_pushing;
13508 boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
13509 boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
13510 int jx = oldx, jy = oldy;
13511 int dx = x - jx, dy = y - jy;
13512 int nextx = x + dx, nexty = y + dy;
13513 int move_direction = (dx == -1 ? MV_LEFT :
13514 dx == +1 ? MV_RIGHT :
13516 dy == +1 ? MV_DOWN : MV_NONE);
13517 int opposite_direction = MV_DIR_OPPOSITE(move_direction);
13518 int dig_side = MV_DIR_OPPOSITE(move_direction);
13519 int old_element = Feld[jx][jy];
13520 int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
13523 if (is_player) /* function can also be called by EL_PENGUIN */
13525 if (player->MovPos == 0)
13527 player->is_digging = FALSE;
13528 player->is_collecting = FALSE;
13531 if (player->MovPos == 0) /* last pushing move finished */
13532 player->is_pushing = FALSE;
13534 if (mode == DF_NO_PUSH) /* player just stopped pushing */
13536 player->is_switching = FALSE;
13537 player->push_delay = -1;
13539 return MP_NO_ACTION;
13543 if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
13544 old_element = Back[jx][jy];
13546 /* in case of element dropped at player position, check background */
13547 else if (Back[jx][jy] != EL_EMPTY &&
13548 game.engine_version >= VERSION_IDENT(2,2,0,0))
13549 old_element = Back[jx][jy];
13551 if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
13552 return MP_NO_ACTION; /* field has no opening in this direction */
13554 if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
13555 return MP_NO_ACTION; /* field has no opening in this direction */
13557 if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
13561 Feld[jx][jy] = player->artwork_element;
13562 InitMovingField(jx, jy, MV_DOWN);
13563 Store[jx][jy] = EL_ACID;
13564 ContinueMoving(jx, jy);
13565 BuryPlayer(player);
13567 return MP_DONT_RUN_INTO;
13570 if (player_can_move && DONT_RUN_INTO(element))
13572 TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
13574 return MP_DONT_RUN_INTO;
13577 if (IS_MOVING(x, y) || IS_PLAYER(x, y))
13578 return MP_NO_ACTION;
13580 collect_count = element_info[element].collect_count_initial;
13582 if (!is_player && !IS_COLLECTIBLE(element)) /* penguin cannot collect it */
13583 return MP_NO_ACTION;
13585 if (game.engine_version < VERSION_IDENT(2,2,0,0))
13586 player_can_move = player_can_move_or_snap;
13588 if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
13589 game.engine_version >= VERSION_IDENT(2,2,0,0))
13591 CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
13592 player->index_bit, dig_side);
13593 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13594 player->index_bit, dig_side);
13596 if (element == EL_DC_LANDMINE)
13599 if (Feld[x][y] != element) /* field changed by snapping */
13602 return MP_NO_ACTION;
13605 if (player->gravity && is_player && !player->is_auto_moving &&
13606 canFallDown(player) && move_direction != MV_DOWN &&
13607 !canMoveToValidFieldWithGravity(jx, jy, move_direction))
13608 return MP_NO_ACTION; /* player cannot walk here due to gravity */
13610 if (player_can_move &&
13611 IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
13613 int sound_element = SND_ELEMENT(element);
13614 int sound_action = ACTION_WALKING;
13616 if (IS_RND_GATE(element))
13618 if (!player->key[RND_GATE_NR(element)])
13619 return MP_NO_ACTION;
13621 else if (IS_RND_GATE_GRAY(element))
13623 if (!player->key[RND_GATE_GRAY_NR(element)])
13624 return MP_NO_ACTION;
13626 else if (IS_RND_GATE_GRAY_ACTIVE(element))
13628 if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
13629 return MP_NO_ACTION;
13631 else if (element == EL_EXIT_OPEN ||
13632 element == EL_EM_EXIT_OPEN ||
13633 element == EL_EM_EXIT_OPENING ||
13634 element == EL_STEEL_EXIT_OPEN ||
13635 element == EL_EM_STEEL_EXIT_OPEN ||
13636 element == EL_EM_STEEL_EXIT_OPENING ||
13637 element == EL_SP_EXIT_OPEN ||
13638 element == EL_SP_EXIT_OPENING)
13640 sound_action = ACTION_PASSING; /* player is passing exit */
13642 else if (element == EL_EMPTY)
13644 sound_action = ACTION_MOVING; /* nothing to walk on */
13647 /* play sound from background or player, whatever is available */
13648 if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
13649 PlayLevelSoundElementAction(x, y, sound_element, sound_action);
13651 PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
13653 else if (player_can_move &&
13654 IS_PASSABLE(element) && canPassField(x, y, move_direction))
13656 if (!ACCESS_FROM(element, opposite_direction))
13657 return MP_NO_ACTION; /* field not accessible from this direction */
13659 if (CAN_MOVE(element)) /* only fixed elements can be passed! */
13660 return MP_NO_ACTION;
13662 if (IS_EM_GATE(element))
13664 if (!player->key[EM_GATE_NR(element)])
13665 return MP_NO_ACTION;
13667 else if (IS_EM_GATE_GRAY(element))
13669 if (!player->key[EM_GATE_GRAY_NR(element)])
13670 return MP_NO_ACTION;
13672 else if (IS_EM_GATE_GRAY_ACTIVE(element))
13674 if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
13675 return MP_NO_ACTION;
13677 else if (IS_EMC_GATE(element))
13679 if (!player->key[EMC_GATE_NR(element)])
13680 return MP_NO_ACTION;
13682 else if (IS_EMC_GATE_GRAY(element))
13684 if (!player->key[EMC_GATE_GRAY_NR(element)])
13685 return MP_NO_ACTION;
13687 else if (IS_EMC_GATE_GRAY_ACTIVE(element))
13689 if (!player->key[EMC_GATE_GRAY_ACTIVE_NR(element)])
13690 return MP_NO_ACTION;
13692 else if (element == EL_DC_GATE_WHITE ||
13693 element == EL_DC_GATE_WHITE_GRAY ||
13694 element == EL_DC_GATE_WHITE_GRAY_ACTIVE)
13696 if (player->num_white_keys == 0)
13697 return MP_NO_ACTION;
13699 player->num_white_keys--;
13701 else if (IS_SP_PORT(element))
13703 if (element == EL_SP_GRAVITY_PORT_LEFT ||
13704 element == EL_SP_GRAVITY_PORT_RIGHT ||
13705 element == EL_SP_GRAVITY_PORT_UP ||
13706 element == EL_SP_GRAVITY_PORT_DOWN)
13707 player->gravity = !player->gravity;
13708 else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
13709 element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
13710 element == EL_SP_GRAVITY_ON_PORT_UP ||
13711 element == EL_SP_GRAVITY_ON_PORT_DOWN)
13712 player->gravity = TRUE;
13713 else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
13714 element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
13715 element == EL_SP_GRAVITY_OFF_PORT_UP ||
13716 element == EL_SP_GRAVITY_OFF_PORT_DOWN)
13717 player->gravity = FALSE;
13720 /* automatically move to the next field with double speed */
13721 player->programmed_action = move_direction;
13723 if (player->move_delay_reset_counter == 0)
13725 player->move_delay_reset_counter = 2; /* two double speed steps */
13727 DOUBLE_PLAYER_SPEED(player);
13730 PlayLevelSoundAction(x, y, ACTION_PASSING);
13732 else if (player_can_move_or_snap && IS_DIGGABLE(element))
13736 if (mode != DF_SNAP)
13738 GfxElement[x][y] = GFX_ELEMENT(element);
13739 player->is_digging = TRUE;
13742 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
13744 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
13745 player->index_bit, dig_side);
13747 if (mode == DF_SNAP)
13749 if (level.block_snap_field)
13750 setFieldForSnapping(x, y, element, move_direction);
13752 TestIfElementTouchesCustomElement(x, y); /* for empty space */
13754 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13755 player->index_bit, dig_side);
13758 else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
13762 if (is_player && mode != DF_SNAP)
13764 GfxElement[x][y] = element;
13765 player->is_collecting = TRUE;
13768 if (element == EL_SPEED_PILL)
13770 player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
13772 else if (element == EL_EXTRA_TIME && level.time > 0)
13774 TimeLeft += level.extra_time;
13776 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
13778 DisplayGameControlValues();
13780 else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
13782 player->shield_normal_time_left += level.shield_normal_time;
13783 if (element == EL_SHIELD_DEADLY)
13784 player->shield_deadly_time_left += level.shield_deadly_time;
13786 else if (element == EL_DYNAMITE ||
13787 element == EL_EM_DYNAMITE ||
13788 element == EL_SP_DISK_RED)
13790 if (player->inventory_size < MAX_INVENTORY_SIZE)
13791 player->inventory_element[player->inventory_size++] = element;
13793 DrawGameDoorValues();
13795 else if (element == EL_DYNABOMB_INCREASE_NUMBER)
13797 player->dynabomb_count++;
13798 player->dynabombs_left++;
13800 else if (element == EL_DYNABOMB_INCREASE_SIZE)
13802 player->dynabomb_size++;
13804 else if (element == EL_DYNABOMB_INCREASE_POWER)
13806 player->dynabomb_xl = TRUE;
13808 else if (IS_KEY(element))
13810 player->key[KEY_NR(element)] = TRUE;
13812 DrawGameDoorValues();
13814 else if (element == EL_DC_KEY_WHITE)
13816 player->num_white_keys++;
13818 /* display white keys? */
13819 /* DrawGameDoorValues(); */
13821 else if (IS_ENVELOPE(element))
13823 player->show_envelope = element;
13825 else if (element == EL_EMC_LENSES)
13827 game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
13829 RedrawAllInvisibleElementsForLenses();
13831 else if (element == EL_EMC_MAGNIFIER)
13833 game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
13835 RedrawAllInvisibleElementsForMagnifier();
13837 else if (IS_DROPPABLE(element) ||
13838 IS_THROWABLE(element)) /* can be collected and dropped */
13842 if (collect_count == 0)
13843 player->inventory_infinite_element = element;
13845 for (i = 0; i < collect_count; i++)
13846 if (player->inventory_size < MAX_INVENTORY_SIZE)
13847 player->inventory_element[player->inventory_size++] = element;
13849 DrawGameDoorValues();
13851 else if (collect_count > 0)
13853 local_player->gems_still_needed -= collect_count;
13854 if (local_player->gems_still_needed < 0)
13855 local_player->gems_still_needed = 0;
13857 game.snapshot.collected_item = TRUE;
13859 game_panel_controls[GAME_PANEL_GEMS].value = local_player->gems_still_needed;
13861 DisplayGameControlValues();
13864 RaiseScoreElement(element);
13865 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
13868 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
13869 player->index_bit, dig_side);
13871 if (mode == DF_SNAP)
13873 if (level.block_snap_field)
13874 setFieldForSnapping(x, y, element, move_direction);
13876 TestIfElementTouchesCustomElement(x, y); /* for empty space */
13878 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13879 player->index_bit, dig_side);
13882 else if (player_can_move_or_snap && IS_PUSHABLE(element))
13884 if (mode == DF_SNAP && element != EL_BD_ROCK)
13885 return MP_NO_ACTION;
13887 if (CAN_FALL(element) && dy)
13888 return MP_NO_ACTION;
13890 if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
13891 !(element == EL_SPRING && level.use_spring_bug))
13892 return MP_NO_ACTION;
13894 if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
13895 ((move_direction & MV_VERTICAL &&
13896 ((element_info[element].move_pattern & MV_LEFT &&
13897 IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
13898 (element_info[element].move_pattern & MV_RIGHT &&
13899 IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
13900 (move_direction & MV_HORIZONTAL &&
13901 ((element_info[element].move_pattern & MV_UP &&
13902 IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
13903 (element_info[element].move_pattern & MV_DOWN &&
13904 IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
13905 return MP_NO_ACTION;
13907 /* do not push elements already moving away faster than player */
13908 if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
13909 ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
13910 return MP_NO_ACTION;
13912 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
13914 if (player->push_delay_value == -1 || !player_was_pushing)
13915 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13917 else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
13919 if (player->push_delay_value == -1)
13920 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13922 else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
13924 if (!player->is_pushing)
13925 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13928 player->is_pushing = TRUE;
13929 player->is_active = TRUE;
13931 if (!(IN_LEV_FIELD(nextx, nexty) &&
13932 (IS_FREE(nextx, nexty) ||
13933 (IS_SB_ELEMENT(element) &&
13934 Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY) ||
13935 (IS_CUSTOM_ELEMENT(element) &&
13936 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty)))))
13937 return MP_NO_ACTION;
13939 if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
13940 return MP_NO_ACTION;
13942 if (player->push_delay == -1) /* new pushing; restart delay */
13943 player->push_delay = 0;
13945 if (player->push_delay < player->push_delay_value &&
13946 !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
13947 element != EL_SPRING && element != EL_BALLOON)
13949 /* make sure that there is no move delay before next try to push */
13950 if (game.engine_version >= VERSION_IDENT(3,0,7,1))
13951 player->move_delay = 0;
13953 return MP_NO_ACTION;
13956 if (IS_CUSTOM_ELEMENT(element) &&
13957 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty))
13959 if (!DigFieldByCE(nextx, nexty, element))
13960 return MP_NO_ACTION;
13963 if (IS_SB_ELEMENT(element))
13965 if (element == EL_SOKOBAN_FIELD_FULL)
13967 Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
13968 local_player->sokobanfields_still_needed++;
13971 if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
13973 Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
13974 local_player->sokobanfields_still_needed--;
13977 Feld[x][y] = EL_SOKOBAN_OBJECT;
13979 if (Back[x][y] == Back[nextx][nexty])
13980 PlayLevelSoundAction(x, y, ACTION_PUSHING);
13981 else if (Back[x][y] != 0)
13982 PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
13985 PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
13988 if (local_player->sokobanfields_still_needed == 0 &&
13989 (game.emulation == EMU_SOKOBAN || level.auto_exit_sokoban))
13991 PlayerWins(player);
13993 PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
13997 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
13999 InitMovingField(x, y, move_direction);
14000 GfxAction[x][y] = ACTION_PUSHING;
14002 if (mode == DF_SNAP)
14003 ContinueMoving(x, y);
14005 MovPos[x][y] = (dx != 0 ? dx : dy);
14007 Pushed[x][y] = TRUE;
14008 Pushed[nextx][nexty] = TRUE;
14010 if (game.engine_version < VERSION_IDENT(2,2,0,7))
14011 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14013 player->push_delay_value = -1; /* get new value later */
14015 /* check for element change _after_ element has been pushed */
14016 if (game.use_change_when_pushing_bug)
14018 CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
14019 player->index_bit, dig_side);
14020 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
14021 player->index_bit, dig_side);
14024 else if (IS_SWITCHABLE(element))
14026 if (PLAYER_SWITCHING(player, x, y))
14028 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14029 player->index_bit, dig_side);
14034 player->is_switching = TRUE;
14035 player->switch_x = x;
14036 player->switch_y = y;
14038 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
14040 if (element == EL_ROBOT_WHEEL)
14042 Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
14046 game.robot_wheel_active = TRUE;
14048 TEST_DrawLevelField(x, y);
14050 else if (element == EL_SP_TERMINAL)
14054 SCAN_PLAYFIELD(xx, yy)
14056 if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
14060 else if (Feld[xx][yy] == EL_SP_TERMINAL)
14062 Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
14064 ResetGfxAnimation(xx, yy);
14065 TEST_DrawLevelField(xx, yy);
14069 else if (IS_BELT_SWITCH(element))
14071 ToggleBeltSwitch(x, y);
14073 else if (element == EL_SWITCHGATE_SWITCH_UP ||
14074 element == EL_SWITCHGATE_SWITCH_DOWN ||
14075 element == EL_DC_SWITCHGATE_SWITCH_UP ||
14076 element == EL_DC_SWITCHGATE_SWITCH_DOWN)
14078 ToggleSwitchgateSwitch(x, y);
14080 else if (element == EL_LIGHT_SWITCH ||
14081 element == EL_LIGHT_SWITCH_ACTIVE)
14083 ToggleLightSwitch(x, y);
14085 else if (element == EL_TIMEGATE_SWITCH ||
14086 element == EL_DC_TIMEGATE_SWITCH)
14088 ActivateTimegateSwitch(x, y);
14090 else if (element == EL_BALLOON_SWITCH_LEFT ||
14091 element == EL_BALLOON_SWITCH_RIGHT ||
14092 element == EL_BALLOON_SWITCH_UP ||
14093 element == EL_BALLOON_SWITCH_DOWN ||
14094 element == EL_BALLOON_SWITCH_NONE ||
14095 element == EL_BALLOON_SWITCH_ANY)
14097 game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT ? MV_LEFT :
14098 element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
14099 element == EL_BALLOON_SWITCH_UP ? MV_UP :
14100 element == EL_BALLOON_SWITCH_DOWN ? MV_DOWN :
14101 element == EL_BALLOON_SWITCH_NONE ? MV_NONE :
14104 else if (element == EL_LAMP)
14106 Feld[x][y] = EL_LAMP_ACTIVE;
14107 local_player->lights_still_needed--;
14109 ResetGfxAnimation(x, y);
14110 TEST_DrawLevelField(x, y);
14112 else if (element == EL_TIME_ORB_FULL)
14114 Feld[x][y] = EL_TIME_ORB_EMPTY;
14116 if (level.time > 0 || level.use_time_orb_bug)
14118 TimeLeft += level.time_orb_time;
14119 game.no_time_limit = FALSE;
14121 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
14123 DisplayGameControlValues();
14126 ResetGfxAnimation(x, y);
14127 TEST_DrawLevelField(x, y);
14129 else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
14130 element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
14134 game.ball_state = !game.ball_state;
14136 SCAN_PLAYFIELD(xx, yy)
14138 int e = Feld[xx][yy];
14140 if (game.ball_state)
14142 if (e == EL_EMC_MAGIC_BALL)
14143 CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
14144 else if (e == EL_EMC_MAGIC_BALL_SWITCH)
14145 CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
14149 if (e == EL_EMC_MAGIC_BALL_ACTIVE)
14150 CreateField(xx, yy, EL_EMC_MAGIC_BALL);
14151 else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
14152 CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
14157 CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
14158 player->index_bit, dig_side);
14160 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
14161 player->index_bit, dig_side);
14163 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14164 player->index_bit, dig_side);
14170 if (!PLAYER_SWITCHING(player, x, y))
14172 player->is_switching = TRUE;
14173 player->switch_x = x;
14174 player->switch_y = y;
14176 CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
14177 player->index_bit, dig_side);
14178 CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
14179 player->index_bit, dig_side);
14181 CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
14182 player->index_bit, dig_side);
14183 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
14184 player->index_bit, dig_side);
14187 CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
14188 player->index_bit, dig_side);
14189 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14190 player->index_bit, dig_side);
14192 return MP_NO_ACTION;
14195 player->push_delay = -1;
14197 if (is_player) /* function can also be called by EL_PENGUIN */
14199 if (Feld[x][y] != element) /* really digged/collected something */
14201 player->is_collecting = !player->is_digging;
14202 player->is_active = TRUE;
14209 static boolean DigFieldByCE(int x, int y, int digging_element)
14211 int element = Feld[x][y];
14213 if (!IS_FREE(x, y))
14215 int action = (IS_DIGGABLE(element) ? ACTION_DIGGING :
14216 IS_COLLECTIBLE(element) ? ACTION_COLLECTING :
14219 /* no element can dig solid indestructible elements */
14220 if (IS_INDESTRUCTIBLE(element) &&
14221 !IS_DIGGABLE(element) &&
14222 !IS_COLLECTIBLE(element))
14225 if (AmoebaNr[x][y] &&
14226 (element == EL_AMOEBA_FULL ||
14227 element == EL_BD_AMOEBA ||
14228 element == EL_AMOEBA_GROWING))
14230 AmoebaCnt[AmoebaNr[x][y]]--;
14231 AmoebaCnt2[AmoebaNr[x][y]]--;
14234 if (IS_MOVING(x, y))
14235 RemoveMovingField(x, y);
14239 TEST_DrawLevelField(x, y);
14242 /* if digged element was about to explode, prevent the explosion */
14243 ExplodeField[x][y] = EX_TYPE_NONE;
14245 PlayLevelSoundAction(x, y, action);
14248 Store[x][y] = EL_EMPTY;
14250 /* this makes it possible to leave the removed element again */
14251 if (IS_EQUAL_OR_IN_GROUP(element, MOVE_ENTER_EL(digging_element)))
14252 Store[x][y] = element;
14257 static boolean SnapField(struct PlayerInfo *player, int dx, int dy)
14259 int jx = player->jx, jy = player->jy;
14260 int x = jx + dx, y = jy + dy;
14261 int snap_direction = (dx == -1 ? MV_LEFT :
14262 dx == +1 ? MV_RIGHT :
14264 dy == +1 ? MV_DOWN : MV_NONE);
14265 boolean can_continue_snapping = (level.continuous_snapping &&
14266 WasJustFalling[x][y] < CHECK_DELAY_FALLING);
14268 if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
14271 if (!player->active || !IN_LEV_FIELD(x, y))
14279 if (player->MovPos == 0)
14280 player->is_pushing = FALSE;
14282 player->is_snapping = FALSE;
14284 if (player->MovPos == 0)
14286 player->is_moving = FALSE;
14287 player->is_digging = FALSE;
14288 player->is_collecting = FALSE;
14294 /* prevent snapping with already pressed snap key when not allowed */
14295 if (player->is_snapping && !can_continue_snapping)
14298 player->MovDir = snap_direction;
14300 if (player->MovPos == 0)
14302 player->is_moving = FALSE;
14303 player->is_digging = FALSE;
14304 player->is_collecting = FALSE;
14307 player->is_dropping = FALSE;
14308 player->is_dropping_pressed = FALSE;
14309 player->drop_pressed_delay = 0;
14311 if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
14314 player->is_snapping = TRUE;
14315 player->is_active = TRUE;
14317 if (player->MovPos == 0)
14319 player->is_moving = FALSE;
14320 player->is_digging = FALSE;
14321 player->is_collecting = FALSE;
14324 if (player->MovPos != 0) /* prevent graphic bugs in versions < 2.2.0 */
14325 TEST_DrawLevelField(player->last_jx, player->last_jy);
14327 TEST_DrawLevelField(x, y);
14332 static boolean DropElement(struct PlayerInfo *player)
14334 int old_element, new_element;
14335 int dropx = player->jx, dropy = player->jy;
14336 int drop_direction = player->MovDir;
14337 int drop_side = drop_direction;
14338 int drop_element = get_next_dropped_element(player);
14340 /* do not drop an element on top of another element; when holding drop key
14341 pressed without moving, dropped element must move away before the next
14342 element can be dropped (this is especially important if the next element
14343 is dynamite, which can be placed on background for historical reasons) */
14344 if (PLAYER_DROPPING(player, dropx, dropy) && Feld[dropx][dropy] != EL_EMPTY)
14347 if (IS_THROWABLE(drop_element))
14349 dropx += GET_DX_FROM_DIR(drop_direction);
14350 dropy += GET_DY_FROM_DIR(drop_direction);
14352 if (!IN_LEV_FIELD(dropx, dropy))
14356 old_element = Feld[dropx][dropy]; /* old element at dropping position */
14357 new_element = drop_element; /* default: no change when dropping */
14359 /* check if player is active, not moving and ready to drop */
14360 if (!player->active || player->MovPos || player->drop_delay > 0)
14363 /* check if player has anything that can be dropped */
14364 if (new_element == EL_UNDEFINED)
14367 /* only set if player has anything that can be dropped */
14368 player->is_dropping_pressed = TRUE;
14370 /* check if drop key was pressed long enough for EM style dynamite */
14371 if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
14374 /* check if anything can be dropped at the current position */
14375 if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
14378 /* collected custom elements can only be dropped on empty fields */
14379 if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
14382 if (old_element != EL_EMPTY)
14383 Back[dropx][dropy] = old_element; /* store old element on this field */
14385 ResetGfxAnimation(dropx, dropy);
14386 ResetRandomAnimationValue(dropx, dropy);
14388 if (player->inventory_size > 0 ||
14389 player->inventory_infinite_element != EL_UNDEFINED)
14391 if (player->inventory_size > 0)
14393 player->inventory_size--;
14395 DrawGameDoorValues();
14397 if (new_element == EL_DYNAMITE)
14398 new_element = EL_DYNAMITE_ACTIVE;
14399 else if (new_element == EL_EM_DYNAMITE)
14400 new_element = EL_EM_DYNAMITE_ACTIVE;
14401 else if (new_element == EL_SP_DISK_RED)
14402 new_element = EL_SP_DISK_RED_ACTIVE;
14405 Feld[dropx][dropy] = new_element;
14407 if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
14408 DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
14409 el2img(Feld[dropx][dropy]), 0);
14411 PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
14413 /* needed if previous element just changed to "empty" in the last frame */
14414 ChangeCount[dropx][dropy] = 0; /* allow at least one more change */
14416 CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
14417 player->index_bit, drop_side);
14418 CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
14420 player->index_bit, drop_side);
14422 TestIfElementTouchesCustomElement(dropx, dropy);
14424 else /* player is dropping a dyna bomb */
14426 player->dynabombs_left--;
14428 Feld[dropx][dropy] = new_element;
14430 if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
14431 DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
14432 el2img(Feld[dropx][dropy]), 0);
14434 PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
14437 if (Feld[dropx][dropy] == new_element) /* uninitialized unless CE change */
14438 InitField_WithBug1(dropx, dropy, FALSE);
14440 new_element = Feld[dropx][dropy]; /* element might have changed */
14442 if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
14443 element_info[new_element].move_pattern == MV_WHEN_DROPPED)
14445 if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
14446 MovDir[dropx][dropy] = drop_direction;
14448 ChangeCount[dropx][dropy] = 0; /* allow at least one more change */
14450 /* do not cause impact style collision by dropping elements that can fall */
14451 CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
14454 player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
14455 player->is_dropping = TRUE;
14457 player->drop_pressed_delay = 0;
14458 player->is_dropping_pressed = FALSE;
14460 player->drop_x = dropx;
14461 player->drop_y = dropy;
14466 /* ------------------------------------------------------------------------- */
14467 /* game sound playing functions */
14468 /* ------------------------------------------------------------------------- */
14470 static int *loop_sound_frame = NULL;
14471 static int *loop_sound_volume = NULL;
14473 void InitPlayLevelSound()
14475 int num_sounds = getSoundListSize();
14477 checked_free(loop_sound_frame);
14478 checked_free(loop_sound_volume);
14480 loop_sound_frame = checked_calloc(num_sounds * sizeof(int));
14481 loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
14484 static void PlayLevelSound(int x, int y, int nr)
14486 int sx = SCREENX(x), sy = SCREENY(y);
14487 int volume, stereo_position;
14488 int max_distance = 8;
14489 int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
14491 if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
14492 (!setup.sound_loops && IS_LOOP_SOUND(nr)))
14495 if (!IN_LEV_FIELD(x, y) ||
14496 sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
14497 sy < -max_distance || sy >= SCR_FIELDY + max_distance)
14500 volume = SOUND_MAX_VOLUME;
14502 if (!IN_SCR_FIELD(sx, sy))
14504 int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
14505 int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
14507 volume -= volume * (dx > dy ? dx : dy) / max_distance;
14510 stereo_position = (SOUND_MAX_LEFT +
14511 (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
14512 (SCR_FIELDX + 2 * max_distance));
14514 if (IS_LOOP_SOUND(nr))
14516 /* This assures that quieter loop sounds do not overwrite louder ones,
14517 while restarting sound volume comparison with each new game frame. */
14519 if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
14522 loop_sound_volume[nr] = volume;
14523 loop_sound_frame[nr] = FrameCounter;
14526 PlaySoundExt(nr, volume, stereo_position, type);
14529 static void PlayLevelSoundNearest(int x, int y, int sound_action)
14531 PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
14532 x > LEVELX(BX2) ? LEVELX(BX2) : x,
14533 y < LEVELY(BY1) ? LEVELY(BY1) :
14534 y > LEVELY(BY2) ? LEVELY(BY2) : y,
14538 static void PlayLevelSoundAction(int x, int y, int action)
14540 PlayLevelSoundElementAction(x, y, Feld[x][y], action);
14543 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
14545 int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
14547 if (sound_effect != SND_UNDEFINED)
14548 PlayLevelSound(x, y, sound_effect);
14551 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
14554 int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
14556 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14557 PlayLevelSound(x, y, sound_effect);
14560 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
14562 int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
14564 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14565 PlayLevelSound(x, y, sound_effect);
14568 static void StopLevelSoundActionIfLoop(int x, int y, int action)
14570 int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
14572 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14573 StopSound(sound_effect);
14576 static int getLevelMusicNr()
14578 if (levelset.music[level_nr] != MUS_UNDEFINED)
14579 return levelset.music[level_nr]; /* from config file */
14581 return MAP_NOCONF_MUSIC(level_nr); /* from music dir */
14584 static void FadeLevelSounds()
14589 static void FadeLevelMusic()
14591 int music_nr = getLevelMusicNr();
14592 char *curr_music = getCurrentlyPlayingMusicFilename();
14593 char *next_music = getMusicInfoEntryFilename(music_nr);
14595 if (!strEqual(curr_music, next_music))
14599 void FadeLevelSoundsAndMusic()
14605 static void PlayLevelMusic()
14607 int music_nr = getLevelMusicNr();
14608 char *curr_music = getCurrentlyPlayingMusicFilename();
14609 char *next_music = getMusicInfoEntryFilename(music_nr);
14611 if (!strEqual(curr_music, next_music))
14612 PlayMusic(music_nr);
14615 void PlayLevelSound_EM(int xx, int yy, int element_em, int sample)
14617 int element = (element_em > -1 ? map_element_EM_to_RND(element_em) : 0);
14618 int offset = (BorderElement == EL_STEELWALL ? 1 : 0);
14619 int x = xx - 1 - offset;
14620 int y = yy - 1 - offset;
14625 PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
14629 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14633 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14637 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14641 PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
14645 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14649 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14652 case SAMPLE_android_clone:
14653 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14656 case SAMPLE_android_move:
14657 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14660 case SAMPLE_spring:
14661 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14665 PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
14669 PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
14672 case SAMPLE_eater_eat:
14673 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14677 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14680 case SAMPLE_collect:
14681 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
14684 case SAMPLE_diamond:
14685 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14688 case SAMPLE_squash:
14689 /* !!! CHECK THIS !!! */
14691 PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
14693 PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
14697 case SAMPLE_wonderfall:
14698 PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
14702 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14706 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14710 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14714 PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
14718 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14722 PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
14725 case SAMPLE_wonder:
14726 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14730 PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
14733 case SAMPLE_exit_open:
14734 PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
14737 case SAMPLE_exit_leave:
14738 PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
14741 case SAMPLE_dynamite:
14742 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14746 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14750 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
14754 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14758 PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
14762 PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
14766 PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
14770 PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
14775 void PlayLevelSound_SP(int xx, int yy, int element_sp, int action_sp)
14777 int element = map_element_SP_to_RND(element_sp);
14778 int action = map_action_SP_to_RND(action_sp);
14779 int offset = (setup.sp_show_border_elements ? 0 : 1);
14780 int x = xx - offset;
14781 int y = yy - offset;
14783 PlayLevelSoundElementAction(x, y, element, action);
14786 void PlayLevelSound_MM(int xx, int yy, int element_mm, int action_mm)
14788 int element = map_element_MM_to_RND(element_mm);
14789 int action = map_action_MM_to_RND(action_mm);
14791 int x = xx - offset;
14792 int y = yy - offset;
14794 if (!IS_MM_ELEMENT(element))
14795 element = EL_MM_DEFAULT;
14797 PlayLevelSoundElementAction(x, y, element, action);
14800 void PlaySound_MM(int sound_mm)
14802 int sound = map_sound_MM_to_RND(sound_mm);
14804 if (sound == SND_UNDEFINED)
14810 void PlaySoundLoop_MM(int sound_mm)
14812 int sound = map_sound_MM_to_RND(sound_mm);
14814 if (sound == SND_UNDEFINED)
14817 PlaySoundLoop(sound);
14820 void StopSound_MM(int sound_mm)
14822 int sound = map_sound_MM_to_RND(sound_mm);
14824 if (sound == SND_UNDEFINED)
14830 void RaiseScore(int value)
14832 local_player->score += value;
14834 game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
14836 DisplayGameControlValues();
14839 void RaiseScoreElement(int element)
14844 case EL_BD_DIAMOND:
14845 case EL_EMERALD_YELLOW:
14846 case EL_EMERALD_RED:
14847 case EL_EMERALD_PURPLE:
14848 case EL_SP_INFOTRON:
14849 RaiseScore(level.score[SC_EMERALD]);
14852 RaiseScore(level.score[SC_DIAMOND]);
14855 RaiseScore(level.score[SC_CRYSTAL]);
14858 RaiseScore(level.score[SC_PEARL]);
14861 case EL_BD_BUTTERFLY:
14862 case EL_SP_ELECTRON:
14863 RaiseScore(level.score[SC_BUG]);
14866 case EL_BD_FIREFLY:
14867 case EL_SP_SNIKSNAK:
14868 RaiseScore(level.score[SC_SPACESHIP]);
14871 case EL_DARK_YAMYAM:
14872 RaiseScore(level.score[SC_YAMYAM]);
14875 RaiseScore(level.score[SC_ROBOT]);
14878 RaiseScore(level.score[SC_PACMAN]);
14881 RaiseScore(level.score[SC_NUT]);
14884 case EL_EM_DYNAMITE:
14885 case EL_SP_DISK_RED:
14886 case EL_DYNABOMB_INCREASE_NUMBER:
14887 case EL_DYNABOMB_INCREASE_SIZE:
14888 case EL_DYNABOMB_INCREASE_POWER:
14889 RaiseScore(level.score[SC_DYNAMITE]);
14891 case EL_SHIELD_NORMAL:
14892 case EL_SHIELD_DEADLY:
14893 RaiseScore(level.score[SC_SHIELD]);
14895 case EL_EXTRA_TIME:
14896 RaiseScore(level.extra_time_score);
14910 case EL_DC_KEY_WHITE:
14911 RaiseScore(level.score[SC_KEY]);
14914 RaiseScore(element_info[element].collect_score);
14919 void RequestQuitGameExt(boolean skip_request, boolean quick_quit, char *message)
14921 if (skip_request || Request(message, REQ_ASK | REQ_STAY_CLOSED))
14923 /* closing door required in case of envelope style request dialogs */
14925 CloseDoor(DOOR_CLOSE_1);
14927 #if defined(NETWORK_AVALIABLE)
14928 if (options.network)
14929 SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
14934 FadeSkipNextFadeIn();
14936 SetGameStatus(GAME_MODE_MAIN);
14941 else /* continue playing the game */
14943 if (tape.playing && tape.deactivate_display)
14944 TapeDeactivateDisplayOff(TRUE);
14946 OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
14948 if (tape.playing && tape.deactivate_display)
14949 TapeDeactivateDisplayOn();
14953 void RequestQuitGame(boolean ask_if_really_quit)
14955 boolean quick_quit = (!ask_if_really_quit || level_editor_test_game);
14956 boolean skip_request = AllPlayersGone || quick_quit;
14958 RequestQuitGameExt(skip_request, quick_quit,
14959 "Do you really want to quit the game?");
14962 void RequestRestartGame(char *message)
14964 game.restart_game_message = NULL;
14966 if (Request(message, REQ_ASK | REQ_STAY_CLOSED))
14968 StartGameActions(options.network, setup.autorecord, level.random_seed);
14972 SetGameStatus(GAME_MODE_MAIN);
14979 /* ------------------------------------------------------------------------- */
14980 /* random generator functions */
14981 /* ------------------------------------------------------------------------- */
14983 unsigned int InitEngineRandom_RND(int seed)
14985 game.num_random_calls = 0;
14987 return InitEngineRandom(seed);
14990 unsigned int RND(int max)
14994 game.num_random_calls++;
14996 return GetEngineRandom(max);
15003 /* ------------------------------------------------------------------------- */
15004 /* game engine snapshot handling functions */
15005 /* ------------------------------------------------------------------------- */
15007 struct EngineSnapshotInfo
15009 /* runtime values for custom element collect score */
15010 int collect_score[NUM_CUSTOM_ELEMENTS];
15012 /* runtime values for group element choice position */
15013 int choice_pos[NUM_GROUP_ELEMENTS];
15015 /* runtime values for belt position animations */
15016 int belt_graphic[4][NUM_BELT_PARTS];
15017 int belt_anim_mode[4][NUM_BELT_PARTS];
15020 static struct EngineSnapshotInfo engine_snapshot_rnd;
15021 static char *snapshot_level_identifier = NULL;
15022 static int snapshot_level_nr = -1;
15024 static void SaveEngineSnapshotValues_RND()
15026 static int belt_base_active_element[4] =
15028 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
15029 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
15030 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
15031 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
15035 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
15037 int element = EL_CUSTOM_START + i;
15039 engine_snapshot_rnd.collect_score[i] = element_info[element].collect_score;
15042 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
15044 int element = EL_GROUP_START + i;
15046 engine_snapshot_rnd.choice_pos[i] = element_info[element].group->choice_pos;
15049 for (i = 0; i < 4; i++)
15051 for (j = 0; j < NUM_BELT_PARTS; j++)
15053 int element = belt_base_active_element[i] + j;
15054 int graphic = el2img(element);
15055 int anim_mode = graphic_info[graphic].anim_mode;
15057 engine_snapshot_rnd.belt_graphic[i][j] = graphic;
15058 engine_snapshot_rnd.belt_anim_mode[i][j] = anim_mode;
15063 static void LoadEngineSnapshotValues_RND()
15065 unsigned int num_random_calls = game.num_random_calls;
15068 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
15070 int element = EL_CUSTOM_START + i;
15072 element_info[element].collect_score = engine_snapshot_rnd.collect_score[i];
15075 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
15077 int element = EL_GROUP_START + i;
15079 element_info[element].group->choice_pos = engine_snapshot_rnd.choice_pos[i];
15082 for (i = 0; i < 4; i++)
15084 for (j = 0; j < NUM_BELT_PARTS; j++)
15086 int graphic = engine_snapshot_rnd.belt_graphic[i][j];
15087 int anim_mode = engine_snapshot_rnd.belt_anim_mode[i][j];
15089 graphic_info[graphic].anim_mode = anim_mode;
15093 if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15095 InitRND(tape.random_seed);
15096 for (i = 0; i < num_random_calls; i++)
15100 if (game.num_random_calls != num_random_calls)
15102 Error(ERR_INFO, "number of random calls out of sync");
15103 Error(ERR_INFO, "number of random calls should be %d", num_random_calls);
15104 Error(ERR_INFO, "number of random calls is %d", game.num_random_calls);
15105 Error(ERR_EXIT, "this should not happen -- please debug");
15109 void FreeEngineSnapshotSingle()
15111 FreeSnapshotSingle();
15113 setString(&snapshot_level_identifier, NULL);
15114 snapshot_level_nr = -1;
15117 void FreeEngineSnapshotList()
15119 FreeSnapshotList();
15122 ListNode *SaveEngineSnapshotBuffers()
15124 ListNode *buffers = NULL;
15126 /* copy some special values to a structure better suited for the snapshot */
15128 if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15129 SaveEngineSnapshotValues_RND();
15130 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15131 SaveEngineSnapshotValues_EM();
15132 if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15133 SaveEngineSnapshotValues_SP(&buffers);
15134 if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15135 SaveEngineSnapshotValues_MM(&buffers);
15137 /* save values stored in special snapshot structure */
15139 if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15140 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_rnd));
15141 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15142 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em));
15143 if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15144 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_sp));
15145 if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15146 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_mm));
15148 /* save further RND engine values */
15150 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(stored_player));
15151 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(game));
15152 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(tape));
15154 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ZX));
15155 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ZY));
15156 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExitX));
15157 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExitY));
15159 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(FrameCounter));
15160 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeFrames));
15161 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimePlayed));
15162 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeLeft));
15163 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TapeTime));
15165 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovDir));
15166 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovPos));
15167 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenGfxPos));
15169 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScrollStepSize));
15171 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AllPlayersGone));
15173 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt));
15174 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt2));
15176 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Feld));
15177 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovPos));
15178 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDir));
15179 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDelay));
15180 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeDelay));
15181 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangePage));
15182 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CustomValue));
15183 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store));
15184 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store2));
15185 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(StorePlayer));
15186 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Back));
15187 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaNr));
15188 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustMoving));
15189 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustFalling));
15190 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckCollision));
15191 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckImpact));
15192 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Stop));
15193 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Pushed));
15195 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeCount));
15196 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeEvent));
15198 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodePhase));
15199 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeDelay));
15200 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeField));
15202 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(RunnerVisit));
15203 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(PlayerVisit));
15205 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxFrame));
15206 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxRandom));
15207 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxElement));
15208 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxAction));
15209 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxDir));
15211 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_x));
15212 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_y));
15215 ListNode *node = engine_snapshot_list_rnd;
15218 while (node != NULL)
15220 num_bytes += ((struct EngineSnapshotNodeInfo *)node->content)->size;
15225 printf("::: size of engine snapshot: %d bytes\n", num_bytes);
15231 void SaveEngineSnapshotSingle()
15233 ListNode *buffers = SaveEngineSnapshotBuffers();
15235 /* finally save all snapshot buffers to single snapshot */
15236 SaveSnapshotSingle(buffers);
15238 /* save level identification information */
15239 setString(&snapshot_level_identifier, leveldir_current->identifier);
15240 snapshot_level_nr = level_nr;
15243 boolean CheckSaveEngineSnapshotToList()
15245 boolean save_snapshot =
15246 ((game.snapshot.mode == SNAPSHOT_MODE_EVERY_STEP) ||
15247 (game.snapshot.mode == SNAPSHOT_MODE_EVERY_MOVE &&
15248 game.snapshot.changed_action) ||
15249 (game.snapshot.mode == SNAPSHOT_MODE_EVERY_COLLECT &&
15250 game.snapshot.collected_item));
15252 game.snapshot.changed_action = FALSE;
15253 game.snapshot.collected_item = FALSE;
15254 game.snapshot.save_snapshot = save_snapshot;
15256 return save_snapshot;
15259 void SaveEngineSnapshotToList()
15261 if (game.snapshot.mode == SNAPSHOT_MODE_OFF ||
15265 ListNode *buffers = SaveEngineSnapshotBuffers();
15267 /* finally save all snapshot buffers to snapshot list */
15268 SaveSnapshotToList(buffers);
15271 void SaveEngineSnapshotToListInitial()
15273 FreeEngineSnapshotList();
15275 SaveEngineSnapshotToList();
15278 void LoadEngineSnapshotValues()
15280 /* restore special values from snapshot structure */
15282 if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15283 LoadEngineSnapshotValues_RND();
15284 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15285 LoadEngineSnapshotValues_EM();
15286 if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15287 LoadEngineSnapshotValues_SP();
15288 if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15289 LoadEngineSnapshotValues_MM();
15292 void LoadEngineSnapshotSingle()
15294 LoadSnapshotSingle();
15296 LoadEngineSnapshotValues();
15299 void LoadEngineSnapshot_Undo(int steps)
15301 LoadSnapshotFromList_Older(steps);
15303 LoadEngineSnapshotValues();
15306 void LoadEngineSnapshot_Redo(int steps)
15308 LoadSnapshotFromList_Newer(steps);
15310 LoadEngineSnapshotValues();
15313 boolean CheckEngineSnapshotSingle()
15315 return (strEqual(snapshot_level_identifier, leveldir_current->identifier) &&
15316 snapshot_level_nr == level_nr);
15319 boolean CheckEngineSnapshotList()
15321 return CheckSnapshotList();
15325 /* ---------- new game button stuff ---------------------------------------- */
15332 boolean *setup_value;
15333 boolean allowed_on_tape;
15335 } gamebutton_info[NUM_GAME_BUTTONS] =
15338 IMG_GFX_GAME_BUTTON_STOP, &game.button.stop,
15339 GAME_CTRL_ID_STOP, NULL,
15343 IMG_GFX_GAME_BUTTON_PAUSE, &game.button.pause,
15344 GAME_CTRL_ID_PAUSE, NULL,
15348 IMG_GFX_GAME_BUTTON_PLAY, &game.button.play,
15349 GAME_CTRL_ID_PLAY, NULL,
15353 IMG_GFX_GAME_BUTTON_UNDO, &game.button.undo,
15354 GAME_CTRL_ID_UNDO, NULL,
15358 IMG_GFX_GAME_BUTTON_REDO, &game.button.redo,
15359 GAME_CTRL_ID_REDO, NULL,
15363 IMG_GFX_GAME_BUTTON_SAVE, &game.button.save,
15364 GAME_CTRL_ID_SAVE, NULL,
15368 IMG_GFX_GAME_BUTTON_PAUSE2, &game.button.pause2,
15369 GAME_CTRL_ID_PAUSE2, NULL,
15373 IMG_GFX_GAME_BUTTON_LOAD, &game.button.load,
15374 GAME_CTRL_ID_LOAD, NULL,
15378 IMG_GFX_GAME_BUTTON_PANEL_STOP, &game.button.panel_stop,
15379 GAME_CTRL_ID_PANEL_STOP, NULL,
15383 IMG_GFX_GAME_BUTTON_PANEL_PAUSE, &game.button.panel_pause,
15384 GAME_CTRL_ID_PANEL_PAUSE, NULL,
15385 FALSE, "pause game"
15388 IMG_GFX_GAME_BUTTON_PANEL_PLAY, &game.button.panel_play,
15389 GAME_CTRL_ID_PANEL_PLAY, NULL,
15393 IMG_GFX_GAME_BUTTON_SOUND_MUSIC, &game.button.sound_music,
15394 SOUND_CTRL_ID_MUSIC, &setup.sound_music,
15395 TRUE, "background music on/off"
15398 IMG_GFX_GAME_BUTTON_SOUND_LOOPS, &game.button.sound_loops,
15399 SOUND_CTRL_ID_LOOPS, &setup.sound_loops,
15400 TRUE, "sound loops on/off"
15403 IMG_GFX_GAME_BUTTON_SOUND_SIMPLE, &game.button.sound_simple,
15404 SOUND_CTRL_ID_SIMPLE, &setup.sound_simple,
15405 TRUE, "normal sounds on/off"
15408 IMG_GFX_GAME_BUTTON_PANEL_SOUND_MUSIC, &game.button.panel_sound_music,
15409 SOUND_CTRL_ID_PANEL_MUSIC, &setup.sound_music,
15410 FALSE, "background music on/off"
15413 IMG_GFX_GAME_BUTTON_PANEL_SOUND_LOOPS, &game.button.panel_sound_loops,
15414 SOUND_CTRL_ID_PANEL_LOOPS, &setup.sound_loops,
15415 FALSE, "sound loops on/off"
15418 IMG_GFX_GAME_BUTTON_PANEL_SOUND_SIMPLE, &game.button.panel_sound_simple,
15419 SOUND_CTRL_ID_PANEL_SIMPLE, &setup.sound_simple,
15420 FALSE, "normal sounds on/off"
15424 void CreateGameButtons()
15428 for (i = 0; i < NUM_GAME_BUTTONS; i++)
15430 struct GraphicInfo *gfx = &graphic_info[gamebutton_info[i].graphic];
15431 struct XY *pos = gamebutton_info[i].pos;
15432 struct GadgetInfo *gi;
15435 unsigned int event_mask;
15436 boolean allowed_on_tape = gamebutton_info[i].allowed_on_tape;
15437 boolean on_tape = (tape.show_game_buttons && allowed_on_tape);
15438 int base_x = (on_tape ? VX : DX);
15439 int base_y = (on_tape ? VY : DY);
15440 int gd_x = gfx->src_x;
15441 int gd_y = gfx->src_y;
15442 int gd_xp = gfx->src_x + gfx->pressed_xoffset;
15443 int gd_yp = gfx->src_y + gfx->pressed_yoffset;
15444 int gd_xa = gfx->src_x + gfx->active_xoffset;
15445 int gd_ya = gfx->src_y + gfx->active_yoffset;
15446 int gd_xap = gfx->src_x + gfx->active_xoffset + gfx->pressed_xoffset;
15447 int gd_yap = gfx->src_y + gfx->active_yoffset + gfx->pressed_yoffset;
15450 if (gfx->bitmap == NULL)
15452 game_gadget[id] = NULL;
15457 if (id == GAME_CTRL_ID_STOP ||
15458 id == GAME_CTRL_ID_PANEL_STOP ||
15459 id == GAME_CTRL_ID_PLAY ||
15460 id == GAME_CTRL_ID_PANEL_PLAY ||
15461 id == GAME_CTRL_ID_SAVE ||
15462 id == GAME_CTRL_ID_LOAD)
15464 button_type = GD_TYPE_NORMAL_BUTTON;
15466 event_mask = GD_EVENT_RELEASED;
15468 else if (id == GAME_CTRL_ID_UNDO ||
15469 id == GAME_CTRL_ID_REDO)
15471 button_type = GD_TYPE_NORMAL_BUTTON;
15473 event_mask = GD_EVENT_PRESSED | GD_EVENT_REPEATED;
15477 button_type = GD_TYPE_CHECK_BUTTON;
15478 checked = (gamebutton_info[i].setup_value != NULL ?
15479 *gamebutton_info[i].setup_value : FALSE);
15480 event_mask = GD_EVENT_PRESSED;
15483 gi = CreateGadget(GDI_CUSTOM_ID, id,
15484 GDI_INFO_TEXT, gamebutton_info[i].infotext,
15485 GDI_X, base_x + GDI_ACTIVE_POS(pos->x),
15486 GDI_Y, base_y + GDI_ACTIVE_POS(pos->y),
15487 GDI_WIDTH, gfx->width,
15488 GDI_HEIGHT, gfx->height,
15489 GDI_TYPE, button_type,
15490 GDI_STATE, GD_BUTTON_UNPRESSED,
15491 GDI_CHECKED, checked,
15492 GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
15493 GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
15494 GDI_ALT_DESIGN_UNPRESSED, gfx->bitmap, gd_xa, gd_ya,
15495 GDI_ALT_DESIGN_PRESSED, gfx->bitmap, gd_xap, gd_yap,
15496 GDI_DIRECT_DRAW, FALSE,
15497 GDI_EVENT_MASK, event_mask,
15498 GDI_CALLBACK_ACTION, HandleGameButtons,
15502 Error(ERR_EXIT, "cannot create gadget");
15504 game_gadget[id] = gi;
15508 void FreeGameButtons()
15512 for (i = 0; i < NUM_GAME_BUTTONS; i++)
15513 FreeGadget(game_gadget[i]);
15516 static void UnmapGameButtonsAtSamePosition(int id)
15520 for (i = 0; i < NUM_GAME_BUTTONS; i++)
15522 gamebutton_info[i].pos->x == gamebutton_info[id].pos->x &&
15523 gamebutton_info[i].pos->y == gamebutton_info[id].pos->y)
15524 UnmapGadget(game_gadget[i]);
15527 static void UnmapGameButtonsAtSamePosition_All()
15529 if (setup.show_snapshot_buttons)
15531 UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_SAVE);
15532 UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE2);
15533 UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_LOAD);
15537 UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_STOP);
15538 UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE);
15539 UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PLAY);
15541 UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_STOP);
15542 UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_PAUSE);
15543 UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_PLAY);
15547 static void MapGameButtonsAtSamePosition(int id)
15551 for (i = 0; i < NUM_GAME_BUTTONS; i++)
15553 gamebutton_info[i].pos->x == gamebutton_info[id].pos->x &&
15554 gamebutton_info[i].pos->y == gamebutton_info[id].pos->y)
15555 MapGadget(game_gadget[i]);
15557 UnmapGameButtonsAtSamePosition_All();
15560 void MapUndoRedoButtons()
15562 UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
15563 UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
15565 MapGadget(game_gadget[GAME_CTRL_ID_UNDO]);
15566 MapGadget(game_gadget[GAME_CTRL_ID_REDO]);
15568 ModifyGadget(game_gadget[GAME_CTRL_ID_PAUSE2], GDI_CHECKED, TRUE, GDI_END);
15571 void UnmapUndoRedoButtons()
15573 UnmapGadget(game_gadget[GAME_CTRL_ID_UNDO]);
15574 UnmapGadget(game_gadget[GAME_CTRL_ID_REDO]);
15576 MapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
15577 MapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
15579 ModifyGadget(game_gadget[GAME_CTRL_ID_PAUSE2], GDI_CHECKED, FALSE, GDI_END);
15582 void MapGameButtonsExt(boolean on_tape)
15586 for (i = 0; i < NUM_GAME_BUTTONS; i++)
15587 if ((!on_tape || gamebutton_info[i].allowed_on_tape) &&
15588 i != GAME_CTRL_ID_UNDO &&
15589 i != GAME_CTRL_ID_REDO)
15590 MapGadget(game_gadget[i]);
15592 UnmapGameButtonsAtSamePosition_All();
15594 RedrawGameButtons();
15597 void UnmapGameButtonsExt(boolean on_tape)
15601 for (i = 0; i < NUM_GAME_BUTTONS; i++)
15602 if (!on_tape || gamebutton_info[i].allowed_on_tape)
15603 UnmapGadget(game_gadget[i]);
15606 void RedrawGameButtonsExt(boolean on_tape)
15610 for (i = 0; i < NUM_GAME_BUTTONS; i++)
15611 if (!on_tape || gamebutton_info[i].allowed_on_tape)
15612 RedrawGadget(game_gadget[i]);
15614 // RedrawGadget() may have set REDRAW_ALL if buttons are defined off-area
15615 redraw_mask &= ~REDRAW_ALL;
15618 void SetGadgetState(struct GadgetInfo *gi, boolean state)
15623 gi->checked = state;
15626 void RedrawSoundButtonGadget(int id)
15628 int id2 = (id == SOUND_CTRL_ID_MUSIC ? SOUND_CTRL_ID_PANEL_MUSIC :
15629 id == SOUND_CTRL_ID_LOOPS ? SOUND_CTRL_ID_PANEL_LOOPS :
15630 id == SOUND_CTRL_ID_SIMPLE ? SOUND_CTRL_ID_PANEL_SIMPLE :
15631 id == SOUND_CTRL_ID_PANEL_MUSIC ? SOUND_CTRL_ID_MUSIC :
15632 id == SOUND_CTRL_ID_PANEL_LOOPS ? SOUND_CTRL_ID_LOOPS :
15633 id == SOUND_CTRL_ID_PANEL_SIMPLE ? SOUND_CTRL_ID_SIMPLE :
15636 SetGadgetState(game_gadget[id2], *gamebutton_info[id2].setup_value);
15637 RedrawGadget(game_gadget[id2]);
15640 void MapGameButtons()
15642 MapGameButtonsExt(FALSE);
15645 void UnmapGameButtons()
15647 UnmapGameButtonsExt(FALSE);
15650 void RedrawGameButtons()
15652 RedrawGameButtonsExt(FALSE);
15655 void MapGameButtonsOnTape()
15657 MapGameButtonsExt(TRUE);
15660 void UnmapGameButtonsOnTape()
15662 UnmapGameButtonsExt(TRUE);
15665 void RedrawGameButtonsOnTape()
15667 RedrawGameButtonsExt(TRUE);
15670 void GameUndoRedoExt()
15672 ClearPlayerAction();
15674 tape.pausing = TRUE;
15677 UpdateAndDisplayGameControlValues();
15679 DrawCompleteVideoDisplay();
15680 DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
15681 DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
15682 DrawVideoDisplay(VIDEO_STATE_1STEP(tape.single_step), 0);
15687 void GameUndo(int steps)
15689 if (!CheckEngineSnapshotList())
15692 LoadEngineSnapshot_Undo(steps);
15697 void GameRedo(int steps)
15699 if (!CheckEngineSnapshotList())
15702 LoadEngineSnapshot_Redo(steps);
15707 static void HandleGameButtonsExt(int id, int button)
15709 static boolean game_undo_executed = FALSE;
15710 int steps = BUTTON_STEPSIZE(button);
15711 boolean handle_game_buttons =
15712 (game_status == GAME_MODE_PLAYING ||
15713 (game_status == GAME_MODE_MAIN && tape.show_game_buttons));
15715 if (!handle_game_buttons)
15720 case GAME_CTRL_ID_STOP:
15721 case GAME_CTRL_ID_PANEL_STOP:
15722 if (game_status == GAME_MODE_MAIN)
15728 RequestQuitGame(TRUE);
15732 case GAME_CTRL_ID_PAUSE:
15733 case GAME_CTRL_ID_PAUSE2:
15734 case GAME_CTRL_ID_PANEL_PAUSE:
15735 if (options.network && game_status == GAME_MODE_PLAYING)
15737 #if defined(NETWORK_AVALIABLE)
15739 SendToServer_ContinuePlaying();
15741 SendToServer_PausePlaying();
15745 TapeTogglePause(TAPE_TOGGLE_MANUAL);
15747 game_undo_executed = FALSE;
15751 case GAME_CTRL_ID_PLAY:
15752 case GAME_CTRL_ID_PANEL_PLAY:
15753 if (game_status == GAME_MODE_MAIN)
15755 StartGameActions(options.network, setup.autorecord, level.random_seed);
15757 else if (tape.pausing)
15759 #if defined(NETWORK_AVALIABLE)
15760 if (options.network)
15761 SendToServer_ContinuePlaying();
15764 TapeTogglePause(TAPE_TOGGLE_MANUAL | TAPE_TOGGLE_PLAY_PAUSE);
15768 case GAME_CTRL_ID_UNDO:
15769 // Important: When using "save snapshot when collecting an item" mode,
15770 // load last (current) snapshot for first "undo" after pressing "pause"
15771 // (else the last-but-one snapshot would be loaded, because the snapshot
15772 // pointer already points to the last snapshot when pressing "pause",
15773 // which is fine for "every step/move" mode, but not for "every collect")
15774 if (game.snapshot.mode == SNAPSHOT_MODE_EVERY_COLLECT &&
15775 !game_undo_executed)
15778 game_undo_executed = TRUE;
15783 case GAME_CTRL_ID_REDO:
15787 case GAME_CTRL_ID_SAVE:
15791 case GAME_CTRL_ID_LOAD:
15795 case SOUND_CTRL_ID_MUSIC:
15796 case SOUND_CTRL_ID_PANEL_MUSIC:
15797 if (setup.sound_music)
15799 setup.sound_music = FALSE;
15803 else if (audio.music_available)
15805 setup.sound = setup.sound_music = TRUE;
15807 SetAudioMode(setup.sound);
15809 if (game_status == GAME_MODE_PLAYING)
15813 RedrawSoundButtonGadget(id);
15817 case SOUND_CTRL_ID_LOOPS:
15818 case SOUND_CTRL_ID_PANEL_LOOPS:
15819 if (setup.sound_loops)
15820 setup.sound_loops = FALSE;
15821 else if (audio.loops_available)
15823 setup.sound = setup.sound_loops = TRUE;
15825 SetAudioMode(setup.sound);
15828 RedrawSoundButtonGadget(id);
15832 case SOUND_CTRL_ID_SIMPLE:
15833 case SOUND_CTRL_ID_PANEL_SIMPLE:
15834 if (setup.sound_simple)
15835 setup.sound_simple = FALSE;
15836 else if (audio.sound_available)
15838 setup.sound = setup.sound_simple = TRUE;
15840 SetAudioMode(setup.sound);
15843 RedrawSoundButtonGadget(id);
15852 static void HandleGameButtons(struct GadgetInfo *gi)
15854 HandleGameButtonsExt(gi->custom_id, gi->event.button);
15857 void HandleSoundButtonKeys(Key key)
15859 if (key == setup.shortcut.sound_simple)
15860 ClickOnGadget(game_gadget[SOUND_CTRL_ID_SIMPLE], MB_LEFTBUTTON);
15861 else if (key == setup.shortcut.sound_loops)
15862 ClickOnGadget(game_gadget[SOUND_CTRL_ID_LOOPS], MB_LEFTBUTTON);
15863 else if (key == setup.shortcut.sound_music)
15864 ClickOnGadget(game_gadget[SOUND_CTRL_ID_MUSIC], MB_LEFTBUTTON);