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_FRAME 35
126 #define GAME_PANEL_SHIELD_NORMAL 36
127 #define GAME_PANEL_SHIELD_NORMAL_TIME 37
128 #define GAME_PANEL_SHIELD_DEADLY 38
129 #define GAME_PANEL_SHIELD_DEADLY_TIME 39
130 #define GAME_PANEL_EXIT 40
131 #define GAME_PANEL_EMC_MAGIC_BALL 41
132 #define GAME_PANEL_EMC_MAGIC_BALL_SWITCH 42
133 #define GAME_PANEL_LIGHT_SWITCH 43
134 #define GAME_PANEL_LIGHT_SWITCH_TIME 44
135 #define GAME_PANEL_TIMEGATE_SWITCH 45
136 #define GAME_PANEL_TIMEGATE_SWITCH_TIME 46
137 #define GAME_PANEL_SWITCHGATE_SWITCH 47
138 #define GAME_PANEL_EMC_LENSES 48
139 #define GAME_PANEL_EMC_LENSES_TIME 49
140 #define GAME_PANEL_EMC_MAGNIFIER 50
141 #define GAME_PANEL_EMC_MAGNIFIER_TIME 51
142 #define GAME_PANEL_BALLOON_SWITCH 52
143 #define GAME_PANEL_DYNABOMB_NUMBER 53
144 #define GAME_PANEL_DYNABOMB_SIZE 54
145 #define GAME_PANEL_DYNABOMB_POWER 55
146 #define GAME_PANEL_PENGUINS 56
147 #define GAME_PANEL_SOKOBAN_OBJECTS 57
148 #define GAME_PANEL_SOKOBAN_FIELDS 58
149 #define GAME_PANEL_ROBOT_WHEEL 59
150 #define GAME_PANEL_CONVEYOR_BELT_1 60
151 #define GAME_PANEL_CONVEYOR_BELT_2 61
152 #define GAME_PANEL_CONVEYOR_BELT_3 62
153 #define GAME_PANEL_CONVEYOR_BELT_4 63
154 #define GAME_PANEL_CONVEYOR_BELT_1_SWITCH 64
155 #define GAME_PANEL_CONVEYOR_BELT_2_SWITCH 65
156 #define GAME_PANEL_CONVEYOR_BELT_3_SWITCH 66
157 #define GAME_PANEL_CONVEYOR_BELT_4_SWITCH 67
158 #define GAME_PANEL_MAGIC_WALL 68
159 #define GAME_PANEL_MAGIC_WALL_TIME 69
160 #define GAME_PANEL_GRAVITY_STATE 70
161 #define GAME_PANEL_GRAPHIC_1 71
162 #define GAME_PANEL_GRAPHIC_2 72
163 #define GAME_PANEL_GRAPHIC_3 73
164 #define GAME_PANEL_GRAPHIC_4 74
165 #define GAME_PANEL_GRAPHIC_5 75
166 #define GAME_PANEL_GRAPHIC_6 76
167 #define GAME_PANEL_GRAPHIC_7 77
168 #define GAME_PANEL_GRAPHIC_8 78
169 #define GAME_PANEL_ELEMENT_1 79
170 #define GAME_PANEL_ELEMENT_2 80
171 #define GAME_PANEL_ELEMENT_3 81
172 #define GAME_PANEL_ELEMENT_4 82
173 #define GAME_PANEL_ELEMENT_5 83
174 #define GAME_PANEL_ELEMENT_6 84
175 #define GAME_PANEL_ELEMENT_7 85
176 #define GAME_PANEL_ELEMENT_8 86
177 #define GAME_PANEL_ELEMENT_COUNT_1 87
178 #define GAME_PANEL_ELEMENT_COUNT_2 88
179 #define GAME_PANEL_ELEMENT_COUNT_3 89
180 #define GAME_PANEL_ELEMENT_COUNT_4 90
181 #define GAME_PANEL_ELEMENT_COUNT_5 91
182 #define GAME_PANEL_ELEMENT_COUNT_6 92
183 #define GAME_PANEL_ELEMENT_COUNT_7 93
184 #define GAME_PANEL_ELEMENT_COUNT_8 94
185 #define GAME_PANEL_CE_SCORE_1 95
186 #define GAME_PANEL_CE_SCORE_2 96
187 #define GAME_PANEL_CE_SCORE_3 97
188 #define GAME_PANEL_CE_SCORE_4 98
189 #define GAME_PANEL_CE_SCORE_5 99
190 #define GAME_PANEL_CE_SCORE_6 100
191 #define GAME_PANEL_CE_SCORE_7 101
192 #define GAME_PANEL_CE_SCORE_8 102
193 #define GAME_PANEL_CE_SCORE_1_ELEMENT 103
194 #define GAME_PANEL_CE_SCORE_2_ELEMENT 104
195 #define GAME_PANEL_CE_SCORE_3_ELEMENT 105
196 #define GAME_PANEL_CE_SCORE_4_ELEMENT 106
197 #define GAME_PANEL_CE_SCORE_5_ELEMENT 107
198 #define GAME_PANEL_CE_SCORE_6_ELEMENT 108
199 #define GAME_PANEL_CE_SCORE_7_ELEMENT 109
200 #define GAME_PANEL_CE_SCORE_8_ELEMENT 110
201 #define GAME_PANEL_PLAYER_NAME 111
202 #define GAME_PANEL_LEVEL_NAME 112
203 #define GAME_PANEL_LEVEL_AUTHOR 113
205 #define NUM_GAME_PANEL_CONTROLS 114
207 struct GamePanelOrderInfo
213 static struct GamePanelOrderInfo game_panel_order[NUM_GAME_PANEL_CONTROLS];
215 struct GamePanelControlInfo
219 struct TextPosInfo *pos;
222 int value, last_value;
223 int frame, last_frame;
228 static struct GamePanelControlInfo game_panel_controls[] =
231 GAME_PANEL_LEVEL_NUMBER,
232 &game.panel.level_number,
241 GAME_PANEL_INVENTORY_COUNT,
242 &game.panel.inventory_count,
246 GAME_PANEL_INVENTORY_FIRST_1,
247 &game.panel.inventory_first[0],
251 GAME_PANEL_INVENTORY_FIRST_2,
252 &game.panel.inventory_first[1],
256 GAME_PANEL_INVENTORY_FIRST_3,
257 &game.panel.inventory_first[2],
261 GAME_PANEL_INVENTORY_FIRST_4,
262 &game.panel.inventory_first[3],
266 GAME_PANEL_INVENTORY_FIRST_5,
267 &game.panel.inventory_first[4],
271 GAME_PANEL_INVENTORY_FIRST_6,
272 &game.panel.inventory_first[5],
276 GAME_PANEL_INVENTORY_FIRST_7,
277 &game.panel.inventory_first[6],
281 GAME_PANEL_INVENTORY_FIRST_8,
282 &game.panel.inventory_first[7],
286 GAME_PANEL_INVENTORY_LAST_1,
287 &game.panel.inventory_last[0],
291 GAME_PANEL_INVENTORY_LAST_2,
292 &game.panel.inventory_last[1],
296 GAME_PANEL_INVENTORY_LAST_3,
297 &game.panel.inventory_last[2],
301 GAME_PANEL_INVENTORY_LAST_4,
302 &game.panel.inventory_last[3],
306 GAME_PANEL_INVENTORY_LAST_5,
307 &game.panel.inventory_last[4],
311 GAME_PANEL_INVENTORY_LAST_6,
312 &game.panel.inventory_last[5],
316 GAME_PANEL_INVENTORY_LAST_7,
317 &game.panel.inventory_last[6],
321 GAME_PANEL_INVENTORY_LAST_8,
322 &game.panel.inventory_last[7],
366 GAME_PANEL_KEY_WHITE,
367 &game.panel.key_white,
371 GAME_PANEL_KEY_WHITE_COUNT,
372 &game.panel.key_white_count,
381 GAME_PANEL_HIGHSCORE,
382 &game.panel.highscore,
411 GAME_PANEL_SHIELD_NORMAL,
412 &game.panel.shield_normal,
416 GAME_PANEL_SHIELD_NORMAL_TIME,
417 &game.panel.shield_normal_time,
421 GAME_PANEL_SHIELD_DEADLY,
422 &game.panel.shield_deadly,
426 GAME_PANEL_SHIELD_DEADLY_TIME,
427 &game.panel.shield_deadly_time,
436 GAME_PANEL_EMC_MAGIC_BALL,
437 &game.panel.emc_magic_ball,
441 GAME_PANEL_EMC_MAGIC_BALL_SWITCH,
442 &game.panel.emc_magic_ball_switch,
446 GAME_PANEL_LIGHT_SWITCH,
447 &game.panel.light_switch,
451 GAME_PANEL_LIGHT_SWITCH_TIME,
452 &game.panel.light_switch_time,
456 GAME_PANEL_TIMEGATE_SWITCH,
457 &game.panel.timegate_switch,
461 GAME_PANEL_TIMEGATE_SWITCH_TIME,
462 &game.panel.timegate_switch_time,
466 GAME_PANEL_SWITCHGATE_SWITCH,
467 &game.panel.switchgate_switch,
471 GAME_PANEL_EMC_LENSES,
472 &game.panel.emc_lenses,
476 GAME_PANEL_EMC_LENSES_TIME,
477 &game.panel.emc_lenses_time,
481 GAME_PANEL_EMC_MAGNIFIER,
482 &game.panel.emc_magnifier,
486 GAME_PANEL_EMC_MAGNIFIER_TIME,
487 &game.panel.emc_magnifier_time,
491 GAME_PANEL_BALLOON_SWITCH,
492 &game.panel.balloon_switch,
496 GAME_PANEL_DYNABOMB_NUMBER,
497 &game.panel.dynabomb_number,
501 GAME_PANEL_DYNABOMB_SIZE,
502 &game.panel.dynabomb_size,
506 GAME_PANEL_DYNABOMB_POWER,
507 &game.panel.dynabomb_power,
512 &game.panel.penguins,
516 GAME_PANEL_SOKOBAN_OBJECTS,
517 &game.panel.sokoban_objects,
521 GAME_PANEL_SOKOBAN_FIELDS,
522 &game.panel.sokoban_fields,
526 GAME_PANEL_ROBOT_WHEEL,
527 &game.panel.robot_wheel,
531 GAME_PANEL_CONVEYOR_BELT_1,
532 &game.panel.conveyor_belt[0],
536 GAME_PANEL_CONVEYOR_BELT_2,
537 &game.panel.conveyor_belt[1],
541 GAME_PANEL_CONVEYOR_BELT_3,
542 &game.panel.conveyor_belt[2],
546 GAME_PANEL_CONVEYOR_BELT_4,
547 &game.panel.conveyor_belt[3],
551 GAME_PANEL_CONVEYOR_BELT_1_SWITCH,
552 &game.panel.conveyor_belt_switch[0],
556 GAME_PANEL_CONVEYOR_BELT_2_SWITCH,
557 &game.panel.conveyor_belt_switch[1],
561 GAME_PANEL_CONVEYOR_BELT_3_SWITCH,
562 &game.panel.conveyor_belt_switch[2],
566 GAME_PANEL_CONVEYOR_BELT_4_SWITCH,
567 &game.panel.conveyor_belt_switch[3],
571 GAME_PANEL_MAGIC_WALL,
572 &game.panel.magic_wall,
576 GAME_PANEL_MAGIC_WALL_TIME,
577 &game.panel.magic_wall_time,
581 GAME_PANEL_GRAVITY_STATE,
582 &game.panel.gravity_state,
586 GAME_PANEL_GRAPHIC_1,
587 &game.panel.graphic[0],
591 GAME_PANEL_GRAPHIC_2,
592 &game.panel.graphic[1],
596 GAME_PANEL_GRAPHIC_3,
597 &game.panel.graphic[2],
601 GAME_PANEL_GRAPHIC_4,
602 &game.panel.graphic[3],
606 GAME_PANEL_GRAPHIC_5,
607 &game.panel.graphic[4],
611 GAME_PANEL_GRAPHIC_6,
612 &game.panel.graphic[5],
616 GAME_PANEL_GRAPHIC_7,
617 &game.panel.graphic[6],
621 GAME_PANEL_GRAPHIC_8,
622 &game.panel.graphic[7],
626 GAME_PANEL_ELEMENT_1,
627 &game.panel.element[0],
631 GAME_PANEL_ELEMENT_2,
632 &game.panel.element[1],
636 GAME_PANEL_ELEMENT_3,
637 &game.panel.element[2],
641 GAME_PANEL_ELEMENT_4,
642 &game.panel.element[3],
646 GAME_PANEL_ELEMENT_5,
647 &game.panel.element[4],
651 GAME_PANEL_ELEMENT_6,
652 &game.panel.element[5],
656 GAME_PANEL_ELEMENT_7,
657 &game.panel.element[6],
661 GAME_PANEL_ELEMENT_8,
662 &game.panel.element[7],
666 GAME_PANEL_ELEMENT_COUNT_1,
667 &game.panel.element_count[0],
671 GAME_PANEL_ELEMENT_COUNT_2,
672 &game.panel.element_count[1],
676 GAME_PANEL_ELEMENT_COUNT_3,
677 &game.panel.element_count[2],
681 GAME_PANEL_ELEMENT_COUNT_4,
682 &game.panel.element_count[3],
686 GAME_PANEL_ELEMENT_COUNT_5,
687 &game.panel.element_count[4],
691 GAME_PANEL_ELEMENT_COUNT_6,
692 &game.panel.element_count[5],
696 GAME_PANEL_ELEMENT_COUNT_7,
697 &game.panel.element_count[6],
701 GAME_PANEL_ELEMENT_COUNT_8,
702 &game.panel.element_count[7],
706 GAME_PANEL_CE_SCORE_1,
707 &game.panel.ce_score[0],
711 GAME_PANEL_CE_SCORE_2,
712 &game.panel.ce_score[1],
716 GAME_PANEL_CE_SCORE_3,
717 &game.panel.ce_score[2],
721 GAME_PANEL_CE_SCORE_4,
722 &game.panel.ce_score[3],
726 GAME_PANEL_CE_SCORE_5,
727 &game.panel.ce_score[4],
731 GAME_PANEL_CE_SCORE_6,
732 &game.panel.ce_score[5],
736 GAME_PANEL_CE_SCORE_7,
737 &game.panel.ce_score[6],
741 GAME_PANEL_CE_SCORE_8,
742 &game.panel.ce_score[7],
746 GAME_PANEL_CE_SCORE_1_ELEMENT,
747 &game.panel.ce_score_element[0],
751 GAME_PANEL_CE_SCORE_2_ELEMENT,
752 &game.panel.ce_score_element[1],
756 GAME_PANEL_CE_SCORE_3_ELEMENT,
757 &game.panel.ce_score_element[2],
761 GAME_PANEL_CE_SCORE_4_ELEMENT,
762 &game.panel.ce_score_element[3],
766 GAME_PANEL_CE_SCORE_5_ELEMENT,
767 &game.panel.ce_score_element[4],
771 GAME_PANEL_CE_SCORE_6_ELEMENT,
772 &game.panel.ce_score_element[5],
776 GAME_PANEL_CE_SCORE_7_ELEMENT,
777 &game.panel.ce_score_element[6],
781 GAME_PANEL_CE_SCORE_8_ELEMENT,
782 &game.panel.ce_score_element[7],
786 GAME_PANEL_PLAYER_NAME,
787 &game.panel.player_name,
791 GAME_PANEL_LEVEL_NAME,
792 &game.panel.level_name,
796 GAME_PANEL_LEVEL_AUTHOR,
797 &game.panel.level_author,
808 /* values for delayed check of falling and moving elements and for collision */
809 #define CHECK_DELAY_MOVING 3
810 #define CHECK_DELAY_FALLING CHECK_DELAY_MOVING
811 #define CHECK_DELAY_COLLISION 2
812 #define CHECK_DELAY_IMPACT CHECK_DELAY_COLLISION
814 /* values for initial player move delay (initial delay counter value) */
815 #define INITIAL_MOVE_DELAY_OFF -1
816 #define INITIAL_MOVE_DELAY_ON 0
818 /* values for player movement speed (which is in fact a delay value) */
819 #define MOVE_DELAY_MIN_SPEED 32
820 #define MOVE_DELAY_NORMAL_SPEED 8
821 #define MOVE_DELAY_HIGH_SPEED 4
822 #define MOVE_DELAY_MAX_SPEED 1
824 #define DOUBLE_MOVE_DELAY(x) (x = (x < MOVE_DELAY_MIN_SPEED ? x * 2 : x))
825 #define HALVE_MOVE_DELAY(x) (x = (x > MOVE_DELAY_MAX_SPEED ? x / 2 : x))
827 #define DOUBLE_PLAYER_SPEED(p) (HALVE_MOVE_DELAY( (p)->move_delay_value))
828 #define HALVE_PLAYER_SPEED(p) (DOUBLE_MOVE_DELAY((p)->move_delay_value))
830 /* values for scroll positions */
831 #define SCROLL_POSITION_X(x) ((x) < SBX_Left + MIDPOSX ? SBX_Left : \
832 (x) > SBX_Right + MIDPOSX ? SBX_Right :\
834 #define SCROLL_POSITION_Y(y) ((y) < SBY_Upper + MIDPOSY ? SBY_Upper :\
835 (y) > SBY_Lower + MIDPOSY ? SBY_Lower :\
838 /* values for other actions */
839 #define MOVE_STEPSIZE_NORMAL (TILEX / MOVE_DELAY_NORMAL_SPEED)
840 #define MOVE_STEPSIZE_MIN (1)
841 #define MOVE_STEPSIZE_MAX (TILEX)
843 #define GET_DX_FROM_DIR(d) ((d) == MV_LEFT ? -1 : (d) == MV_RIGHT ? 1 : 0)
844 #define GET_DY_FROM_DIR(d) ((d) == MV_UP ? -1 : (d) == MV_DOWN ? 1 : 0)
846 #define INIT_GFX_RANDOM() (GetSimpleRandom(1000000))
848 #define GET_NEW_PUSH_DELAY(e) ( (element_info[e].push_delay_fixed) + \
849 RND(element_info[e].push_delay_random))
850 #define GET_NEW_DROP_DELAY(e) ( (element_info[e].drop_delay_fixed) + \
851 RND(element_info[e].drop_delay_random))
852 #define GET_NEW_MOVE_DELAY(e) ( (element_info[e].move_delay_fixed) + \
853 RND(element_info[e].move_delay_random))
854 #define GET_MAX_MOVE_DELAY(e) ( (element_info[e].move_delay_fixed) + \
855 (element_info[e].move_delay_random))
856 #define GET_NEW_CE_VALUE(e) ( (element_info[e].ce_value_fixed_initial) +\
857 RND(element_info[e].ce_value_random_initial))
858 #define GET_CE_SCORE(e) ( (element_info[e].collect_score))
859 #define GET_CHANGE_DELAY(c) ( ((c)->delay_fixed * (c)->delay_frames) + \
860 RND((c)->delay_random * (c)->delay_frames))
861 #define GET_CE_DELAY_VALUE(c) ( ((c)->delay_fixed) + \
862 RND((c)->delay_random))
865 #define GET_VALID_RUNTIME_ELEMENT(e) \
866 ((e) >= NUM_RUNTIME_ELEMENTS ? EL_UNKNOWN : (e))
868 #define RESOLVED_REFERENCE_ELEMENT(be, e) \
869 ((be) + (e) - EL_SELF < EL_CUSTOM_START ? EL_CUSTOM_START : \
870 (be) + (e) - EL_SELF > EL_CUSTOM_END ? EL_CUSTOM_END : \
871 (be) + (e) - EL_SELF)
873 #define GET_PLAYER_FROM_BITS(p) \
874 (EL_PLAYER_1 + ((p) != PLAYER_BITS_ANY ? log_2(p) : 0))
876 #define GET_TARGET_ELEMENT(be, e, ch, cv, cs) \
877 ((e) == EL_TRIGGER_PLAYER ? (ch)->actual_trigger_player : \
878 (e) == EL_TRIGGER_ELEMENT ? (ch)->actual_trigger_element : \
879 (e) == EL_TRIGGER_CE_VALUE ? (ch)->actual_trigger_ce_value : \
880 (e) == EL_TRIGGER_CE_SCORE ? (ch)->actual_trigger_ce_score : \
881 (e) == EL_CURRENT_CE_VALUE ? (cv) : \
882 (e) == EL_CURRENT_CE_SCORE ? (cs) : \
883 (e) >= EL_PREV_CE_8 && (e) <= EL_NEXT_CE_8 ? \
884 RESOLVED_REFERENCE_ELEMENT(be, e) : \
887 #define CAN_GROW_INTO(e) \
888 ((e) == EL_SAND || (IS_DIGGABLE(e) && level.grow_into_diggable))
890 #define ELEMENT_CAN_ENTER_FIELD_BASE_X(x, y, condition) \
891 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
894 #define ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, condition) \
895 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
896 (CAN_MOVE_INTO_ACID(e) && \
897 Feld[x][y] == EL_ACID) || \
900 #define ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, condition) \
901 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) || \
902 (CAN_MOVE_INTO_ACID(e) && \
903 Feld[x][y] == EL_ACID) || \
906 #define ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, condition) \
907 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
909 (CAN_MOVE_INTO_ACID(e) && \
910 Feld[x][y] == EL_ACID) || \
911 (DONT_COLLIDE_WITH(e) && \
913 !PLAYER_ENEMY_PROTECTED(x, y))))
915 #define ELEMENT_CAN_ENTER_FIELD(e, x, y) \
916 ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, 0)
918 #define SATELLITE_CAN_ENTER_FIELD(x, y) \
919 ELEMENT_CAN_ENTER_FIELD_BASE_2(EL_SATELLITE, x, y, 0)
921 #define ANDROID_CAN_ENTER_FIELD(e, x, y) \
922 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, Feld[x][y] == EL_EMC_PLANT)
924 #define ANDROID_CAN_CLONE_FIELD(x, y) \
925 (IN_LEV_FIELD(x, y) && (CAN_BE_CLONED_BY_ANDROID(Feld[x][y]) || \
926 CAN_BE_CLONED_BY_ANDROID(EL_TRIGGER_ELEMENT)))
928 #define ENEMY_CAN_ENTER_FIELD(e, x, y) \
929 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
931 #define YAMYAM_CAN_ENTER_FIELD(e, x, y) \
932 ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, Feld[x][y] == EL_DIAMOND)
934 #define DARK_YAMYAM_CAN_ENTER_FIELD(e, x, y) \
935 ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x,y, IS_FOOD_DARK_YAMYAM(Feld[x][y]))
937 #define PACMAN_CAN_ENTER_FIELD(e, x, y) \
938 ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, IS_AMOEBOID(Feld[x][y]))
940 #define PIG_CAN_ENTER_FIELD(e, x, y) \
941 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, IS_FOOD_PIG(Feld[x][y]))
943 #define PENGUIN_CAN_ENTER_FIELD(e, x, y) \
944 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (Feld[x][y] == EL_EXIT_OPEN || \
945 Feld[x][y] == EL_EM_EXIT_OPEN || \
946 Feld[x][y] == EL_STEEL_EXIT_OPEN || \
947 Feld[x][y] == EL_EM_STEEL_EXIT_OPEN || \
948 IS_FOOD_PENGUIN(Feld[x][y])))
949 #define DRAGON_CAN_ENTER_FIELD(e, x, y) \
950 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
952 #define MOLE_CAN_ENTER_FIELD(e, x, y, condition) \
953 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (condition))
955 #define SPRING_CAN_ENTER_FIELD(e, x, y) \
956 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
958 #define SPRING_CAN_BUMP_FROM_FIELD(x, y) \
959 (IN_LEV_FIELD(x, y) && (Feld[x][y] == EL_EMC_SPRING_BUMPER || \
960 Feld[x][y] == EL_EMC_SPRING_BUMPER_ACTIVE))
962 #define MOVE_ENTER_EL(e) (element_info[e].move_enter_element)
964 #define CE_ENTER_FIELD_COND(e, x, y) \
965 (!IS_PLAYER(x, y) && \
966 IS_EQUAL_OR_IN_GROUP(Feld[x][y], MOVE_ENTER_EL(e)))
968 #define CUSTOM_ELEMENT_CAN_ENTER_FIELD(e, x, y) \
969 ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, CE_ENTER_FIELD_COND(e, x, y))
971 #define IN_LEV_FIELD_AND_IS_FREE(x, y) (IN_LEV_FIELD(x, y) && IS_FREE(x, y))
972 #define IN_LEV_FIELD_AND_NOT_FREE(x, y) (IN_LEV_FIELD(x, y) && !IS_FREE(x, y))
974 #define ACCESS_FROM(e, d) (element_info[e].access_direction &(d))
975 #define IS_WALKABLE_FROM(e, d) (IS_WALKABLE(e) && ACCESS_FROM(e, d))
976 #define IS_PASSABLE_FROM(e, d) (IS_PASSABLE(e) && ACCESS_FROM(e, d))
977 #define IS_ACCESSIBLE_FROM(e, d) (IS_ACCESSIBLE(e) && ACCESS_FROM(e, d))
979 /* game button identifiers */
980 #define GAME_CTRL_ID_STOP 0
981 #define GAME_CTRL_ID_PAUSE 1
982 #define GAME_CTRL_ID_PLAY 2
983 #define GAME_CTRL_ID_UNDO 3
984 #define GAME_CTRL_ID_REDO 4
985 #define GAME_CTRL_ID_SAVE 5
986 #define GAME_CTRL_ID_PAUSE2 6
987 #define GAME_CTRL_ID_LOAD 7
988 #define SOUND_CTRL_ID_MUSIC 8
989 #define SOUND_CTRL_ID_LOOPS 9
990 #define SOUND_CTRL_ID_SIMPLE 10
992 #define NUM_GAME_BUTTONS 11
995 /* forward declaration for internal use */
997 static void CreateField(int, int, int);
999 static void ResetGfxAnimation(int, int);
1001 static void SetPlayerWaiting(struct PlayerInfo *, boolean);
1002 static void AdvanceFrameAndPlayerCounters(int);
1004 static boolean MovePlayerOneStep(struct PlayerInfo *, int, int, int, int);
1005 static boolean MovePlayer(struct PlayerInfo *, int, int);
1006 static void ScrollPlayer(struct PlayerInfo *, int);
1007 static void ScrollScreen(struct PlayerInfo *, int);
1009 static int DigField(struct PlayerInfo *, int, int, int, int, int, int, int);
1010 static boolean DigFieldByCE(int, int, int);
1011 static boolean SnapField(struct PlayerInfo *, int, int);
1012 static boolean DropElement(struct PlayerInfo *);
1014 static void InitBeltMovement(void);
1015 static void CloseAllOpenTimegates(void);
1016 static void CheckGravityMovement(struct PlayerInfo *);
1017 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *);
1018 static void KillPlayerUnlessEnemyProtected(int, int);
1019 static void KillPlayerUnlessExplosionProtected(int, int);
1021 static void TestIfPlayerTouchesCustomElement(int, int);
1022 static void TestIfElementTouchesCustomElement(int, int);
1023 static void TestIfElementHitsCustomElement(int, int, int);
1025 static void HandleElementChange(int, int, int);
1026 static void ExecuteCustomElementAction(int, int, int, int);
1027 static boolean ChangeElement(int, int, int, int);
1029 static boolean CheckTriggeredElementChangeExt(int, int, int, int, int,int,int);
1030 #define CheckTriggeredElementChange(x, y, e, ev) \
1031 CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY,CH_SIDE_ANY, -1)
1032 #define CheckTriggeredElementChangeByPlayer(x, y, e, ev, p, s) \
1033 CheckTriggeredElementChangeExt(x, y, e, ev, p, s, -1)
1034 #define CheckTriggeredElementChangeBySide(x, y, e, ev, s) \
1035 CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, s, -1)
1036 #define CheckTriggeredElementChangeByPage(x, y, e, ev, p) \
1037 CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY, CH_SIDE_ANY, p)
1039 static boolean CheckElementChangeExt(int, int, int, int, int, int, int);
1040 #define CheckElementChange(x, y, e, te, ev) \
1041 CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, CH_SIDE_ANY)
1042 #define CheckElementChangeByPlayer(x, y, e, ev, p, s) \
1043 CheckElementChangeExt(x, y, e, EL_EMPTY, ev, p, s)
1044 #define CheckElementChangeBySide(x, y, e, te, ev, s) \
1045 CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, s)
1047 static void PlayLevelSound(int, int, int);
1048 static void PlayLevelSoundNearest(int, int, int);
1049 static void PlayLevelSoundAction(int, int, int);
1050 static void PlayLevelSoundElementAction(int, int, int, int);
1051 static void PlayLevelSoundElementActionIfLoop(int, int, int, int);
1052 static void PlayLevelSoundActionIfLoop(int, int, int);
1053 static void StopLevelSoundActionIfLoop(int, int, int);
1054 static void PlayLevelMusic();
1056 static void HandleGameButtons(struct GadgetInfo *);
1058 int AmoebeNachbarNr(int, int);
1059 void AmoebeUmwandeln(int, int);
1060 void ContinueMoving(int, int);
1061 void Bang(int, int);
1062 void InitMovDir(int, int);
1063 void InitAmoebaNr(int, int);
1064 int NewHiScore(void);
1066 void TestIfGoodThingHitsBadThing(int, int, int);
1067 void TestIfBadThingHitsGoodThing(int, int, int);
1068 void TestIfPlayerTouchesBadThing(int, int);
1069 void TestIfPlayerRunsIntoBadThing(int, int, int);
1070 void TestIfBadThingTouchesPlayer(int, int);
1071 void TestIfBadThingRunsIntoPlayer(int, int, int);
1072 void TestIfFriendTouchesBadThing(int, int);
1073 void TestIfBadThingTouchesFriend(int, int);
1074 void TestIfBadThingTouchesOtherBadThing(int, int);
1075 void TestIfGoodThingGetsHitByBadThing(int, int, int);
1077 void KillPlayer(struct PlayerInfo *);
1078 void BuryPlayer(struct PlayerInfo *);
1079 void RemovePlayer(struct PlayerInfo *);
1081 static int getInvisibleActiveFromInvisibleElement(int);
1082 static int getInvisibleFromInvisibleActiveElement(int);
1084 static struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS];
1086 /* for detection of endless loops, caused by custom element programming */
1087 /* (using maximal playfield width x 10 is just a rough approximation) */
1088 #define MAX_ELEMENT_CHANGE_RECURSION_DEPTH (MAX_PLAYFIELD_WIDTH * 10)
1090 #define RECURSION_LOOP_DETECTION_START(e, rc) \
1092 if (recursion_loop_detected) \
1095 if (recursion_loop_depth > MAX_ELEMENT_CHANGE_RECURSION_DEPTH) \
1097 recursion_loop_detected = TRUE; \
1098 recursion_loop_element = (e); \
1101 recursion_loop_depth++; \
1104 #define RECURSION_LOOP_DETECTION_END() \
1106 recursion_loop_depth--; \
1109 static int recursion_loop_depth;
1110 static boolean recursion_loop_detected;
1111 static boolean recursion_loop_element;
1113 static int map_player_action[MAX_PLAYERS];
1116 /* ------------------------------------------------------------------------- */
1117 /* definition of elements that automatically change to other elements after */
1118 /* a specified time, eventually calling a function when changing */
1119 /* ------------------------------------------------------------------------- */
1121 /* forward declaration for changer functions */
1122 static void InitBuggyBase(int, int);
1123 static void WarnBuggyBase(int, int);
1125 static void InitTrap(int, int);
1126 static void ActivateTrap(int, int);
1127 static void ChangeActiveTrap(int, int);
1129 static void InitRobotWheel(int, int);
1130 static void RunRobotWheel(int, int);
1131 static void StopRobotWheel(int, int);
1133 static void InitTimegateWheel(int, int);
1134 static void RunTimegateWheel(int, int);
1136 static void InitMagicBallDelay(int, int);
1137 static void ActivateMagicBall(int, int);
1139 struct ChangingElementInfo
1144 void (*pre_change_function)(int x, int y);
1145 void (*change_function)(int x, int y);
1146 void (*post_change_function)(int x, int y);
1149 static struct ChangingElementInfo change_delay_list[] =
1184 EL_STEEL_EXIT_OPENING,
1192 EL_STEEL_EXIT_CLOSING,
1193 EL_STEEL_EXIT_CLOSED,
1216 EL_EM_STEEL_EXIT_OPENING,
1217 EL_EM_STEEL_EXIT_OPEN,
1224 EL_EM_STEEL_EXIT_CLOSING,
1248 EL_SWITCHGATE_OPENING,
1256 EL_SWITCHGATE_CLOSING,
1257 EL_SWITCHGATE_CLOSED,
1264 EL_TIMEGATE_OPENING,
1272 EL_TIMEGATE_CLOSING,
1281 EL_ACID_SPLASH_LEFT,
1289 EL_ACID_SPLASH_RIGHT,
1298 EL_SP_BUGGY_BASE_ACTIVATING,
1305 EL_SP_BUGGY_BASE_ACTIVATING,
1306 EL_SP_BUGGY_BASE_ACTIVE,
1313 EL_SP_BUGGY_BASE_ACTIVE,
1337 EL_ROBOT_WHEEL_ACTIVE,
1345 EL_TIMEGATE_SWITCH_ACTIVE,
1353 EL_DC_TIMEGATE_SWITCH_ACTIVE,
1354 EL_DC_TIMEGATE_SWITCH,
1361 EL_EMC_MAGIC_BALL_ACTIVE,
1362 EL_EMC_MAGIC_BALL_ACTIVE,
1369 EL_EMC_SPRING_BUMPER_ACTIVE,
1370 EL_EMC_SPRING_BUMPER,
1377 EL_DIAGONAL_SHRINKING,
1385 EL_DIAGONAL_GROWING,
1406 int push_delay_fixed, push_delay_random;
1410 { EL_SPRING, 0, 0 },
1411 { EL_BALLOON, 0, 0 },
1413 { EL_SOKOBAN_OBJECT, 2, 0 },
1414 { EL_SOKOBAN_FIELD_FULL, 2, 0 },
1415 { EL_SATELLITE, 2, 0 },
1416 { EL_SP_DISK_YELLOW, 2, 0 },
1418 { EL_UNDEFINED, 0, 0 },
1426 move_stepsize_list[] =
1428 { EL_AMOEBA_DROP, 2 },
1429 { EL_AMOEBA_DROPPING, 2 },
1430 { EL_QUICKSAND_FILLING, 1 },
1431 { EL_QUICKSAND_EMPTYING, 1 },
1432 { EL_QUICKSAND_FAST_FILLING, 2 },
1433 { EL_QUICKSAND_FAST_EMPTYING, 2 },
1434 { EL_MAGIC_WALL_FILLING, 2 },
1435 { EL_MAGIC_WALL_EMPTYING, 2 },
1436 { EL_BD_MAGIC_WALL_FILLING, 2 },
1437 { EL_BD_MAGIC_WALL_EMPTYING, 2 },
1438 { EL_DC_MAGIC_WALL_FILLING, 2 },
1439 { EL_DC_MAGIC_WALL_EMPTYING, 2 },
1441 { EL_UNDEFINED, 0 },
1449 collect_count_list[] =
1452 { EL_BD_DIAMOND, 1 },
1453 { EL_EMERALD_YELLOW, 1 },
1454 { EL_EMERALD_RED, 1 },
1455 { EL_EMERALD_PURPLE, 1 },
1457 { EL_SP_INFOTRON, 1 },
1461 { EL_UNDEFINED, 0 },
1469 access_direction_list[] =
1471 { EL_TUBE_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1472 { EL_TUBE_VERTICAL, MV_UP | MV_DOWN },
1473 { EL_TUBE_HORIZONTAL, MV_LEFT | MV_RIGHT },
1474 { EL_TUBE_VERTICAL_LEFT, MV_LEFT | MV_UP | MV_DOWN },
1475 { EL_TUBE_VERTICAL_RIGHT, MV_RIGHT | MV_UP | MV_DOWN },
1476 { EL_TUBE_HORIZONTAL_UP, MV_LEFT | MV_RIGHT | MV_UP },
1477 { EL_TUBE_HORIZONTAL_DOWN, MV_LEFT | MV_RIGHT | MV_DOWN },
1478 { EL_TUBE_LEFT_UP, MV_LEFT | MV_UP },
1479 { EL_TUBE_LEFT_DOWN, MV_LEFT | MV_DOWN },
1480 { EL_TUBE_RIGHT_UP, MV_RIGHT | MV_UP },
1481 { EL_TUBE_RIGHT_DOWN, MV_RIGHT | MV_DOWN },
1483 { EL_SP_PORT_LEFT, MV_RIGHT },
1484 { EL_SP_PORT_RIGHT, MV_LEFT },
1485 { EL_SP_PORT_UP, MV_DOWN },
1486 { EL_SP_PORT_DOWN, MV_UP },
1487 { EL_SP_PORT_HORIZONTAL, MV_LEFT | MV_RIGHT },
1488 { EL_SP_PORT_VERTICAL, MV_UP | MV_DOWN },
1489 { EL_SP_PORT_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1490 { EL_SP_GRAVITY_PORT_LEFT, MV_RIGHT },
1491 { EL_SP_GRAVITY_PORT_RIGHT, MV_LEFT },
1492 { EL_SP_GRAVITY_PORT_UP, MV_DOWN },
1493 { EL_SP_GRAVITY_PORT_DOWN, MV_UP },
1494 { EL_SP_GRAVITY_ON_PORT_LEFT, MV_RIGHT },
1495 { EL_SP_GRAVITY_ON_PORT_RIGHT, MV_LEFT },
1496 { EL_SP_GRAVITY_ON_PORT_UP, MV_DOWN },
1497 { EL_SP_GRAVITY_ON_PORT_DOWN, MV_UP },
1498 { EL_SP_GRAVITY_OFF_PORT_LEFT, MV_RIGHT },
1499 { EL_SP_GRAVITY_OFF_PORT_RIGHT, MV_LEFT },
1500 { EL_SP_GRAVITY_OFF_PORT_UP, MV_DOWN },
1501 { EL_SP_GRAVITY_OFF_PORT_DOWN, MV_UP },
1503 { EL_UNDEFINED, MV_NONE }
1506 static boolean trigger_events[MAX_NUM_ELEMENTS][NUM_CHANGE_EVENTS];
1508 #define IS_AUTO_CHANGING(e) (element_info[e].has_change_event[CE_DELAY])
1509 #define IS_JUST_CHANGING(x, y) (ChangeDelay[x][y] != 0)
1510 #define IS_CHANGING(x, y) (IS_AUTO_CHANGING(Feld[x][y]) || \
1511 IS_JUST_CHANGING(x, y))
1513 #define CE_PAGE(e, ce) (element_info[e].event_page[ce])
1515 /* static variables for playfield scan mode (scanning forward or backward) */
1516 static int playfield_scan_start_x = 0;
1517 static int playfield_scan_start_y = 0;
1518 static int playfield_scan_delta_x = 1;
1519 static int playfield_scan_delta_y = 1;
1521 #define SCAN_PLAYFIELD(x, y) for ((y) = playfield_scan_start_y; \
1522 (y) >= 0 && (y) <= lev_fieldy - 1; \
1523 (y) += playfield_scan_delta_y) \
1524 for ((x) = playfield_scan_start_x; \
1525 (x) >= 0 && (x) <= lev_fieldx - 1; \
1526 (x) += playfield_scan_delta_x)
1529 void DEBUG_SetMaximumDynamite()
1533 for (i = 0; i < MAX_INVENTORY_SIZE; i++)
1534 if (local_player->inventory_size < MAX_INVENTORY_SIZE)
1535 local_player->inventory_element[local_player->inventory_size++] =
1540 static void InitPlayfieldScanModeVars()
1542 if (game.use_reverse_scan_direction)
1544 playfield_scan_start_x = lev_fieldx - 1;
1545 playfield_scan_start_y = lev_fieldy - 1;
1547 playfield_scan_delta_x = -1;
1548 playfield_scan_delta_y = -1;
1552 playfield_scan_start_x = 0;
1553 playfield_scan_start_y = 0;
1555 playfield_scan_delta_x = 1;
1556 playfield_scan_delta_y = 1;
1560 static void InitPlayfieldScanMode(int mode)
1562 game.use_reverse_scan_direction =
1563 (mode == CA_ARG_SCAN_MODE_REVERSE ? TRUE : FALSE);
1565 InitPlayfieldScanModeVars();
1568 static int get_move_delay_from_stepsize(int move_stepsize)
1571 MIN(MAX(MOVE_STEPSIZE_MIN, move_stepsize), MOVE_STEPSIZE_MAX);
1573 /* make sure that stepsize value is always a power of 2 */
1574 move_stepsize = (1 << log_2(move_stepsize));
1576 return TILEX / move_stepsize;
1579 static void SetPlayerMoveSpeed(struct PlayerInfo *player, int move_stepsize,
1582 int player_nr = player->index_nr;
1583 int move_delay = get_move_delay_from_stepsize(move_stepsize);
1584 boolean cannot_move = (move_stepsize == STEPSIZE_NOT_MOVING ? TRUE : FALSE);
1586 /* do no immediately change move delay -- the player might just be moving */
1587 player->move_delay_value_next = move_delay;
1589 /* information if player can move must be set separately */
1590 player->cannot_move = cannot_move;
1594 player->move_delay = game.initial_move_delay[player_nr];
1595 player->move_delay_value = game.initial_move_delay_value[player_nr];
1597 player->move_delay_value_next = -1;
1599 player->move_delay_reset_counter = 0;
1603 void GetPlayerConfig()
1605 GameFrameDelay = setup.game_frame_delay;
1607 if (!audio.sound_available)
1608 setup.sound_simple = FALSE;
1610 if (!audio.loops_available)
1611 setup.sound_loops = FALSE;
1613 if (!audio.music_available)
1614 setup.sound_music = FALSE;
1616 if (!video.fullscreen_available)
1617 setup.fullscreen = FALSE;
1619 setup.sound = (setup.sound_simple || setup.sound_loops || setup.sound_music);
1621 SetAudioMode(setup.sound);
1624 int GetElementFromGroupElement(int element)
1626 if (IS_GROUP_ELEMENT(element))
1628 struct ElementGroupInfo *group = element_info[element].group;
1629 int last_anim_random_frame = gfx.anim_random_frame;
1632 if (group->choice_mode == ANIM_RANDOM)
1633 gfx.anim_random_frame = RND(group->num_elements_resolved);
1635 element_pos = getAnimationFrame(group->num_elements_resolved, 1,
1636 group->choice_mode, 0,
1639 if (group->choice_mode == ANIM_RANDOM)
1640 gfx.anim_random_frame = last_anim_random_frame;
1642 group->choice_pos++;
1644 element = group->element_resolved[element_pos];
1650 static void InitPlayerField(int x, int y, int element, boolean init_game)
1652 if (element == EL_SP_MURPHY)
1656 if (stored_player[0].present)
1658 Feld[x][y] = EL_SP_MURPHY_CLONE;
1664 stored_player[0].initial_element = element;
1665 stored_player[0].use_murphy = TRUE;
1667 if (!level.use_artwork_element[0])
1668 stored_player[0].artwork_element = EL_SP_MURPHY;
1671 Feld[x][y] = EL_PLAYER_1;
1677 struct PlayerInfo *player = &stored_player[Feld[x][y] - EL_PLAYER_1];
1678 int jx = player->jx, jy = player->jy;
1680 player->present = TRUE;
1682 player->block_last_field = (element == EL_SP_MURPHY ?
1683 level.sp_block_last_field :
1684 level.block_last_field);
1686 /* ---------- initialize player's last field block delay --------------- */
1688 /* always start with reliable default value (no adjustment needed) */
1689 player->block_delay_adjustment = 0;
1691 /* special case 1: in Supaplex, Murphy blocks last field one more frame */
1692 if (player->block_last_field && element == EL_SP_MURPHY)
1693 player->block_delay_adjustment = 1;
1695 /* special case 2: in game engines before 3.1.1, blocking was different */
1696 if (game.use_block_last_field_bug)
1697 player->block_delay_adjustment = (player->block_last_field ? -1 : 1);
1699 if (!options.network || player->connected)
1701 player->active = TRUE;
1703 /* remove potentially duplicate players */
1704 if (StorePlayer[jx][jy] == Feld[x][y])
1705 StorePlayer[jx][jy] = 0;
1707 StorePlayer[x][y] = Feld[x][y];
1709 #if DEBUG_INIT_PLAYER
1712 printf("- player element %d activated", player->element_nr);
1713 printf(" (local player is %d and currently %s)\n",
1714 local_player->element_nr,
1715 local_player->active ? "active" : "not active");
1720 Feld[x][y] = EL_EMPTY;
1722 player->jx = player->last_jx = x;
1723 player->jy = player->last_jy = y;
1728 int player_nr = GET_PLAYER_NR(element);
1729 struct PlayerInfo *player = &stored_player[player_nr];
1731 if (player->active && player->killed)
1732 player->reanimated = TRUE; /* if player was just killed, reanimate him */
1736 static void InitField(int x, int y, boolean init_game)
1738 int element = Feld[x][y];
1747 InitPlayerField(x, y, element, init_game);
1750 case EL_SOKOBAN_FIELD_PLAYER:
1751 element = Feld[x][y] = EL_PLAYER_1;
1752 InitField(x, y, init_game);
1754 element = Feld[x][y] = EL_SOKOBAN_FIELD_EMPTY;
1755 InitField(x, y, init_game);
1758 case EL_SOKOBAN_FIELD_EMPTY:
1759 local_player->sokobanfields_still_needed++;
1763 if (x < lev_fieldx-1 && Feld[x+1][y] == EL_ACID)
1764 Feld[x][y] = EL_ACID_POOL_TOPLEFT;
1765 else if (x > 0 && Feld[x-1][y] == EL_ACID)
1766 Feld[x][y] = EL_ACID_POOL_TOPRIGHT;
1767 else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPLEFT)
1768 Feld[x][y] = EL_ACID_POOL_BOTTOMLEFT;
1769 else if (y > 0 && Feld[x][y-1] == EL_ACID)
1770 Feld[x][y] = EL_ACID_POOL_BOTTOM;
1771 else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPRIGHT)
1772 Feld[x][y] = EL_ACID_POOL_BOTTOMRIGHT;
1781 case EL_SPACESHIP_RIGHT:
1782 case EL_SPACESHIP_UP:
1783 case EL_SPACESHIP_LEFT:
1784 case EL_SPACESHIP_DOWN:
1785 case EL_BD_BUTTERFLY:
1786 case EL_BD_BUTTERFLY_RIGHT:
1787 case EL_BD_BUTTERFLY_UP:
1788 case EL_BD_BUTTERFLY_LEFT:
1789 case EL_BD_BUTTERFLY_DOWN:
1791 case EL_BD_FIREFLY_RIGHT:
1792 case EL_BD_FIREFLY_UP:
1793 case EL_BD_FIREFLY_LEFT:
1794 case EL_BD_FIREFLY_DOWN:
1795 case EL_PACMAN_RIGHT:
1797 case EL_PACMAN_LEFT:
1798 case EL_PACMAN_DOWN:
1800 case EL_YAMYAM_LEFT:
1801 case EL_YAMYAM_RIGHT:
1803 case EL_YAMYAM_DOWN:
1804 case EL_DARK_YAMYAM:
1807 case EL_SP_SNIKSNAK:
1808 case EL_SP_ELECTRON:
1817 case EL_AMOEBA_FULL:
1822 case EL_AMOEBA_DROP:
1823 if (y == lev_fieldy - 1)
1825 Feld[x][y] = EL_AMOEBA_GROWING;
1826 Store[x][y] = EL_AMOEBA_WET;
1830 case EL_DYNAMITE_ACTIVE:
1831 case EL_SP_DISK_RED_ACTIVE:
1832 case EL_DYNABOMB_PLAYER_1_ACTIVE:
1833 case EL_DYNABOMB_PLAYER_2_ACTIVE:
1834 case EL_DYNABOMB_PLAYER_3_ACTIVE:
1835 case EL_DYNABOMB_PLAYER_4_ACTIVE:
1836 MovDelay[x][y] = 96;
1839 case EL_EM_DYNAMITE_ACTIVE:
1840 MovDelay[x][y] = 32;
1844 local_player->lights_still_needed++;
1848 local_player->friends_still_needed++;
1853 GfxDir[x][y] = MovDir[x][y] = 1 << RND(4);
1856 case EL_CONVEYOR_BELT_1_SWITCH_LEFT:
1857 case EL_CONVEYOR_BELT_1_SWITCH_MIDDLE:
1858 case EL_CONVEYOR_BELT_1_SWITCH_RIGHT:
1859 case EL_CONVEYOR_BELT_2_SWITCH_LEFT:
1860 case EL_CONVEYOR_BELT_2_SWITCH_MIDDLE:
1861 case EL_CONVEYOR_BELT_2_SWITCH_RIGHT:
1862 case EL_CONVEYOR_BELT_3_SWITCH_LEFT:
1863 case EL_CONVEYOR_BELT_3_SWITCH_MIDDLE:
1864 case EL_CONVEYOR_BELT_3_SWITCH_RIGHT:
1865 case EL_CONVEYOR_BELT_4_SWITCH_LEFT:
1866 case EL_CONVEYOR_BELT_4_SWITCH_MIDDLE:
1867 case EL_CONVEYOR_BELT_4_SWITCH_RIGHT:
1870 int belt_nr = getBeltNrFromBeltSwitchElement(Feld[x][y]);
1871 int belt_dir = getBeltDirFromBeltSwitchElement(Feld[x][y]);
1872 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(Feld[x][y]);
1874 if (game.belt_dir_nr[belt_nr] == 3) /* initial value */
1876 game.belt_dir[belt_nr] = belt_dir;
1877 game.belt_dir_nr[belt_nr] = belt_dir_nr;
1879 else /* more than one switch -- set it like the first switch */
1881 Feld[x][y] = Feld[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
1886 case EL_LIGHT_SWITCH_ACTIVE:
1888 game.light_time_left = level.time_light * FRAMES_PER_SECOND;
1891 case EL_INVISIBLE_STEELWALL:
1892 case EL_INVISIBLE_WALL:
1893 case EL_INVISIBLE_SAND:
1894 if (game.light_time_left > 0 ||
1895 game.lenses_time_left > 0)
1896 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
1899 case EL_EMC_MAGIC_BALL:
1900 if (game.ball_state)
1901 Feld[x][y] = EL_EMC_MAGIC_BALL_ACTIVE;
1904 case EL_EMC_MAGIC_BALL_SWITCH:
1905 if (game.ball_state)
1906 Feld[x][y] = EL_EMC_MAGIC_BALL_SWITCH_ACTIVE;
1909 case EL_TRIGGER_PLAYER:
1910 case EL_TRIGGER_ELEMENT:
1911 case EL_TRIGGER_CE_VALUE:
1912 case EL_TRIGGER_CE_SCORE:
1914 case EL_ANY_ELEMENT:
1915 case EL_CURRENT_CE_VALUE:
1916 case EL_CURRENT_CE_SCORE:
1933 /* reference elements should not be used on the playfield */
1934 Feld[x][y] = EL_EMPTY;
1938 if (IS_CUSTOM_ELEMENT(element))
1940 if (CAN_MOVE(element))
1943 if (!element_info[element].use_last_ce_value || init_game)
1944 CustomValue[x][y] = GET_NEW_CE_VALUE(Feld[x][y]);
1946 else if (IS_GROUP_ELEMENT(element))
1948 Feld[x][y] = GetElementFromGroupElement(element);
1950 InitField(x, y, init_game);
1957 CheckTriggeredElementChange(x, y, element, CE_CREATION_OF_X);
1960 inline static void InitField_WithBug1(int x, int y, boolean init_game)
1962 InitField(x, y, init_game);
1964 /* not needed to call InitMovDir() -- already done by InitField()! */
1965 if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
1966 CAN_MOVE(Feld[x][y]))
1970 inline static void InitField_WithBug2(int x, int y, boolean init_game)
1972 int old_element = Feld[x][y];
1974 InitField(x, y, init_game);
1976 /* not needed to call InitMovDir() -- already done by InitField()! */
1977 if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
1978 CAN_MOVE(old_element) &&
1979 (old_element < EL_MOLE_LEFT || old_element > EL_MOLE_DOWN))
1982 /* this case is in fact a combination of not less than three bugs:
1983 first, it calls InitMovDir() for elements that can move, although this is
1984 already done by InitField(); then, it checks the element that was at this
1985 field _before_ the call to InitField() (which can change it); lastly, it
1986 was not called for "mole with direction" elements, which were treated as
1987 "cannot move" due to (fixed) wrong element initialization in "src/init.c"
1991 static int get_key_element_from_nr(int key_nr)
1993 int key_base_element = (key_nr >= STD_NUM_KEYS ? EL_EMC_KEY_5 - STD_NUM_KEYS :
1994 level.game_engine_type == GAME_ENGINE_TYPE_EM ?
1995 EL_EM_KEY_1 : EL_KEY_1);
1997 return key_base_element + key_nr;
2000 static int get_next_dropped_element(struct PlayerInfo *player)
2002 return (player->inventory_size > 0 ?
2003 player->inventory_element[player->inventory_size - 1] :
2004 player->inventory_infinite_element != EL_UNDEFINED ?
2005 player->inventory_infinite_element :
2006 player->dynabombs_left > 0 ?
2007 EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
2011 static int get_inventory_element_from_pos(struct PlayerInfo *player, int pos)
2013 /* pos >= 0: get element from bottom of the stack;
2014 pos < 0: get element from top of the stack */
2018 int min_inventory_size = -pos;
2019 int inventory_pos = player->inventory_size - min_inventory_size;
2020 int min_dynabombs_left = min_inventory_size - player->inventory_size;
2022 return (player->inventory_size >= min_inventory_size ?
2023 player->inventory_element[inventory_pos] :
2024 player->inventory_infinite_element != EL_UNDEFINED ?
2025 player->inventory_infinite_element :
2026 player->dynabombs_left >= min_dynabombs_left ?
2027 EL_DYNABOMB_PLAYER_1 + player->index_nr :
2032 int min_dynabombs_left = pos + 1;
2033 int min_inventory_size = pos + 1 - player->dynabombs_left;
2034 int inventory_pos = pos - player->dynabombs_left;
2036 return (player->inventory_infinite_element != EL_UNDEFINED ?
2037 player->inventory_infinite_element :
2038 player->dynabombs_left >= min_dynabombs_left ?
2039 EL_DYNABOMB_PLAYER_1 + player->index_nr :
2040 player->inventory_size >= min_inventory_size ?
2041 player->inventory_element[inventory_pos] :
2046 static int compareGamePanelOrderInfo(const void *object1, const void *object2)
2048 const struct GamePanelOrderInfo *gpo1 = (struct GamePanelOrderInfo *)object1;
2049 const struct GamePanelOrderInfo *gpo2 = (struct GamePanelOrderInfo *)object2;
2052 if (gpo1->sort_priority != gpo2->sort_priority)
2053 compare_result = gpo1->sort_priority - gpo2->sort_priority;
2055 compare_result = gpo1->nr - gpo2->nr;
2057 return compare_result;
2060 int getPlayerInventorySize(int player_nr)
2062 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2063 return level.native_em_level->ply[player_nr]->dynamite;
2064 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
2065 return level.native_sp_level->game_sp->red_disk_count;
2067 return stored_player[player_nr].inventory_size;
2070 void InitGameControlValues()
2074 for (i = 0; game_panel_controls[i].nr != -1; i++)
2076 struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2077 struct GamePanelOrderInfo *gpo = &game_panel_order[i];
2078 struct TextPosInfo *pos = gpc->pos;
2080 int type = gpc->type;
2084 Error(ERR_INFO, "'game_panel_controls' structure corrupted at %d", i);
2085 Error(ERR_EXIT, "this should not happen -- please debug");
2088 /* force update of game controls after initialization */
2089 gpc->value = gpc->last_value = -1;
2090 gpc->frame = gpc->last_frame = -1;
2091 gpc->gfx_frame = -1;
2093 /* determine panel value width for later calculation of alignment */
2094 if (type == TYPE_INTEGER || type == TYPE_STRING)
2096 pos->width = pos->size * getFontWidth(pos->font);
2097 pos->height = getFontHeight(pos->font);
2099 else if (type == TYPE_ELEMENT)
2101 pos->width = pos->size;
2102 pos->height = pos->size;
2105 /* fill structure for game panel draw order */
2107 gpo->sort_priority = pos->sort_priority;
2110 /* sort game panel controls according to sort_priority and control number */
2111 qsort(game_panel_order, NUM_GAME_PANEL_CONTROLS,
2112 sizeof(struct GamePanelOrderInfo), compareGamePanelOrderInfo);
2115 void UpdatePlayfieldElementCount()
2117 boolean use_element_count = FALSE;
2120 /* first check if it is needed at all to calculate playfield element count */
2121 for (i = GAME_PANEL_ELEMENT_COUNT_1; i <= GAME_PANEL_ELEMENT_COUNT_8; i++)
2122 if (!PANEL_DEACTIVATED(game_panel_controls[i].pos))
2123 use_element_count = TRUE;
2125 if (!use_element_count)
2128 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2129 element_info[i].element_count = 0;
2131 SCAN_PLAYFIELD(x, y)
2133 element_info[Feld[x][y]].element_count++;
2136 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
2137 for (j = 0; j < MAX_NUM_ELEMENTS; j++)
2138 if (IS_IN_GROUP(j, i))
2139 element_info[EL_GROUP_START + i].element_count +=
2140 element_info[j].element_count;
2143 void UpdateGameControlValues()
2146 int time = (local_player->LevelSolved ?
2147 local_player->LevelSolved_CountingTime :
2148 level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2149 level.native_em_level->lev->time :
2150 level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2151 level.native_sp_level->game_sp->time_played :
2152 game.no_time_limit ? TimePlayed : TimeLeft);
2153 int score = (local_player->LevelSolved ?
2154 local_player->LevelSolved_CountingScore :
2155 level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2156 level.native_em_level->lev->score :
2157 level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2158 level.native_sp_level->game_sp->score :
2159 local_player->score);
2160 int gems = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2161 level.native_em_level->lev->required :
2162 level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2163 level.native_sp_level->game_sp->infotrons_still_needed :
2164 local_player->gems_still_needed);
2165 int exit_closed = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2166 level.native_em_level->lev->required > 0 :
2167 level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2168 level.native_sp_level->game_sp->infotrons_still_needed > 0 :
2169 local_player->gems_still_needed > 0 ||
2170 local_player->sokobanfields_still_needed > 0 ||
2171 local_player->lights_still_needed > 0);
2173 UpdatePlayfieldElementCount();
2175 /* update game panel control values */
2177 game_panel_controls[GAME_PANEL_LEVEL_NUMBER].value = level_nr;
2178 game_panel_controls[GAME_PANEL_GEMS].value = gems;
2180 game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value = 0;
2181 for (i = 0; i < MAX_NUM_KEYS; i++)
2182 game_panel_controls[GAME_PANEL_KEY_1 + i].value = EL_EMPTY;
2183 game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_EMPTY;
2184 game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value = 0;
2186 if (game.centered_player_nr == -1)
2188 for (i = 0; i < MAX_PLAYERS; i++)
2190 /* only one player in Supaplex game engine */
2191 if (level.game_engine_type == GAME_ENGINE_TYPE_SP && i > 0)
2194 for (k = 0; k < MAX_NUM_KEYS; k++)
2196 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2198 if (level.native_em_level->ply[i]->keys & (1 << k))
2199 game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2200 get_key_element_from_nr(k);
2202 else if (stored_player[i].key[k])
2203 game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2204 get_key_element_from_nr(k);
2207 game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2208 getPlayerInventorySize(i);
2210 if (stored_player[i].num_white_keys > 0)
2211 game_panel_controls[GAME_PANEL_KEY_WHITE].value =
2214 game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2215 stored_player[i].num_white_keys;
2220 int player_nr = game.centered_player_nr;
2222 for (k = 0; k < MAX_NUM_KEYS; k++)
2224 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2226 if (level.native_em_level->ply[player_nr]->keys & (1 << k))
2227 game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2228 get_key_element_from_nr(k);
2230 else if (stored_player[player_nr].key[k])
2231 game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2232 get_key_element_from_nr(k);
2235 game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2236 getPlayerInventorySize(player_nr);
2238 if (stored_player[player_nr].num_white_keys > 0)
2239 game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_DC_KEY_WHITE;
2241 game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2242 stored_player[player_nr].num_white_keys;
2245 for (i = 0; i < NUM_PANEL_INVENTORY; i++)
2247 game_panel_controls[GAME_PANEL_INVENTORY_FIRST_1 + i].value =
2248 get_inventory_element_from_pos(local_player, i);
2249 game_panel_controls[GAME_PANEL_INVENTORY_LAST_1 + i].value =
2250 get_inventory_element_from_pos(local_player, -i - 1);
2253 game_panel_controls[GAME_PANEL_SCORE].value = score;
2254 game_panel_controls[GAME_PANEL_HIGHSCORE].value = highscore[0].Score;
2256 game_panel_controls[GAME_PANEL_TIME].value = time;
2258 game_panel_controls[GAME_PANEL_TIME_HH].value = time / 3600;
2259 game_panel_controls[GAME_PANEL_TIME_MM].value = (time / 60) % 60;
2260 game_panel_controls[GAME_PANEL_TIME_SS].value = time % 60;
2262 game_panel_controls[GAME_PANEL_FRAME].value = FrameCounter;
2264 game_panel_controls[GAME_PANEL_SHIELD_NORMAL].value =
2265 (local_player->shield_normal_time_left > 0 ? EL_SHIELD_NORMAL_ACTIVE :
2267 game_panel_controls[GAME_PANEL_SHIELD_NORMAL_TIME].value =
2268 local_player->shield_normal_time_left;
2269 game_panel_controls[GAME_PANEL_SHIELD_DEADLY].value =
2270 (local_player->shield_deadly_time_left > 0 ? EL_SHIELD_DEADLY_ACTIVE :
2272 game_panel_controls[GAME_PANEL_SHIELD_DEADLY_TIME].value =
2273 local_player->shield_deadly_time_left;
2275 game_panel_controls[GAME_PANEL_EXIT].value =
2276 (exit_closed ? EL_EXIT_CLOSED : EL_EXIT_OPEN);
2278 game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL].value =
2279 (game.ball_state ? EL_EMC_MAGIC_BALL_ACTIVE : EL_EMC_MAGIC_BALL);
2280 game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL_SWITCH].value =
2281 (game.ball_state ? EL_EMC_MAGIC_BALL_SWITCH_ACTIVE :
2282 EL_EMC_MAGIC_BALL_SWITCH);
2284 game_panel_controls[GAME_PANEL_LIGHT_SWITCH].value =
2285 (game.light_time_left > 0 ? EL_LIGHT_SWITCH_ACTIVE : EL_LIGHT_SWITCH);
2286 game_panel_controls[GAME_PANEL_LIGHT_SWITCH_TIME].value =
2287 game.light_time_left;
2289 game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH].value =
2290 (game.timegate_time_left > 0 ? EL_TIMEGATE_OPEN : EL_TIMEGATE_CLOSED);
2291 game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH_TIME].value =
2292 game.timegate_time_left;
2294 game_panel_controls[GAME_PANEL_SWITCHGATE_SWITCH].value =
2295 EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
2297 game_panel_controls[GAME_PANEL_EMC_LENSES].value =
2298 (game.lenses_time_left > 0 ? EL_EMC_LENSES : EL_EMPTY);
2299 game_panel_controls[GAME_PANEL_EMC_LENSES_TIME].value =
2300 game.lenses_time_left;
2302 game_panel_controls[GAME_PANEL_EMC_MAGNIFIER].value =
2303 (game.magnify_time_left > 0 ? EL_EMC_MAGNIFIER : EL_EMPTY);
2304 game_panel_controls[GAME_PANEL_EMC_MAGNIFIER_TIME].value =
2305 game.magnify_time_left;
2307 game_panel_controls[GAME_PANEL_BALLOON_SWITCH].value =
2308 (game.wind_direction == MV_LEFT ? EL_BALLOON_SWITCH_LEFT :
2309 game.wind_direction == MV_RIGHT ? EL_BALLOON_SWITCH_RIGHT :
2310 game.wind_direction == MV_UP ? EL_BALLOON_SWITCH_UP :
2311 game.wind_direction == MV_DOWN ? EL_BALLOON_SWITCH_DOWN :
2312 EL_BALLOON_SWITCH_NONE);
2314 game_panel_controls[GAME_PANEL_DYNABOMB_NUMBER].value =
2315 local_player->dynabomb_count;
2316 game_panel_controls[GAME_PANEL_DYNABOMB_SIZE].value =
2317 local_player->dynabomb_size;
2318 game_panel_controls[GAME_PANEL_DYNABOMB_POWER].value =
2319 (local_player->dynabomb_xl ? EL_DYNABOMB_INCREASE_POWER : EL_EMPTY);
2321 game_panel_controls[GAME_PANEL_PENGUINS].value =
2322 local_player->friends_still_needed;
2324 game_panel_controls[GAME_PANEL_SOKOBAN_OBJECTS].value =
2325 local_player->sokobanfields_still_needed;
2326 game_panel_controls[GAME_PANEL_SOKOBAN_FIELDS].value =
2327 local_player->sokobanfields_still_needed;
2329 game_panel_controls[GAME_PANEL_ROBOT_WHEEL].value =
2330 (game.robot_wheel_active ? EL_ROBOT_WHEEL_ACTIVE : EL_ROBOT_WHEEL);
2332 for (i = 0; i < NUM_BELTS; i++)
2334 game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1 + i].value =
2335 (game.belt_dir[i] != MV_NONE ? EL_CONVEYOR_BELT_1_MIDDLE_ACTIVE :
2336 EL_CONVEYOR_BELT_1_MIDDLE) + i;
2337 game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1_SWITCH + i].value =
2338 getBeltSwitchElementFromBeltNrAndBeltDir(i, game.belt_dir[i]);
2341 game_panel_controls[GAME_PANEL_MAGIC_WALL].value =
2342 (game.magic_wall_active ? EL_MAGIC_WALL_ACTIVE : EL_MAGIC_WALL);
2343 game_panel_controls[GAME_PANEL_MAGIC_WALL_TIME].value =
2344 game.magic_wall_time_left;
2346 game_panel_controls[GAME_PANEL_GRAVITY_STATE].value =
2347 local_player->gravity;
2349 for (i = 0; i < NUM_PANEL_GRAPHICS; i++)
2350 game_panel_controls[GAME_PANEL_GRAPHIC_1 + i].value = EL_GRAPHIC_1 + i;
2352 for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2353 game_panel_controls[GAME_PANEL_ELEMENT_1 + i].value =
2354 (IS_DRAWABLE_ELEMENT(game.panel.element[i].id) ?
2355 game.panel.element[i].id : EL_UNDEFINED);
2357 for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2358 game_panel_controls[GAME_PANEL_ELEMENT_COUNT_1 + i].value =
2359 (IS_VALID_ELEMENT(game.panel.element_count[i].id) ?
2360 element_info[game.panel.element_count[i].id].element_count : 0);
2362 for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2363 game_panel_controls[GAME_PANEL_CE_SCORE_1 + i].value =
2364 (IS_CUSTOM_ELEMENT(game.panel.ce_score[i].id) ?
2365 element_info[game.panel.ce_score[i].id].collect_score : 0);
2367 for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2368 game_panel_controls[GAME_PANEL_CE_SCORE_1_ELEMENT + i].value =
2369 (IS_CUSTOM_ELEMENT(game.panel.ce_score_element[i].id) ?
2370 element_info[game.panel.ce_score_element[i].id].collect_score :
2373 game_panel_controls[GAME_PANEL_PLAYER_NAME].value = 0;
2374 game_panel_controls[GAME_PANEL_LEVEL_NAME].value = 0;
2375 game_panel_controls[GAME_PANEL_LEVEL_AUTHOR].value = 0;
2377 /* update game panel control frames */
2379 for (i = 0; game_panel_controls[i].nr != -1; i++)
2381 struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2383 if (gpc->type == TYPE_ELEMENT)
2385 if (gpc->value != EL_UNDEFINED && gpc->value != EL_EMPTY)
2387 int last_anim_random_frame = gfx.anim_random_frame;
2388 int element = gpc->value;
2389 int graphic = el2panelimg(element);
2391 if (gpc->value != gpc->last_value)
2394 gpc->gfx_random = INIT_GFX_RANDOM();
2400 if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2401 IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2402 gpc->gfx_random = INIT_GFX_RANDOM();
2405 if (ANIM_MODE(graphic) == ANIM_RANDOM)
2406 gfx.anim_random_frame = gpc->gfx_random;
2408 if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
2409 gpc->gfx_frame = element_info[element].collect_score;
2411 gpc->frame = getGraphicAnimationFrame(el2panelimg(gpc->value),
2414 if (ANIM_MODE(graphic) == ANIM_RANDOM)
2415 gfx.anim_random_frame = last_anim_random_frame;
2421 void DisplayGameControlValues()
2423 boolean redraw_panel = FALSE;
2426 for (i = 0; game_panel_controls[i].nr != -1; i++)
2428 struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2430 if (PANEL_DEACTIVATED(gpc->pos))
2433 if (gpc->value == gpc->last_value &&
2434 gpc->frame == gpc->last_frame)
2437 redraw_panel = TRUE;
2443 /* copy default game door content to main double buffer */
2445 /* !!! CHECK AGAIN !!! */
2446 SetPanelBackground();
2447 // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
2448 DrawBackground(DX, DY, DXSIZE, DYSIZE);
2450 /* redraw game control buttons */
2451 RedrawGameButtons();
2453 SetGameStatus(GAME_MODE_PSEUDO_PANEL);
2455 for (i = 0; i < NUM_GAME_PANEL_CONTROLS; i++)
2457 int nr = game_panel_order[i].nr;
2458 struct GamePanelControlInfo *gpc = &game_panel_controls[nr];
2459 struct TextPosInfo *pos = gpc->pos;
2460 int type = gpc->type;
2461 int value = gpc->value;
2462 int frame = gpc->frame;
2463 int size = pos->size;
2464 int font = pos->font;
2465 boolean draw_masked = pos->draw_masked;
2466 int mask_mode = (draw_masked ? BLIT_MASKED : BLIT_OPAQUE);
2468 if (PANEL_DEACTIVATED(pos))
2471 gpc->last_value = value;
2472 gpc->last_frame = frame;
2474 if (type == TYPE_INTEGER)
2476 if (nr == GAME_PANEL_LEVEL_NUMBER ||
2477 nr == GAME_PANEL_TIME)
2479 boolean use_dynamic_size = (size == -1 ? TRUE : FALSE);
2481 if (use_dynamic_size) /* use dynamic number of digits */
2483 int value_change = (nr == GAME_PANEL_LEVEL_NUMBER ? 100 : 1000);
2484 int size1 = (nr == GAME_PANEL_LEVEL_NUMBER ? 2 : 3);
2485 int size2 = size1 + 1;
2486 int font1 = pos->font;
2487 int font2 = pos->font_alt;
2489 size = (value < value_change ? size1 : size2);
2490 font = (value < value_change ? font1 : font2);
2494 /* correct text size if "digits" is zero or less */
2496 size = strlen(int2str(value, size));
2498 /* dynamically correct text alignment */
2499 pos->width = size * getFontWidth(font);
2501 DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2502 int2str(value, size), font, mask_mode);
2504 else if (type == TYPE_ELEMENT)
2506 int element, graphic;
2510 int dst_x = PANEL_XPOS(pos);
2511 int dst_y = PANEL_YPOS(pos);
2513 if (value != EL_UNDEFINED && value != EL_EMPTY)
2516 graphic = el2panelimg(value);
2518 // printf("::: %d, '%s' [%d]\n", element, EL_NAME(element), size);
2520 if (element >= EL_GRAPHIC_1 && element <= EL_GRAPHIC_8 && size == 0)
2523 getSizedGraphicSource(graphic, frame, size, &src_bitmap,
2526 width = graphic_info[graphic].width * size / TILESIZE;
2527 height = graphic_info[graphic].height * size / TILESIZE;
2530 BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2533 BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2537 else if (type == TYPE_STRING)
2539 boolean active = (value != 0);
2540 char *state_normal = "off";
2541 char *state_active = "on";
2542 char *state = (active ? state_active : state_normal);
2543 char *s = (nr == GAME_PANEL_GRAVITY_STATE ? state :
2544 nr == GAME_PANEL_PLAYER_NAME ? setup.player_name :
2545 nr == GAME_PANEL_LEVEL_NAME ? level.name :
2546 nr == GAME_PANEL_LEVEL_AUTHOR ? level.author : NULL);
2548 if (nr == GAME_PANEL_GRAVITY_STATE)
2550 int font1 = pos->font; /* (used for normal state) */
2551 int font2 = pos->font_alt; /* (used for active state) */
2553 font = (active ? font2 : font1);
2562 /* don't truncate output if "chars" is zero or less */
2565 /* dynamically correct text alignment */
2566 pos->width = size * getFontWidth(font);
2569 s_cut = getStringCopyN(s, size);
2571 DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2572 s_cut, font, mask_mode);
2578 redraw_mask |= REDRAW_DOOR_1;
2581 SetGameStatus(GAME_MODE_PLAYING);
2584 void UpdateAndDisplayGameControlValues()
2586 if (tape.deactivate_display)
2589 UpdateGameControlValues();
2590 DisplayGameControlValues();
2593 void UpdateGameDoorValues()
2595 UpdateGameControlValues();
2598 void DrawGameDoorValues()
2600 DisplayGameControlValues();
2605 =============================================================================
2607 -----------------------------------------------------------------------------
2608 initialize game engine due to level / tape version number
2609 =============================================================================
2612 static void InitGameEngine()
2614 int i, j, k, l, x, y;
2616 /* set game engine from tape file when re-playing, else from level file */
2617 game.engine_version = (tape.playing ? tape.engine_version :
2618 level.game_version);
2620 /* set single or multi-player game mode (needed for re-playing tapes) */
2621 game.team_mode = setup.team_mode;
2625 int num_players = 0;
2627 for (i = 0; i < MAX_PLAYERS; i++)
2628 if (tape.player_participates[i])
2631 /* multi-player tapes contain input data for more than one player */
2632 game.team_mode = (num_players > 1);
2635 /* ---------------------------------------------------------------------- */
2636 /* set flags for bugs and changes according to active game engine version */
2637 /* ---------------------------------------------------------------------- */
2640 Summary of bugfix/change:
2641 Fixed handling for custom elements that change when pushed by the player.
2643 Fixed/changed in version:
2647 Before 3.1.0, custom elements that "change when pushing" changed directly
2648 after the player started pushing them (until then handled in "DigField()").
2649 Since 3.1.0, these custom elements are not changed until the "pushing"
2650 move of the element is finished (now handled in "ContinueMoving()").
2652 Affected levels/tapes:
2653 The first condition is generally needed for all levels/tapes before version
2654 3.1.0, which might use the old behaviour before it was changed; known tapes
2655 that are affected are some tapes from the level set "Walpurgis Gardens" by
2657 The second condition is an exception from the above case and is needed for
2658 the special case of tapes recorded with game (not engine!) version 3.1.0 or
2659 above (including some development versions of 3.1.0), but before it was
2660 known that this change would break tapes like the above and was fixed in
2661 3.1.1, so that the changed behaviour was active although the engine version
2662 while recording maybe was before 3.1.0. There is at least one tape that is
2663 affected by this exception, which is the tape for the one-level set "Bug
2664 Machine" by Juergen Bonhagen.
2667 game.use_change_when_pushing_bug =
2668 (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2670 tape.game_version >= VERSION_IDENT(3,1,0,0) &&
2671 tape.game_version < VERSION_IDENT(3,1,1,0)));
2674 Summary of bugfix/change:
2675 Fixed handling for blocking the field the player leaves when moving.
2677 Fixed/changed in version:
2681 Before 3.1.1, when "block last field when moving" was enabled, the field
2682 the player is leaving when moving was blocked for the time of the move,
2683 and was directly unblocked afterwards. This resulted in the last field
2684 being blocked for exactly one less than the number of frames of one player
2685 move. Additionally, even when blocking was disabled, the last field was
2686 blocked for exactly one frame.
2687 Since 3.1.1, due to changes in player movement handling, the last field
2688 is not blocked at all when blocking is disabled. When blocking is enabled,
2689 the last field is blocked for exactly the number of frames of one player
2690 move. Additionally, if the player is Murphy, the hero of Supaplex, the
2691 last field is blocked for exactly one more than the number of frames of
2694 Affected levels/tapes:
2695 (!!! yet to be determined -- probably many !!!)
2698 game.use_block_last_field_bug =
2699 (game.engine_version < VERSION_IDENT(3,1,1,0));
2701 game_em.use_single_button =
2702 (game.engine_version > VERSION_IDENT(4,0,0,2));
2704 /* ---------------------------------------------------------------------- */
2706 /* set maximal allowed number of custom element changes per game frame */
2707 game.max_num_changes_per_frame = 1;
2709 /* default scan direction: scan playfield from top/left to bottom/right */
2710 InitPlayfieldScanMode(CA_ARG_SCAN_MODE_NORMAL);
2712 /* dynamically adjust element properties according to game engine version */
2713 InitElementPropertiesEngine(game.engine_version);
2716 printf("level %d: level version == %06d\n", level_nr, level.game_version);
2717 printf(" tape version == %06d [%s] [file: %06d]\n",
2718 tape.engine_version, (tape.playing ? "PLAYING" : "RECORDING"),
2720 printf(" => game.engine_version == %06d\n", game.engine_version);
2723 /* ---------- initialize player's initial move delay --------------------- */
2725 /* dynamically adjust player properties according to level information */
2726 for (i = 0; i < MAX_PLAYERS; i++)
2727 game.initial_move_delay_value[i] =
2728 get_move_delay_from_stepsize(level.initial_player_stepsize[i]);
2730 /* dynamically adjust player properties according to game engine version */
2731 for (i = 0; i < MAX_PLAYERS; i++)
2732 game.initial_move_delay[i] =
2733 (game.engine_version <= VERSION_IDENT(2,0,1,0) ?
2734 game.initial_move_delay_value[i] : 0);
2736 /* ---------- initialize player's initial push delay --------------------- */
2738 /* dynamically adjust player properties according to game engine version */
2739 game.initial_push_delay_value =
2740 (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
2742 /* ---------- initialize changing elements ------------------------------- */
2744 /* initialize changing elements information */
2745 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2747 struct ElementInfo *ei = &element_info[i];
2749 /* this pointer might have been changed in the level editor */
2750 ei->change = &ei->change_page[0];
2752 if (!IS_CUSTOM_ELEMENT(i))
2754 ei->change->target_element = EL_EMPTY_SPACE;
2755 ei->change->delay_fixed = 0;
2756 ei->change->delay_random = 0;
2757 ei->change->delay_frames = 1;
2760 for (j = 0; j < NUM_CHANGE_EVENTS; j++)
2762 ei->has_change_event[j] = FALSE;
2764 ei->event_page_nr[j] = 0;
2765 ei->event_page[j] = &ei->change_page[0];
2769 /* add changing elements from pre-defined list */
2770 for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
2772 struct ChangingElementInfo *ch_delay = &change_delay_list[i];
2773 struct ElementInfo *ei = &element_info[ch_delay->element];
2775 ei->change->target_element = ch_delay->target_element;
2776 ei->change->delay_fixed = ch_delay->change_delay;
2778 ei->change->pre_change_function = ch_delay->pre_change_function;
2779 ei->change->change_function = ch_delay->change_function;
2780 ei->change->post_change_function = ch_delay->post_change_function;
2782 ei->change->can_change = TRUE;
2783 ei->change->can_change_or_has_action = TRUE;
2785 ei->has_change_event[CE_DELAY] = TRUE;
2787 SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE, TRUE);
2788 SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE_OR_HAS_ACTION, TRUE);
2791 /* ---------- initialize internal run-time variables --------------------- */
2793 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2795 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
2797 for (j = 0; j < ei->num_change_pages; j++)
2799 ei->change_page[j].can_change_or_has_action =
2800 (ei->change_page[j].can_change |
2801 ei->change_page[j].has_action);
2805 /* add change events from custom element configuration */
2806 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2808 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
2810 for (j = 0; j < ei->num_change_pages; j++)
2812 if (!ei->change_page[j].can_change_or_has_action)
2815 for (k = 0; k < NUM_CHANGE_EVENTS; k++)
2817 /* only add event page for the first page found with this event */
2818 if (ei->change_page[j].has_event[k] && !(ei->has_change_event[k]))
2820 ei->has_change_event[k] = TRUE;
2822 ei->event_page_nr[k] = j;
2823 ei->event_page[k] = &ei->change_page[j];
2829 /* ---------- initialize reference elements in change conditions --------- */
2831 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2833 int element = EL_CUSTOM_START + i;
2834 struct ElementInfo *ei = &element_info[element];
2836 for (j = 0; j < ei->num_change_pages; j++)
2838 int trigger_element = ei->change_page[j].initial_trigger_element;
2840 if (trigger_element >= EL_PREV_CE_8 &&
2841 trigger_element <= EL_NEXT_CE_8)
2842 trigger_element = RESOLVED_REFERENCE_ELEMENT(element, trigger_element);
2844 ei->change_page[j].trigger_element = trigger_element;
2848 /* ---------- initialize run-time trigger player and element ------------- */
2850 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2852 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
2854 for (j = 0; j < ei->num_change_pages; j++)
2856 ei->change_page[j].actual_trigger_element = EL_EMPTY;
2857 ei->change_page[j].actual_trigger_player = EL_EMPTY;
2858 ei->change_page[j].actual_trigger_player_bits = CH_PLAYER_NONE;
2859 ei->change_page[j].actual_trigger_side = CH_SIDE_NONE;
2860 ei->change_page[j].actual_trigger_ce_value = 0;
2861 ei->change_page[j].actual_trigger_ce_score = 0;
2865 /* ---------- initialize trigger events ---------------------------------- */
2867 /* initialize trigger events information */
2868 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2869 for (j = 0; j < NUM_CHANGE_EVENTS; j++)
2870 trigger_events[i][j] = FALSE;
2872 /* add trigger events from element change event properties */
2873 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2875 struct ElementInfo *ei = &element_info[i];
2877 for (j = 0; j < ei->num_change_pages; j++)
2879 if (!ei->change_page[j].can_change_or_has_action)
2882 if (ei->change_page[j].has_event[CE_BY_OTHER_ACTION])
2884 int trigger_element = ei->change_page[j].trigger_element;
2886 for (k = 0; k < NUM_CHANGE_EVENTS; k++)
2888 if (ei->change_page[j].has_event[k])
2890 if (IS_GROUP_ELEMENT(trigger_element))
2892 struct ElementGroupInfo *group =
2893 element_info[trigger_element].group;
2895 for (l = 0; l < group->num_elements_resolved; l++)
2896 trigger_events[group->element_resolved[l]][k] = TRUE;
2898 else if (trigger_element == EL_ANY_ELEMENT)
2899 for (l = 0; l < MAX_NUM_ELEMENTS; l++)
2900 trigger_events[l][k] = TRUE;
2902 trigger_events[trigger_element][k] = TRUE;
2909 /* ---------- initialize push delay -------------------------------------- */
2911 /* initialize push delay values to default */
2912 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2914 if (!IS_CUSTOM_ELEMENT(i))
2916 /* set default push delay values (corrected since version 3.0.7-1) */
2917 if (game.engine_version < VERSION_IDENT(3,0,7,1))
2919 element_info[i].push_delay_fixed = 2;
2920 element_info[i].push_delay_random = 8;
2924 element_info[i].push_delay_fixed = 8;
2925 element_info[i].push_delay_random = 8;
2930 /* set push delay value for certain elements from pre-defined list */
2931 for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
2933 int e = push_delay_list[i].element;
2935 element_info[e].push_delay_fixed = push_delay_list[i].push_delay_fixed;
2936 element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
2939 /* set push delay value for Supaplex elements for newer engine versions */
2940 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
2942 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2944 if (IS_SP_ELEMENT(i))
2946 /* set SP push delay to just enough to push under a falling zonk */
2947 int delay = (game.engine_version >= VERSION_IDENT(3,1,1,0) ? 8 : 6);
2949 element_info[i].push_delay_fixed = delay;
2950 element_info[i].push_delay_random = 0;
2955 /* ---------- initialize move stepsize ----------------------------------- */
2957 /* initialize move stepsize values to default */
2958 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2959 if (!IS_CUSTOM_ELEMENT(i))
2960 element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
2962 /* set move stepsize value for certain elements from pre-defined list */
2963 for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
2965 int e = move_stepsize_list[i].element;
2967 element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
2970 /* ---------- initialize collect score ----------------------------------- */
2972 /* initialize collect score values for custom elements from initial value */
2973 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2974 if (IS_CUSTOM_ELEMENT(i))
2975 element_info[i].collect_score = element_info[i].collect_score_initial;
2977 /* ---------- initialize collect count ----------------------------------- */
2979 /* initialize collect count values for non-custom elements */
2980 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2981 if (!IS_CUSTOM_ELEMENT(i))
2982 element_info[i].collect_count_initial = 0;
2984 /* add collect count values for all elements from pre-defined list */
2985 for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
2986 element_info[collect_count_list[i].element].collect_count_initial =
2987 collect_count_list[i].count;
2989 /* ---------- initialize access direction -------------------------------- */
2991 /* initialize access direction values to default (access from every side) */
2992 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2993 if (!IS_CUSTOM_ELEMENT(i))
2994 element_info[i].access_direction = MV_ALL_DIRECTIONS;
2996 /* set access direction value for certain elements from pre-defined list */
2997 for (i = 0; access_direction_list[i].element != EL_UNDEFINED; i++)
2998 element_info[access_direction_list[i].element].access_direction =
2999 access_direction_list[i].direction;
3001 /* ---------- initialize explosion content ------------------------------- */
3002 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3004 if (IS_CUSTOM_ELEMENT(i))
3007 for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
3009 /* (content for EL_YAMYAM set at run-time with game.yamyam_content_nr) */
3011 element_info[i].content.e[x][y] =
3012 (i == EL_PLAYER_1 ? EL_EMERALD_YELLOW :
3013 i == EL_PLAYER_2 ? EL_EMERALD_RED :
3014 i == EL_PLAYER_3 ? EL_EMERALD :
3015 i == EL_PLAYER_4 ? EL_EMERALD_PURPLE :
3016 i == EL_MOLE ? EL_EMERALD_RED :
3017 i == EL_PENGUIN ? EL_EMERALD_PURPLE :
3018 i == EL_BUG ? (x == 1 && y == 1 ? EL_DIAMOND : EL_EMERALD) :
3019 i == EL_BD_BUTTERFLY ? EL_BD_DIAMOND :
3020 i == EL_SP_ELECTRON ? EL_SP_INFOTRON :
3021 i == EL_AMOEBA_TO_DIAMOND ? level.amoeba_content :
3022 i == EL_WALL_EMERALD ? EL_EMERALD :
3023 i == EL_WALL_DIAMOND ? EL_DIAMOND :
3024 i == EL_WALL_BD_DIAMOND ? EL_BD_DIAMOND :
3025 i == EL_WALL_EMERALD_YELLOW ? EL_EMERALD_YELLOW :
3026 i == EL_WALL_EMERALD_RED ? EL_EMERALD_RED :
3027 i == EL_WALL_EMERALD_PURPLE ? EL_EMERALD_PURPLE :
3028 i == EL_WALL_PEARL ? EL_PEARL :
3029 i == EL_WALL_CRYSTAL ? EL_CRYSTAL :
3034 /* ---------- initialize recursion detection ------------------------------ */
3035 recursion_loop_depth = 0;
3036 recursion_loop_detected = FALSE;
3037 recursion_loop_element = EL_UNDEFINED;
3039 /* ---------- initialize graphics engine ---------------------------------- */
3040 game.scroll_delay_value =
3041 (game.forced_scroll_delay_value != -1 ? game.forced_scroll_delay_value :
3042 setup.scroll_delay ? setup.scroll_delay_value : 0);
3043 game.scroll_delay_value =
3044 MIN(MAX(MIN_SCROLL_DELAY, game.scroll_delay_value), MAX_SCROLL_DELAY);
3046 /* ---------- initialize game engine snapshots ---------------------------- */
3047 for (i = 0; i < MAX_PLAYERS; i++)
3048 game.snapshot.last_action[i] = 0;
3049 game.snapshot.changed_action = FALSE;
3050 game.snapshot.collected_item = FALSE;
3051 game.snapshot.mode =
3052 (strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_STEP) ?
3053 SNAPSHOT_MODE_EVERY_STEP :
3054 strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_MOVE) ?
3055 SNAPSHOT_MODE_EVERY_MOVE :
3056 strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_COLLECT) ?
3057 SNAPSHOT_MODE_EVERY_COLLECT : SNAPSHOT_MODE_OFF);
3058 game.snapshot.save_snapshot = FALSE;
3061 int get_num_special_action(int element, int action_first, int action_last)
3063 int num_special_action = 0;
3066 for (i = action_first; i <= action_last; i++)
3068 boolean found = FALSE;
3070 for (j = 0; j < NUM_DIRECTIONS; j++)
3071 if (el_act_dir2img(element, i, j) !=
3072 el_act_dir2img(element, ACTION_DEFAULT, j))
3076 num_special_action++;
3081 return num_special_action;
3086 =============================================================================
3088 -----------------------------------------------------------------------------
3089 initialize and start new game
3090 =============================================================================
3095 int full_lev_fieldx = lev_fieldx + (BorderElement != EL_EMPTY ? 2 : 0);
3096 int full_lev_fieldy = lev_fieldy + (BorderElement != EL_EMPTY ? 2 : 0);
3097 int fade_mask = REDRAW_FIELD;
3099 boolean emulate_bd = TRUE; /* unless non-BOULDERDASH elements found */
3100 boolean emulate_sb = TRUE; /* unless non-SOKOBAN elements found */
3101 boolean emulate_sp = TRUE; /* unless non-SUPAPLEX elements found */
3102 int initial_move_dir = MV_DOWN;
3105 // required here to update video display before fading (FIX THIS)
3106 DrawMaskedBorder(REDRAW_DOOR_2);
3108 if (!game.restart_level)
3109 CloseDoor(DOOR_CLOSE_1);
3111 SetGameStatus(GAME_MODE_PLAYING);
3113 if (level_editor_test_game)
3114 FadeSkipNextFadeIn();
3116 FadeSetEnterScreen();
3118 if (CheckIfGlobalBorderHasChanged())
3119 fade_mask = REDRAW_ALL;
3121 FadeSoundsAndMusic();
3123 ExpireSoundLoops(TRUE);
3127 /* needed if different viewport properties defined for playing */
3128 ChangeViewportPropertiesIfNeeded();
3132 OpenDoor(GetDoorState() | DOOR_NO_DELAY | DOOR_FORCE_REDRAW);
3134 DrawCompleteVideoDisplay();
3137 InitGameControlValues();
3139 /* don't play tapes over network */
3140 network_playing = (options.network && !tape.playing);
3142 for (i = 0; i < MAX_PLAYERS; i++)
3144 struct PlayerInfo *player = &stored_player[i];
3146 player->index_nr = i;
3147 player->index_bit = (1 << i);
3148 player->element_nr = EL_PLAYER_1 + i;
3150 player->present = FALSE;
3151 player->active = FALSE;
3152 player->mapped = FALSE;
3154 player->killed = FALSE;
3155 player->reanimated = FALSE;
3158 player->effective_action = 0;
3159 player->programmed_action = 0;
3162 player->score_final = 0;
3164 player->gems_still_needed = level.gems_needed;
3165 player->sokobanfields_still_needed = 0;
3166 player->lights_still_needed = 0;
3167 player->friends_still_needed = 0;
3169 for (j = 0; j < MAX_NUM_KEYS; j++)
3170 player->key[j] = FALSE;
3172 player->num_white_keys = 0;
3174 player->dynabomb_count = 0;
3175 player->dynabomb_size = 1;
3176 player->dynabombs_left = 0;
3177 player->dynabomb_xl = FALSE;
3179 player->MovDir = initial_move_dir;
3182 player->GfxDir = initial_move_dir;
3183 player->GfxAction = ACTION_DEFAULT;
3185 player->StepFrame = 0;
3187 player->initial_element = player->element_nr;
3188 player->artwork_element =
3189 (level.use_artwork_element[i] ? level.artwork_element[i] :
3190 player->element_nr);
3191 player->use_murphy = FALSE;
3193 player->block_last_field = FALSE; /* initialized in InitPlayerField() */
3194 player->block_delay_adjustment = 0; /* initialized in InitPlayerField() */
3196 player->gravity = level.initial_player_gravity[i];
3198 player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
3200 player->actual_frame_counter = 0;
3202 player->step_counter = 0;
3204 player->last_move_dir = initial_move_dir;
3206 player->is_active = FALSE;
3208 player->is_waiting = FALSE;
3209 player->is_moving = FALSE;
3210 player->is_auto_moving = FALSE;
3211 player->is_digging = FALSE;
3212 player->is_snapping = FALSE;
3213 player->is_collecting = FALSE;
3214 player->is_pushing = FALSE;
3215 player->is_switching = FALSE;
3216 player->is_dropping = FALSE;
3217 player->is_dropping_pressed = FALSE;
3219 player->is_bored = FALSE;
3220 player->is_sleeping = FALSE;
3222 player->was_waiting = TRUE;
3223 player->was_moving = FALSE;
3224 player->was_snapping = FALSE;
3225 player->was_dropping = FALSE;
3227 player->frame_counter_bored = -1;
3228 player->frame_counter_sleeping = -1;
3230 player->anim_delay_counter = 0;
3231 player->post_delay_counter = 0;
3233 player->dir_waiting = initial_move_dir;
3234 player->action_waiting = ACTION_DEFAULT;
3235 player->last_action_waiting = ACTION_DEFAULT;
3236 player->special_action_bored = ACTION_DEFAULT;
3237 player->special_action_sleeping = ACTION_DEFAULT;
3239 player->switch_x = -1;
3240 player->switch_y = -1;
3242 player->drop_x = -1;
3243 player->drop_y = -1;
3245 player->show_envelope = 0;
3247 SetPlayerMoveSpeed(player, level.initial_player_stepsize[i], TRUE);
3249 player->push_delay = -1; /* initialized when pushing starts */
3250 player->push_delay_value = game.initial_push_delay_value;
3252 player->drop_delay = 0;
3253 player->drop_pressed_delay = 0;
3255 player->last_jx = -1;
3256 player->last_jy = -1;
3260 player->shield_normal_time_left = 0;
3261 player->shield_deadly_time_left = 0;
3263 player->inventory_infinite_element = EL_UNDEFINED;
3264 player->inventory_size = 0;
3266 if (level.use_initial_inventory[i])
3268 for (j = 0; j < level.initial_inventory_size[i]; j++)
3270 int element = level.initial_inventory_content[i][j];
3271 int collect_count = element_info[element].collect_count_initial;
3274 if (!IS_CUSTOM_ELEMENT(element))
3277 if (collect_count == 0)
3278 player->inventory_infinite_element = element;
3280 for (k = 0; k < collect_count; k++)
3281 if (player->inventory_size < MAX_INVENTORY_SIZE)
3282 player->inventory_element[player->inventory_size++] = element;
3286 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
3287 SnapField(player, 0, 0);
3289 player->LevelSolved = FALSE;
3290 player->GameOver = FALSE;
3292 player->LevelSolved_GameWon = FALSE;
3293 player->LevelSolved_GameEnd = FALSE;
3294 player->LevelSolved_PanelOff = FALSE;
3295 player->LevelSolved_SaveTape = FALSE;
3296 player->LevelSolved_SaveScore = FALSE;
3297 player->LevelSolved_CountingTime = 0;
3298 player->LevelSolved_CountingScore = 0;
3300 map_player_action[i] = i;
3303 network_player_action_received = FALSE;
3305 #if defined(NETWORK_AVALIABLE)
3306 /* initial null action */
3307 if (network_playing)
3308 SendToServer_MovePlayer(MV_NONE);
3317 TimeLeft = level.time;
3320 ScreenMovDir = MV_NONE;
3324 ScrollStepSize = 0; /* will be correctly initialized by ScrollScreen() */
3326 AllPlayersGone = FALSE;
3328 game.no_time_limit = (level.time == 0);
3330 game.yamyam_content_nr = 0;
3331 game.robot_wheel_active = FALSE;
3332 game.magic_wall_active = FALSE;
3333 game.magic_wall_time_left = 0;
3334 game.light_time_left = 0;
3335 game.timegate_time_left = 0;
3336 game.switchgate_pos = 0;
3337 game.wind_direction = level.wind_direction_initial;
3339 game.lenses_time_left = 0;
3340 game.magnify_time_left = 0;
3342 game.ball_state = level.ball_state_initial;
3343 game.ball_content_nr = 0;
3345 game.envelope_active = FALSE;
3347 /* set focus to local player for network games, else to all players */
3348 game.centered_player_nr = (network_playing ? local_player->index_nr : -1);
3349 game.centered_player_nr_next = game.centered_player_nr;
3350 game.set_centered_player = FALSE;
3352 if (network_playing && tape.recording)
3354 /* store client dependent player focus when recording network games */
3355 tape.centered_player_nr_next = game.centered_player_nr_next;
3356 tape.set_centered_player = TRUE;
3359 for (i = 0; i < NUM_BELTS; i++)
3361 game.belt_dir[i] = MV_NONE;
3362 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
3365 for (i = 0; i < MAX_NUM_AMOEBA; i++)
3366 AmoebaCnt[i] = AmoebaCnt2[i] = 0;
3368 #if DEBUG_INIT_PLAYER
3371 printf("Player status at level initialization:\n");
3375 SCAN_PLAYFIELD(x, y)
3377 Feld[x][y] = level.field[x][y];
3378 MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
3379 ChangeDelay[x][y] = 0;
3380 ChangePage[x][y] = -1;
3381 CustomValue[x][y] = 0; /* initialized in InitField() */
3382 Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
3384 WasJustMoving[x][y] = 0;
3385 WasJustFalling[x][y] = 0;
3386 CheckCollision[x][y] = 0;
3387 CheckImpact[x][y] = 0;
3389 Pushed[x][y] = FALSE;
3391 ChangeCount[x][y] = 0;
3392 ChangeEvent[x][y] = -1;
3394 ExplodePhase[x][y] = 0;
3395 ExplodeDelay[x][y] = 0;
3396 ExplodeField[x][y] = EX_TYPE_NONE;
3398 RunnerVisit[x][y] = 0;
3399 PlayerVisit[x][y] = 0;
3402 GfxRandom[x][y] = INIT_GFX_RANDOM();
3403 GfxElement[x][y] = EL_UNDEFINED;
3404 GfxAction[x][y] = ACTION_DEFAULT;
3405 GfxDir[x][y] = MV_NONE;
3406 GfxRedraw[x][y] = GFX_REDRAW_NONE;
3409 SCAN_PLAYFIELD(x, y)
3411 if (emulate_bd && !IS_BD_ELEMENT(Feld[x][y]))
3413 if (emulate_sb && !IS_SB_ELEMENT(Feld[x][y]))
3415 if (emulate_sp && !IS_SP_ELEMENT(Feld[x][y]))
3418 InitField(x, y, TRUE);
3420 ResetGfxAnimation(x, y);
3425 for (i = 0; i < MAX_PLAYERS; i++)
3427 struct PlayerInfo *player = &stored_player[i];
3429 /* set number of special actions for bored and sleeping animation */
3430 player->num_special_action_bored =
3431 get_num_special_action(player->artwork_element,
3432 ACTION_BORING_1, ACTION_BORING_LAST);
3433 player->num_special_action_sleeping =
3434 get_num_special_action(player->artwork_element,
3435 ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
3438 game.emulation = (emulate_bd ? EMU_BOULDERDASH :
3439 emulate_sb ? EMU_SOKOBAN :
3440 emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
3442 /* initialize type of slippery elements */
3443 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3445 if (!IS_CUSTOM_ELEMENT(i))
3447 /* default: elements slip down either to the left or right randomly */
3448 element_info[i].slippery_type = SLIPPERY_ANY_RANDOM;
3450 /* SP style elements prefer to slip down on the left side */
3451 if (game.engine_version >= VERSION_IDENT(3,1,1,0) && IS_SP_ELEMENT(i))
3452 element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3454 /* BD style elements prefer to slip down on the left side */
3455 if (game.emulation == EMU_BOULDERDASH)
3456 element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3460 /* initialize explosion and ignition delay */
3461 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3463 if (!IS_CUSTOM_ELEMENT(i))
3466 int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
3467 game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
3468 game.emulation == EMU_SUPAPLEX ? 3 : 2);
3469 int last_phase = (num_phase + 1) * delay;
3470 int half_phase = (num_phase / 2) * delay;
3472 element_info[i].explosion_delay = last_phase - 1;
3473 element_info[i].ignition_delay = half_phase;
3475 if (i == EL_BLACK_ORB)
3476 element_info[i].ignition_delay = 1;
3480 /* correct non-moving belts to start moving left */
3481 for (i = 0; i < NUM_BELTS; i++)
3482 if (game.belt_dir[i] == MV_NONE)
3483 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
3485 #if USE_NEW_PLAYER_ASSIGNMENTS
3486 /* !!! SAME AS init.c:InitPlayerInfo() -- FIX THIS !!! */
3487 /* choose default local player */
3488 local_player = &stored_player[0];
3490 for (i = 0; i < MAX_PLAYERS; i++)
3491 stored_player[i].connected = FALSE;
3493 local_player->connected = TRUE;
3494 /* !!! SAME AS init.c:InitPlayerInfo() -- FIX THIS !!! */
3498 for (i = 0; i < MAX_PLAYERS; i++)
3499 stored_player[i].connected = tape.player_participates[i];
3501 else if (game.team_mode && !options.network)
3503 /* try to guess locally connected team mode players (needed for correct
3504 assignment of player figures from level to locally playing players) */
3506 for (i = 0; i < MAX_PLAYERS; i++)
3507 if (setup.input[i].use_joystick ||
3508 setup.input[i].key.left != KSYM_UNDEFINED)
3509 stored_player[i].connected = TRUE;
3512 #if DEBUG_INIT_PLAYER
3515 printf("Player status after level initialization:\n");
3517 for (i = 0; i < MAX_PLAYERS; i++)
3519 struct PlayerInfo *player = &stored_player[i];
3521 printf("- player %d: present == %d, connected == %d, active == %d",
3527 if (local_player == player)
3528 printf(" (local player)");
3535 #if DEBUG_INIT_PLAYER
3537 printf("Reassigning players ...\n");
3540 /* check if any connected player was not found in playfield */
3541 for (i = 0; i < MAX_PLAYERS; i++)
3543 struct PlayerInfo *player = &stored_player[i];
3545 if (player->connected && !player->present)
3547 struct PlayerInfo *field_player = NULL;
3549 #if DEBUG_INIT_PLAYER
3551 printf("- looking for field player for player %d ...\n", i + 1);
3554 /* assign first free player found that is present in the playfield */
3556 /* first try: look for unmapped playfield player that is not connected */
3557 for (j = 0; j < MAX_PLAYERS; j++)
3558 if (field_player == NULL &&
3559 stored_player[j].present &&
3560 !stored_player[j].mapped &&
3561 !stored_player[j].connected)
3562 field_player = &stored_player[j];
3564 /* second try: look for *any* unmapped playfield player */
3565 for (j = 0; j < MAX_PLAYERS; j++)
3566 if (field_player == NULL &&
3567 stored_player[j].present &&
3568 !stored_player[j].mapped)
3569 field_player = &stored_player[j];
3571 if (field_player != NULL)
3573 int jx = field_player->jx, jy = field_player->jy;
3575 #if DEBUG_INIT_PLAYER
3577 printf("- found player %d\n", field_player->index_nr + 1);
3580 player->present = FALSE;
3581 player->active = FALSE;
3583 field_player->present = TRUE;
3584 field_player->active = TRUE;
3587 player->initial_element = field_player->initial_element;
3588 player->artwork_element = field_player->artwork_element;
3590 player->block_last_field = field_player->block_last_field;
3591 player->block_delay_adjustment = field_player->block_delay_adjustment;
3594 StorePlayer[jx][jy] = field_player->element_nr;
3596 field_player->jx = field_player->last_jx = jx;
3597 field_player->jy = field_player->last_jy = jy;
3599 if (local_player == player)
3600 local_player = field_player;
3602 map_player_action[field_player->index_nr] = i;
3604 field_player->mapped = TRUE;
3606 #if DEBUG_INIT_PLAYER
3608 printf("- map_player_action[%d] == %d\n",
3609 field_player->index_nr + 1, i + 1);
3614 if (player->connected && player->present)
3615 player->mapped = TRUE;
3618 #if DEBUG_INIT_PLAYER
3621 printf("Player status after player assignment (first stage):\n");
3623 for (i = 0; i < MAX_PLAYERS; i++)
3625 struct PlayerInfo *player = &stored_player[i];
3627 printf("- player %d: present == %d, connected == %d, active == %d",
3633 if (local_player == player)
3634 printf(" (local player)");
3643 /* check if any connected player was not found in playfield */
3644 for (i = 0; i < MAX_PLAYERS; i++)
3646 struct PlayerInfo *player = &stored_player[i];
3648 if (player->connected && !player->present)
3650 for (j = 0; j < MAX_PLAYERS; j++)
3652 struct PlayerInfo *field_player = &stored_player[j];
3653 int jx = field_player->jx, jy = field_player->jy;
3655 /* assign first free player found that is present in the playfield */
3656 if (field_player->present && !field_player->connected)
3658 player->present = TRUE;
3659 player->active = TRUE;
3661 field_player->present = FALSE;
3662 field_player->active = FALSE;
3664 player->initial_element = field_player->initial_element;
3665 player->artwork_element = field_player->artwork_element;
3667 player->block_last_field = field_player->block_last_field;
3668 player->block_delay_adjustment = field_player->block_delay_adjustment;
3670 StorePlayer[jx][jy] = player->element_nr;
3672 player->jx = player->last_jx = jx;
3673 player->jy = player->last_jy = jy;
3683 printf("::: local_player->present == %d\n", local_player->present);
3688 /* when playing a tape, eliminate all players who do not participate */
3690 #if USE_NEW_PLAYER_ASSIGNMENTS
3692 if (!game.team_mode)
3694 for (i = 0; i < MAX_PLAYERS; i++)
3696 if (stored_player[i].active &&
3697 !tape.player_participates[map_player_action[i]])
3699 struct PlayerInfo *player = &stored_player[i];
3700 int jx = player->jx, jy = player->jy;
3702 #if DEBUG_INIT_PLAYER
3704 printf("Removing player %d at (%d, %d)\n", i + 1, jx, jy);
3707 player->active = FALSE;
3708 StorePlayer[jx][jy] = 0;
3709 Feld[jx][jy] = EL_EMPTY;
3716 for (i = 0; i < MAX_PLAYERS; i++)
3718 if (stored_player[i].active &&
3719 !tape.player_participates[i])
3721 struct PlayerInfo *player = &stored_player[i];
3722 int jx = player->jx, jy = player->jy;
3724 player->active = FALSE;
3725 StorePlayer[jx][jy] = 0;
3726 Feld[jx][jy] = EL_EMPTY;
3731 else if (!options.network && !game.team_mode) /* && !tape.playing */
3733 /* when in single player mode, eliminate all but the first active player */
3735 for (i = 0; i < MAX_PLAYERS; i++)
3737 if (stored_player[i].active)
3739 for (j = i + 1; j < MAX_PLAYERS; j++)
3741 if (stored_player[j].active)
3743 struct PlayerInfo *player = &stored_player[j];
3744 int jx = player->jx, jy = player->jy;
3746 player->active = FALSE;
3747 player->present = FALSE;
3749 StorePlayer[jx][jy] = 0;
3750 Feld[jx][jy] = EL_EMPTY;
3757 /* when recording the game, store which players take part in the game */
3760 #if USE_NEW_PLAYER_ASSIGNMENTS
3761 for (i = 0; i < MAX_PLAYERS; i++)
3762 if (stored_player[i].connected)
3763 tape.player_participates[i] = TRUE;
3765 for (i = 0; i < MAX_PLAYERS; i++)
3766 if (stored_player[i].active)
3767 tape.player_participates[i] = TRUE;
3771 #if DEBUG_INIT_PLAYER
3774 printf("Player status after player assignment (final stage):\n");
3776 for (i = 0; i < MAX_PLAYERS; i++)
3778 struct PlayerInfo *player = &stored_player[i];
3780 printf("- player %d: present == %d, connected == %d, active == %d",
3786 if (local_player == player)
3787 printf(" (local player)");
3794 if (BorderElement == EL_EMPTY)
3797 SBX_Right = lev_fieldx - SCR_FIELDX;
3799 SBY_Lower = lev_fieldy - SCR_FIELDY;
3804 SBX_Right = lev_fieldx - SCR_FIELDX + 1;
3806 SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
3809 if (full_lev_fieldx <= SCR_FIELDX)
3810 SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
3811 if (full_lev_fieldy <= SCR_FIELDY)
3812 SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
3814 if (EVEN(SCR_FIELDX) && full_lev_fieldx > SCR_FIELDX)
3816 if (EVEN(SCR_FIELDY) && full_lev_fieldy > SCR_FIELDY)
3819 /* if local player not found, look for custom element that might create
3820 the player (make some assumptions about the right custom element) */
3821 if (!local_player->present)
3823 int start_x = 0, start_y = 0;
3824 int found_rating = 0;
3825 int found_element = EL_UNDEFINED;
3826 int player_nr = local_player->index_nr;
3828 SCAN_PLAYFIELD(x, y)
3830 int element = Feld[x][y];
3835 if (level.use_start_element[player_nr] &&
3836 level.start_element[player_nr] == element &&
3843 found_element = element;
3846 if (!IS_CUSTOM_ELEMENT(element))
3849 if (CAN_CHANGE(element))
3851 for (i = 0; i < element_info[element].num_change_pages; i++)
3853 /* check for player created from custom element as single target */
3854 content = element_info[element].change_page[i].target_element;
3855 is_player = ELEM_IS_PLAYER(content);
3857 if (is_player && (found_rating < 3 ||
3858 (found_rating == 3 && element < found_element)))
3864 found_element = element;
3869 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
3871 /* check for player created from custom element as explosion content */
3872 content = element_info[element].content.e[xx][yy];
3873 is_player = ELEM_IS_PLAYER(content);
3875 if (is_player && (found_rating < 2 ||
3876 (found_rating == 2 && element < found_element)))
3878 start_x = x + xx - 1;
3879 start_y = y + yy - 1;