1 /***********************************************************
2 * Rocks'n'Diamonds -- McDuffin Strikes Back! *
3 *----------------------------------------------------------*
4 * (c) 1995-2002 Artsoft Entertainment *
6 * Detmolder Strasse 189 *
9 * e-mail: info@artsoft.org *
10 *----------------------------------------------------------*
12 ***********************************************************/
14 #include "libgame/libgame.h"
24 /* this switch controls how rocks move horizontally */
25 #define OLD_GAME_BEHAVIOUR FALSE
27 /* EXPERIMENTAL STUFF */
28 #define USE_NEW_AMOEBA_CODE FALSE
35 /* for MovePlayer() */
36 #define MF_NO_ACTION 0
40 /* for ScrollPlayer() */
42 #define SCROLL_GO_ON 1
45 #define EX_PHASE_START 0
46 #define EX_NO_EXPLOSION 0
51 /* special positions in the game control window (relative to control window) */
54 #define XX_EMERALDS 29
55 #define YY_EMERALDS 54
56 #define XX_DYNAMITE 29
57 #define YY_DYNAMITE 89
65 /* special positions in the game control window (relative to main window) */
66 #define DX_LEVEL (DX + XX_LEVEL)
67 #define DY_LEVEL (DY + YY_LEVEL)
68 #define DX_EMERALDS (DX + XX_EMERALDS)
69 #define DY_EMERALDS (DY + YY_EMERALDS)
70 #define DX_DYNAMITE (DX + XX_DYNAMITE)
71 #define DY_DYNAMITE (DY + YY_DYNAMITE)
72 #define DX_KEYS (DX + XX_KEYS)
73 #define DY_KEYS (DY + YY_KEYS)
74 #define DX_SCORE (DX + XX_SCORE)
75 #define DY_SCORE (DY + YY_SCORE)
76 #define DX_TIME (DX + XX_TIME)
77 #define DY_TIME (DY + YY_TIME)
79 /* values for initial player move delay (initial delay counter value) */
80 #define INITIAL_MOVE_DELAY_OFF -1
81 #define INITIAL_MOVE_DELAY_ON 0
83 /* values for player movement speed (which is in fact a delay value) */
84 #define MOVE_DELAY_NORMAL_SPEED 8
85 #define MOVE_DELAY_HIGH_SPEED 4
87 #define DOUBLE_MOVE_DELAY(x) (x = (x <= MOVE_DELAY_HIGH_SPEED ? x * 2 : x))
88 #define HALVE_MOVE_DELAY(x) (x = (x >= MOVE_DELAY_HIGH_SPEED ? x / 2 : x))
89 #define DOUBLE_PLAYER_SPEED(p) (HALVE_MOVE_DELAY((p)->move_delay_value))
90 #define HALVE_PLAYER_SPEED(p) (DOUBLE_MOVE_DELAY((p)->move_delay_value))
92 /* values for other actions */
93 #define MOVE_STEPSIZE_NORMAL (TILEX / MOVE_DELAY_NORMAL_SPEED)
95 #define INIT_GFX_RANDOM() (SimpleRND(1000000))
97 #define GET_NEW_PUSH_DELAY(e) ( (element_info[e].push_delay_fixed) + \
98 RND(element_info[e].push_delay_random))
99 #define GET_NEW_MOVE_DELAY(e) ( (element_info[e].move_delay_fixed) + \
100 RND(element_info[e].move_delay_random))
101 #define GET_MAX_MOVE_DELAY(e) ( (element_info[e].move_delay_fixed) + \
102 (element_info[e].move_delay_random))
104 #define ELEMENT_CAN_ENTER_FIELD_GENERIC(e, x, y, condition) \
105 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
107 (DONT_COLLIDE_WITH(e) && \
108 IS_FREE_OR_PLAYER(x, y))))
110 #define ELEMENT_CAN_ENTER_FIELD_GENERIC_2(x, y, condition) \
111 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
114 #define ELEMENT_CAN_ENTER_FIELD(e, x, y) \
115 ELEMENT_CAN_ENTER_FIELD_GENERIC(e, x, y, 0)
117 #define ELEMENT_CAN_ENTER_FIELD_OR_ACID(e, x, y) \
118 ELEMENT_CAN_ENTER_FIELD_GENERIC(e, x, y, (Feld[x][y] == EL_ACID))
120 #define ELEMENT_CAN_ENTER_FIELD_OR_ACID_2(x, y) \
121 ELEMENT_CAN_ENTER_FIELD_GENERIC_2(x, y, (Feld[x][y] == EL_ACID))
123 #define ENEMY_CAN_ENTER_FIELD(x, y) (IN_LEV_FIELD(x, y) && IS_FREE(x, y))
125 #define YAMYAM_CAN_ENTER_FIELD(x, y) \
126 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) || \
127 Feld[x][y] == EL_DIAMOND))
129 #define DARK_YAMYAM_CAN_ENTER_FIELD(x, y) \
130 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) || \
131 IS_FOOD_DARK_YAMYAM(Feld[x][y])))
133 #define PACMAN_CAN_ENTER_FIELD(x, y) \
134 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) || \
135 IS_AMOEBOID(Feld[x][y])))
137 #define PIG_CAN_ENTER_FIELD(x, y) \
138 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
139 IS_FOOD_PIG(Feld[x][y])))
141 #define PENGUIN_CAN_ENTER_FIELD(x, y) \
142 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
143 IS_FOOD_PENGUIN(Feld[x][y]) || \
144 Feld[x][y] == EL_EXIT_OPEN || \
145 Feld[x][y] == EL_ACID))
147 #define MOLE_CAN_ENTER_FIELD(x, y, condition) \
148 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || (condition)))
150 #define IN_LEV_FIELD_AND_IS_FREE(x, y) (IN_LEV_FIELD(x, y) && IS_FREE(x, y))
151 #define IN_LEV_FIELD_AND_NOT_FREE(x, y) (IN_LEV_FIELD(x, y) && !IS_FREE(x, y))
153 /* game button identifiers */
154 #define GAME_CTRL_ID_STOP 0
155 #define GAME_CTRL_ID_PAUSE 1
156 #define GAME_CTRL_ID_PLAY 2
157 #define SOUND_CTRL_ID_MUSIC 3
158 #define SOUND_CTRL_ID_LOOPS 4
159 #define SOUND_CTRL_ID_SIMPLE 5
161 #define NUM_GAME_BUTTONS 6
164 /* forward declaration for internal use */
166 static boolean MovePlayerOneStep(struct PlayerInfo *, int, int, int, int);
167 static boolean MovePlayer(struct PlayerInfo *, int, int);
168 static void ScrollPlayer(struct PlayerInfo *, int);
169 static void ScrollScreen(struct PlayerInfo *, int);
171 static void InitBeltMovement(void);
172 static void CloseAllOpenTimegates(void);
173 static void CheckGravityMovement(struct PlayerInfo *);
174 static void KillHeroUnlessProtected(int, int);
176 static void TestIfPlayerTouchesCustomElement(int, int);
177 static void TestIfElementTouchesCustomElement(int, int);
179 static void ChangeElement(int, int, int);
180 static boolean CheckTriggeredElementSideChange(int, int, int, int, int);
181 static boolean CheckTriggeredElementChange(int, int, int, int);
182 static boolean CheckElementSideChange(int, int, int, int, int, int);
183 static boolean CheckElementChange(int, int, int, int);
185 static void PlayLevelSound(int, int, int);
186 static void PlayLevelSoundNearest(int, int, int);
187 static void PlayLevelSoundAction(int, int, int);
188 static void PlayLevelSoundElementAction(int, int, int, int);
189 static void PlayLevelSoundActionIfLoop(int, int, int);
190 static void StopLevelSoundActionIfLoop(int, int, int);
191 static void PlayLevelMusic();
193 static void MapGameButtons();
194 static void HandleGameButtons(struct GadgetInfo *);
196 static struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS];
199 /* ------------------------------------------------------------------------- */
200 /* definition of elements that automatically change to other elements after */
201 /* a specified time, eventually calling a function when changing */
202 /* ------------------------------------------------------------------------- */
204 /* forward declaration for changer functions */
205 static void InitBuggyBase(int x, int y);
206 static void WarnBuggyBase(int x, int y);
208 static void InitTrap(int x, int y);
209 static void ActivateTrap(int x, int y);
210 static void ChangeActiveTrap(int x, int y);
212 static void InitRobotWheel(int x, int y);
213 static void RunRobotWheel(int x, int y);
214 static void StopRobotWheel(int x, int y);
216 static void InitTimegateWheel(int x, int y);
217 static void RunTimegateWheel(int x, int y);
219 struct ChangingElementInfo
224 void (*pre_change_function)(int x, int y);
225 void (*change_function)(int x, int y);
226 void (*post_change_function)(int x, int y);
229 static struct ChangingElementInfo change_delay_list[] =
280 EL_SWITCHGATE_OPENING,
288 EL_SWITCHGATE_CLOSING,
289 EL_SWITCHGATE_CLOSED,
321 EL_ACID_SPLASH_RIGHT,
330 EL_SP_BUGGY_BASE_ACTIVATING,
337 EL_SP_BUGGY_BASE_ACTIVATING,
338 EL_SP_BUGGY_BASE_ACTIVE,
345 EL_SP_BUGGY_BASE_ACTIVE,
369 EL_ROBOT_WHEEL_ACTIVE,
377 EL_TIMEGATE_SWITCH_ACTIVE,
398 int push_delay_fixed, push_delay_random;
403 { EL_BALLOON, 0, 0 },
405 { EL_SOKOBAN_OBJECT, 2, 0 },
406 { EL_SOKOBAN_FIELD_FULL, 2, 0 },
407 { EL_SATELLITE, 2, 0 },
408 { EL_SP_DISK_YELLOW, 2, 0 },
410 { EL_UNDEFINED, 0, 0 },
418 move_stepsize_list[] =
420 { EL_AMOEBA_DROP, 2 },
421 { EL_AMOEBA_DROPPING, 2 },
422 { EL_QUICKSAND_FILLING, 1 },
423 { EL_QUICKSAND_EMPTYING, 1 },
424 { EL_MAGIC_WALL_FILLING, 2 },
425 { EL_BD_MAGIC_WALL_FILLING, 2 },
426 { EL_MAGIC_WALL_EMPTYING, 2 },
427 { EL_BD_MAGIC_WALL_EMPTYING, 2 },
437 collect_count_list[] =
440 { EL_BD_DIAMOND, 1 },
441 { EL_EMERALD_YELLOW, 1 },
442 { EL_EMERALD_RED, 1 },
443 { EL_EMERALD_PURPLE, 1 },
445 { EL_SP_INFOTRON, 1 },
452 static unsigned long trigger_events[MAX_NUM_ELEMENTS];
454 #define IS_AUTO_CHANGING(e) (element_info[e].change_events & \
455 CH_EVENT_BIT(CE_DELAY))
456 #define IS_JUST_CHANGING(x, y) (ChangeDelay[x][y] != 0)
457 #define IS_CHANGING(x, y) (IS_AUTO_CHANGING(Feld[x][y]) || \
458 IS_JUST_CHANGING(x, y))
460 #define CE_PAGE(e, ce) (element_info[e].event_page[ce])
463 void GetPlayerConfig()
465 if (!audio.sound_available)
466 setup.sound_simple = FALSE;
468 if (!audio.loops_available)
469 setup.sound_loops = FALSE;
471 if (!audio.music_available)
472 setup.sound_music = FALSE;
474 if (!video.fullscreen_available)
475 setup.fullscreen = FALSE;
477 setup.sound = (setup.sound_simple || setup.sound_loops || setup.sound_music);
479 SetAudioMode(setup.sound);
483 static int getBeltNrFromBeltElement(int element)
485 return (element < EL_CONVEYOR_BELT_2_LEFT ? 0 :
486 element < EL_CONVEYOR_BELT_3_LEFT ? 1 :
487 element < EL_CONVEYOR_BELT_4_LEFT ? 2 : 3);
490 static int getBeltNrFromBeltActiveElement(int element)
492 return (element < EL_CONVEYOR_BELT_2_LEFT_ACTIVE ? 0 :
493 element < EL_CONVEYOR_BELT_3_LEFT_ACTIVE ? 1 :
494 element < EL_CONVEYOR_BELT_4_LEFT_ACTIVE ? 2 : 3);
497 static int getBeltNrFromBeltSwitchElement(int element)
499 return (element < EL_CONVEYOR_BELT_2_SWITCH_LEFT ? 0 :
500 element < EL_CONVEYOR_BELT_3_SWITCH_LEFT ? 1 :
501 element < EL_CONVEYOR_BELT_4_SWITCH_LEFT ? 2 : 3);
504 static int getBeltDirNrFromBeltSwitchElement(int element)
506 static int belt_base_element[4] =
508 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
509 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
510 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
511 EL_CONVEYOR_BELT_4_SWITCH_LEFT
514 int belt_nr = getBeltNrFromBeltSwitchElement(element);
515 int belt_dir_nr = element - belt_base_element[belt_nr];
517 return (belt_dir_nr % 3);
520 static int getBeltDirFromBeltSwitchElement(int element)
522 static int belt_move_dir[3] =
529 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(element);
531 return belt_move_dir[belt_dir_nr];
534 static void InitPlayerField(int x, int y, int element, boolean init_game)
536 if (element == EL_SP_MURPHY)
540 if (stored_player[0].present)
542 Feld[x][y] = EL_SP_MURPHY_CLONE;
548 stored_player[0].use_murphy_graphic = TRUE;
551 Feld[x][y] = EL_PLAYER_1;
557 struct PlayerInfo *player = &stored_player[Feld[x][y] - EL_PLAYER_1];
558 int jx = player->jx, jy = player->jy;
560 player->present = TRUE;
562 if (!options.network || player->connected)
564 player->active = TRUE;
566 /* remove potentially duplicate players */
567 if (StorePlayer[jx][jy] == Feld[x][y])
568 StorePlayer[jx][jy] = 0;
570 StorePlayer[x][y] = Feld[x][y];
574 printf("Player %d activated.\n", player->element_nr);
575 printf("[Local player is %d and currently %s.]\n",
576 local_player->element_nr,
577 local_player->active ? "active" : "not active");
581 Feld[x][y] = EL_EMPTY;
582 player->jx = player->last_jx = x;
583 player->jy = player->last_jy = y;
587 static void InitField(int x, int y, boolean init_game)
589 int element = Feld[x][y];
598 InitPlayerField(x, y, element, init_game);
602 if (x < lev_fieldx-1 && Feld[x+1][y] == EL_ACID)
603 Feld[x][y] = EL_ACID_POOL_TOPLEFT;
604 else if (x > 0 && Feld[x-1][y] == EL_ACID)
605 Feld[x][y] = EL_ACID_POOL_TOPRIGHT;
606 else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPLEFT)
607 Feld[x][y] = EL_ACID_POOL_BOTTOMLEFT;
608 else if (y > 0 && Feld[x][y-1] == EL_ACID)
609 Feld[x][y] = EL_ACID_POOL_BOTTOM;
610 else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPRIGHT)
611 Feld[x][y] = EL_ACID_POOL_BOTTOMRIGHT;
619 case EL_SPACESHIP_RIGHT:
620 case EL_SPACESHIP_UP:
621 case EL_SPACESHIP_LEFT:
622 case EL_SPACESHIP_DOWN:
624 case EL_BD_BUTTERFLY_RIGHT:
625 case EL_BD_BUTTERFLY_UP:
626 case EL_BD_BUTTERFLY_LEFT:
627 case EL_BD_BUTTERFLY_DOWN:
628 case EL_BD_BUTTERFLY:
629 case EL_BD_FIREFLY_RIGHT:
630 case EL_BD_FIREFLY_UP:
631 case EL_BD_FIREFLY_LEFT:
632 case EL_BD_FIREFLY_DOWN:
634 case EL_PACMAN_RIGHT:
658 if (y == lev_fieldy - 1)
660 Feld[x][y] = EL_AMOEBA_GROWING;
661 Store[x][y] = EL_AMOEBA_WET;
665 case EL_DYNAMITE_ACTIVE:
670 local_player->lights_still_needed++;
673 case EL_SOKOBAN_FIELD_EMPTY:
674 local_player->sokobanfields_still_needed++;
678 local_player->friends_still_needed++;
683 GfxDir[x][y] = MovDir[x][y] = 1 << RND(4);
688 Feld[x][y] = EL_EMPTY;
693 case EL_EM_KEY_1_FILE:
694 Feld[x][y] = EL_EM_KEY_1;
696 case EL_EM_KEY_2_FILE:
697 Feld[x][y] = EL_EM_KEY_2;
699 case EL_EM_KEY_3_FILE:
700 Feld[x][y] = EL_EM_KEY_3;
702 case EL_EM_KEY_4_FILE:
703 Feld[x][y] = EL_EM_KEY_4;
707 case EL_CONVEYOR_BELT_1_SWITCH_LEFT:
708 case EL_CONVEYOR_BELT_1_SWITCH_MIDDLE:
709 case EL_CONVEYOR_BELT_1_SWITCH_RIGHT:
710 case EL_CONVEYOR_BELT_2_SWITCH_LEFT:
711 case EL_CONVEYOR_BELT_2_SWITCH_MIDDLE:
712 case EL_CONVEYOR_BELT_2_SWITCH_RIGHT:
713 case EL_CONVEYOR_BELT_3_SWITCH_LEFT:
714 case EL_CONVEYOR_BELT_3_SWITCH_MIDDLE:
715 case EL_CONVEYOR_BELT_3_SWITCH_RIGHT:
716 case EL_CONVEYOR_BELT_4_SWITCH_LEFT:
717 case EL_CONVEYOR_BELT_4_SWITCH_MIDDLE:
718 case EL_CONVEYOR_BELT_4_SWITCH_RIGHT:
721 int belt_nr = getBeltNrFromBeltSwitchElement(Feld[x][y]);
722 int belt_dir = getBeltDirFromBeltSwitchElement(Feld[x][y]);
723 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(Feld[x][y]);
725 if (game.belt_dir_nr[belt_nr] == 3) /* initial value */
727 game.belt_dir[belt_nr] = belt_dir;
728 game.belt_dir_nr[belt_nr] = belt_dir_nr;
730 else /* more than one switch -- set it like the first switch */
732 Feld[x][y] = Feld[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
737 case EL_SWITCHGATE_SWITCH_DOWN: /* always start with same switch pos */
739 Feld[x][y] = EL_SWITCHGATE_SWITCH_UP;
742 case EL_LIGHT_SWITCH_ACTIVE:
744 game.light_time_left = level.time_light * FRAMES_PER_SECOND;
748 if (IS_CUSTOM_ELEMENT(element) && CAN_MOVE(element))
754 void DrawGameDoorValues()
758 for (i=0; i<MAX_PLAYERS; i++)
760 if (stored_player[i].key[j])
761 DrawMiniGraphicExt(drawto, DX_KEYS + j * MINI_TILEX, DY_KEYS,
762 el2edimg(EL_KEY_1 + j));
764 DrawText(DX + XX_EMERALDS, DY + YY_EMERALDS,
765 int2str(local_player->gems_still_needed, 3), FONT_TEXT_2);
766 DrawText(DX + XX_DYNAMITE, DY + YY_DYNAMITE,
767 int2str(local_player->inventory_size, 3), FONT_TEXT_2);
768 DrawText(DX + XX_SCORE, DY + YY_SCORE,
769 int2str(local_player->score, 5), FONT_TEXT_2);
770 DrawText(DX + XX_TIME, DY + YY_TIME,
771 int2str(TimeLeft, 3), FONT_TEXT_2);
776 =============================================================================
778 -----------------------------------------------------------------------------
779 initialize game engine due to level / tape version number
780 =============================================================================
783 static void InitGameEngine()
787 /* set game engine from tape file when re-playing, else from level file */
788 game.engine_version = (tape.playing ? tape.engine_version :
791 /* dynamically adjust element properties according to game engine version */
792 InitElementPropertiesEngine(game.engine_version);
795 printf("level %d: level version == %06d\n", level_nr, level.game_version);
796 printf(" tape version == %06d [%s] [file: %06d]\n",
797 tape.engine_version, (tape.playing ? "PLAYING" : "RECORDING"),
799 printf(" => game.engine_version == %06d\n", game.engine_version);
802 /* ---------- initialize player's initial move delay --------------------- */
804 /* dynamically adjust player properties according to game engine version */
805 game.initial_move_delay =
806 (game.engine_version <= VERSION_IDENT(2,0,1,0) ? INITIAL_MOVE_DELAY_ON :
807 INITIAL_MOVE_DELAY_OFF);
809 /* dynamically adjust player properties according to level information */
810 game.initial_move_delay_value =
811 (level.double_speed ? MOVE_DELAY_HIGH_SPEED : MOVE_DELAY_NORMAL_SPEED);
813 /* ---------- initialize player's initial push delay --------------------- */
815 /* dynamically adjust player properties according to game engine version */
816 game.initial_push_delay_value =
817 (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
819 /* ---------- initialize changing elements ------------------------------- */
821 /* initialize changing elements information */
822 for (i=0; i < MAX_NUM_ELEMENTS; i++)
824 struct ElementInfo *ei = &element_info[i];
826 /* this pointer might have been changed in the level editor */
827 ei->change = &ei->change_page[0];
829 if (!IS_CUSTOM_ELEMENT(i))
831 ei->change->target_element = EL_EMPTY_SPACE;
832 ei->change->delay_fixed = 0;
833 ei->change->delay_random = 0;
834 ei->change->delay_frames = 1;
837 ei->change_events = CE_BITMASK_DEFAULT;
838 for (j=0; j < NUM_CHANGE_EVENTS; j++)
840 ei->event_page_nr[j] = 0;
841 ei->event_page[j] = &ei->change_page[0];
845 /* add changing elements from pre-defined list */
846 for (i=0; change_delay_list[i].element != EL_UNDEFINED; i++)
848 struct ChangingElementInfo *ch_delay = &change_delay_list[i];
849 struct ElementInfo *ei = &element_info[ch_delay->element];
851 ei->change->target_element = ch_delay->target_element;
852 ei->change->delay_fixed = ch_delay->change_delay;
854 ei->change->pre_change_function = ch_delay->pre_change_function;
855 ei->change->change_function = ch_delay->change_function;
856 ei->change->post_change_function = ch_delay->post_change_function;
858 ei->change_events |= CH_EVENT_BIT(CE_DELAY);
862 /* add change events from custom element configuration */
863 for (i=0; i < NUM_CUSTOM_ELEMENTS; i++)
865 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
867 for (j=0; j < ei->num_change_pages; j++)
869 if (!ei->change_page[j].can_change)
872 for (k=0; k < NUM_CHANGE_EVENTS; k++)
874 /* only add event page for the first page found with this event */
875 if (ei->change_page[j].events & CH_EVENT_BIT(k) &&
876 !(ei->change_events & CH_EVENT_BIT(k)))
878 ei->change_events |= CH_EVENT_BIT(k);
879 ei->event_page_nr[k] = j;
880 ei->event_page[k] = &ei->change_page[j];
888 /* add change events from custom element configuration */
889 for (i=0; i < NUM_CUSTOM_ELEMENTS; i++)
891 int element = EL_CUSTOM_START + i;
893 /* only add custom elements that change after fixed/random frame delay */
894 if (CAN_CHANGE(element) && HAS_CHANGE_EVENT(element, CE_DELAY))
895 element_info[element].change_events |= CH_EVENT_BIT(CE_DELAY);
899 /* ---------- initialize trigger events ---------------------------------- */
901 /* initialize trigger events information */
902 for (i=0; i<MAX_NUM_ELEMENTS; i++)
903 trigger_events[i] = EP_BITMASK_DEFAULT;
906 /* add trigger events from element change event properties */
907 for (i=0; i<MAX_NUM_ELEMENTS; i++)
909 struct ElementInfo *ei = &element_info[i];
911 for (j=0; j < ei->num_change_pages; j++)
913 if (!ei->change_page[j].can_change)
916 if (ei->change_page[j].events & CH_EVENT_BIT(CE_BY_OTHER_ACTION))
918 int trigger_element = ei->change_page[j].trigger_element;
920 trigger_events[trigger_element] |= ei->change_page[j].events;
925 /* add trigger events from element change event properties */
926 for (i=0; i<MAX_NUM_ELEMENTS; i++)
927 if (HAS_CHANGE_EVENT(i, CE_BY_OTHER_ACTION))
928 trigger_events[element_info[i].change->trigger_element] |=
929 element_info[i].change->events;
932 /* ---------- initialize push delay -------------------------------------- */
934 /* initialize push delay values to default */
935 for (i=0; i<MAX_NUM_ELEMENTS; i++)
937 if (!IS_CUSTOM_ELEMENT(i))
939 element_info[i].push_delay_fixed = game.default_push_delay_fixed;
940 element_info[i].push_delay_random = game.default_push_delay_random;
944 /* set push delay value for certain elements from pre-defined list */
945 for (i=0; push_delay_list[i].element != EL_UNDEFINED; i++)
947 int e = push_delay_list[i].element;
949 element_info[e].push_delay_fixed = push_delay_list[i].push_delay_fixed;
950 element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
953 /* ---------- initialize move stepsize ----------------------------------- */
955 /* initialize move stepsize values to default */
956 for (i=0; i<MAX_NUM_ELEMENTS; i++)
957 if (!IS_CUSTOM_ELEMENT(i))
958 element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
960 /* set move stepsize value for certain elements from pre-defined list */
961 for (i=0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
963 int e = move_stepsize_list[i].element;
965 element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
968 /* ---------- initialize gem count --------------------------------------- */
970 /* initialize gem count values for each element */
971 for (i=0; i<MAX_NUM_ELEMENTS; i++)
972 if (!IS_CUSTOM_ELEMENT(i))
973 element_info[i].collect_count = 0;
975 /* add gem count values for all elements from pre-defined list */
976 for (i=0; collect_count_list[i].element != EL_UNDEFINED; i++)
977 element_info[collect_count_list[i].element].collect_count =
978 collect_count_list[i].count;
983 =============================================================================
985 -----------------------------------------------------------------------------
986 initialize and start new game
987 =============================================================================
992 boolean emulate_bd = TRUE; /* unless non-BOULDERDASH elements found */
993 boolean emulate_sb = TRUE; /* unless non-SOKOBAN elements found */
994 boolean emulate_sp = TRUE; /* unless non-SUPAPLEX elements found */
1001 #if USE_NEW_AMOEBA_CODE
1002 printf("Using new amoeba code.\n");
1004 printf("Using old amoeba code.\n");
1009 /* don't play tapes over network */
1010 network_playing = (options.network && !tape.playing);
1012 for (i=0; i<MAX_PLAYERS; i++)
1014 struct PlayerInfo *player = &stored_player[i];
1016 player->index_nr = i;
1017 player->element_nr = EL_PLAYER_1 + i;
1019 player->present = FALSE;
1020 player->active = FALSE;
1023 player->effective_action = 0;
1024 player->programmed_action = 0;
1027 player->gems_still_needed = level.gems_needed;
1028 player->sokobanfields_still_needed = 0;
1029 player->lights_still_needed = 0;
1030 player->friends_still_needed = 0;
1033 player->key[j] = FALSE;
1035 player->dynabomb_count = 0;
1036 player->dynabomb_size = 1;
1037 player->dynabombs_left = 0;
1038 player->dynabomb_xl = FALSE;
1040 player->MovDir = MV_NO_MOVING;
1043 player->GfxDir = MV_NO_MOVING;
1044 player->GfxAction = ACTION_DEFAULT;
1046 player->StepFrame = 0;
1048 player->use_murphy_graphic = FALSE;
1050 player->actual_frame_counter = 0;
1052 player->last_move_dir = MV_NO_MOVING;
1054 player->is_waiting = FALSE;
1055 player->is_moving = FALSE;
1056 player->is_digging = FALSE;
1057 player->is_snapping = FALSE;
1058 player->is_collecting = FALSE;
1059 player->is_pushing = FALSE;
1060 player->is_switching = FALSE;
1062 player->switch_x = -1;
1063 player->switch_y = -1;
1065 player->show_envelope = 0;
1067 player->move_delay = game.initial_move_delay;
1068 player->move_delay_value = game.initial_move_delay_value;
1070 player->push_delay = 0;
1071 player->push_delay_value = game.initial_push_delay_value;
1073 player->last_jx = player->last_jy = 0;
1074 player->jx = player->jy = 0;
1076 player->shield_normal_time_left = 0;
1077 player->shield_deadly_time_left = 0;
1079 player->inventory_size = 0;
1081 DigField(player, 0, 0, 0, 0, DF_NO_PUSH);
1082 SnapField(player, 0, 0);
1084 player->LevelSolved = FALSE;
1085 player->GameOver = FALSE;
1088 network_player_action_received = FALSE;
1090 #if defined(PLATFORM_UNIX)
1091 /* initial null action */
1092 if (network_playing)
1093 SendToServer_MovePlayer(MV_NO_MOVING);
1101 TimeLeft = level.time;
1103 ScreenMovDir = MV_NO_MOVING;
1107 ScrollStepSize = 0; /* will be correctly initialized by ScrollScreen() */
1109 AllPlayersGone = FALSE;
1111 game.yamyam_content_nr = 0;
1112 game.magic_wall_active = FALSE;
1113 game.magic_wall_time_left = 0;
1114 game.light_time_left = 0;
1115 game.timegate_time_left = 0;
1116 game.switchgate_pos = 0;
1117 game.balloon_dir = MV_NO_MOVING;
1118 game.gravity = level.initial_gravity;
1119 game.explosions_delayed = TRUE;
1121 game.envelope_active = FALSE;
1125 game.belt_dir[i] = MV_NO_MOVING;
1126 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
1129 for (i=0; i<MAX_NUM_AMOEBA; i++)
1130 AmoebaCnt[i] = AmoebaCnt2[i] = 0;
1132 for (x=0; x<lev_fieldx; x++)
1134 for (y=0; y<lev_fieldy; y++)
1136 Feld[x][y] = level.field[x][y];
1137 MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
1138 ChangeDelay[x][y] = 0;
1139 ChangePage[x][y] = -1;
1140 Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
1142 WasJustMoving[x][y] = 0;
1143 WasJustFalling[x][y] = 0;
1145 Pushed[x][y] = FALSE;
1147 Changed[x][y] = CE_BITMASK_DEFAULT;
1148 ChangeEvent[x][y] = CE_BITMASK_DEFAULT;
1150 ExplodePhase[x][y] = 0;
1151 ExplodeField[x][y] = EX_NO_EXPLOSION;
1154 GfxRandom[x][y] = INIT_GFX_RANDOM();
1155 GfxElement[x][y] = EL_UNDEFINED;
1156 GfxAction[x][y] = ACTION_DEFAULT;
1157 GfxDir[x][y] = MV_NO_MOVING;
1161 for(y=0; y<lev_fieldy; y++)
1163 for(x=0; x<lev_fieldx; x++)
1165 if (emulate_bd && !IS_BD_ELEMENT(Feld[x][y]))
1167 if (emulate_sb && !IS_SB_ELEMENT(Feld[x][y]))
1169 if (emulate_sp && !IS_SP_ELEMENT(Feld[x][y]))
1172 InitField(x, y, TRUE);
1178 game.emulation = (emulate_bd ? EMU_BOULDERDASH :
1179 emulate_sb ? EMU_SOKOBAN :
1180 emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
1182 /* correct non-moving belts to start moving left */
1184 if (game.belt_dir[i] == MV_NO_MOVING)
1185 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
1187 /* check if any connected player was not found in playfield */
1188 for (i=0; i<MAX_PLAYERS; i++)
1190 struct PlayerInfo *player = &stored_player[i];
1192 if (player->connected && !player->present)
1194 for (j=0; j<MAX_PLAYERS; j++)
1196 struct PlayerInfo *some_player = &stored_player[j];
1197 int jx = some_player->jx, jy = some_player->jy;
1199 /* assign first free player found that is present in the playfield */
1200 if (some_player->present && !some_player->connected)
1202 player->present = TRUE;
1203 player->active = TRUE;
1204 some_player->present = FALSE;
1206 StorePlayer[jx][jy] = player->element_nr;
1207 player->jx = player->last_jx = jx;
1208 player->jy = player->last_jy = jy;
1218 /* when playing a tape, eliminate all players who do not participate */
1220 for (i=0; i<MAX_PLAYERS; i++)
1222 if (stored_player[i].active && !tape.player_participates[i])
1224 struct PlayerInfo *player = &stored_player[i];
1225 int jx = player->jx, jy = player->jy;
1227 player->active = FALSE;
1228 StorePlayer[jx][jy] = 0;
1229 Feld[jx][jy] = EL_EMPTY;
1233 else if (!options.network && !setup.team_mode) /* && !tape.playing */
1235 /* when in single player mode, eliminate all but the first active player */
1237 for (i=0; i<MAX_PLAYERS; i++)
1239 if (stored_player[i].active)
1241 for (j=i+1; j<MAX_PLAYERS; j++)
1243 if (stored_player[j].active)
1245 struct PlayerInfo *player = &stored_player[j];
1246 int jx = player->jx, jy = player->jy;
1248 player->active = FALSE;
1249 StorePlayer[jx][jy] = 0;
1250 Feld[jx][jy] = EL_EMPTY;
1257 /* when recording the game, store which players take part in the game */
1260 for (i=0; i<MAX_PLAYERS; i++)
1261 if (stored_player[i].active)
1262 tape.player_participates[i] = TRUE;
1267 for (i=0; i<MAX_PLAYERS; i++)
1269 struct PlayerInfo *player = &stored_player[i];
1271 printf("Player %d: present == %d, connected == %d, active == %d.\n",
1276 if (local_player == player)
1277 printf("Player %d is local player.\n", i+1);
1281 if (BorderElement == EL_EMPTY)
1284 SBX_Right = lev_fieldx - SCR_FIELDX;
1286 SBY_Lower = lev_fieldy - SCR_FIELDY;
1291 SBX_Right = lev_fieldx - SCR_FIELDX + 1;
1293 SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
1296 if (lev_fieldx + (SBX_Left == -1 ? 2 : 0) <= SCR_FIELDX)
1297 SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
1299 if (lev_fieldy + (SBY_Upper == -1 ? 2 : 0) <= SCR_FIELDY)
1300 SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
1302 /* if local player not found, look for custom element that might create
1303 the player (make some assumptions about the right custom element) */
1304 if (!local_player->present)
1306 int start_x = 0, start_y = 0;
1307 int found_rating = 0;
1308 int found_element = EL_UNDEFINED;
1310 for(y=0; y < lev_fieldy; y++) for(x=0; x < lev_fieldx; x++)
1312 int element = Feld[x][y];
1317 if (!IS_CUSTOM_ELEMENT(element))
1320 if (CAN_CHANGE(element))
1322 for (i=0; i < element_info[element].num_change_pages; i++)
1324 content = element_info[element].change_page[i].target_element;
1325 is_player = ELEM_IS_PLAYER(content);
1327 if (is_player && (found_rating < 3 || element < found_element))
1333 found_element = element;
1338 for(yy=0; yy < 3; yy++) for(xx=0; xx < 3; xx++)
1340 content = element_info[element].content[xx][yy];
1341 is_player = ELEM_IS_PLAYER(content);
1343 if (is_player && (found_rating < 2 || element < found_element))
1345 start_x = x + xx - 1;
1346 start_y = y + yy - 1;
1349 found_element = element;
1352 if (!CAN_CHANGE(element))
1355 for (i=0; i < element_info[element].num_change_pages; i++)
1357 content = element_info[element].change_page[i].content[xx][yy];
1358 is_player = ELEM_IS_PLAYER(content);
1360 if (is_player && (found_rating < 1 || element < found_element))
1362 start_x = x + xx - 1;
1363 start_y = y + yy - 1;
1366 found_element = element;
1372 scroll_x = (start_x < SBX_Left + MIDPOSX ? SBX_Left :
1373 start_x > SBX_Right + MIDPOSX ? SBX_Right :
1376 scroll_y = (start_y < SBY_Upper + MIDPOSY ? SBY_Upper :
1377 start_y > SBY_Lower + MIDPOSY ? SBY_Lower :
1383 scroll_x = (local_player->jx < SBX_Left + MIDPOSX ? SBX_Left :
1384 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
1385 local_player->jx - MIDPOSX);
1387 scroll_y = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
1388 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
1389 local_player->jy - MIDPOSY);
1391 scroll_x = SBX_Left;
1392 scroll_y = SBY_Upper;
1393 if (local_player->jx >= SBX_Left + MIDPOSX)
1394 scroll_x = (local_player->jx <= SBX_Right + MIDPOSX ?
1395 local_player->jx - MIDPOSX :
1397 if (local_player->jy >= SBY_Upper + MIDPOSY)
1398 scroll_y = (local_player->jy <= SBY_Lower + MIDPOSY ?
1399 local_player->jy - MIDPOSY :
1404 CloseDoor(DOOR_CLOSE_1);
1409 /* after drawing the level, correct some elements */
1410 if (game.timegate_time_left == 0)
1411 CloseAllOpenTimegates();
1413 if (setup.soft_scrolling)
1414 BlitBitmap(fieldbuffer, backbuffer, FX, FY, SXSIZE, SYSIZE, SX, SY);
1416 redraw_mask |= REDRAW_FROM_BACKBUFFER;
1419 /* copy default game door content to main double buffer */
1420 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
1421 DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
1424 DrawText(DX + XX_LEVEL, DY + YY_LEVEL, int2str(level_nr, 2), FONT_TEXT_2);
1427 DrawTextExt(drawto, DX + XX_EMERALDS, DY + YY_EMERALDS,
1428 int2str(level_nr, 3), FONT_LEVEL_NUMBER, BLIT_OPAQUE);
1429 BlitBitmap(drawto, drawto,
1430 DX + XX_EMERALDS, DY + YY_EMERALDS + 1,
1431 getFontWidth(FONT_LEVEL_NUMBER) * 3,
1432 getFontHeight(FONT_LEVEL_NUMBER) - 1,
1433 DX + XX_LEVEL - 1, DY + YY_LEVEL + 1);
1436 DrawGameDoorValues();
1440 game_gadget[SOUND_CTRL_ID_MUSIC]->checked = setup.sound_music;
1441 game_gadget[SOUND_CTRL_ID_LOOPS]->checked = setup.sound_loops;
1442 game_gadget[SOUND_CTRL_ID_SIMPLE]->checked = setup.sound_simple;
1446 /* copy actual game door content to door double buffer for OpenDoor() */
1447 BlitBitmap(drawto, bitmap_db_door,
1448 DX, DY, DXSIZE, DYSIZE, DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1);
1450 OpenDoor(DOOR_OPEN_ALL);
1452 PlaySoundStereo(SND_GAME_STARTING, SOUND_MIDDLE);
1454 if (setup.sound_music)
1457 KeyboardAutoRepeatOffUnlessAutoplay();
1462 printf("Player %d %sactive.\n",
1463 i + 1, (stored_player[i].active ? "" : "not "));
1467 printf("::: starting game [%d]\n", FrameCounter);
1471 void InitMovDir(int x, int y)
1473 int i, element = Feld[x][y];
1474 static int xy[4][2] =
1481 static int direction[3][4] =
1483 { MV_RIGHT, MV_UP, MV_LEFT, MV_DOWN },
1484 { MV_LEFT, MV_DOWN, MV_RIGHT, MV_UP },
1485 { MV_LEFT, MV_RIGHT, MV_UP, MV_DOWN }
1494 Feld[x][y] = EL_BUG;
1495 MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
1498 case EL_SPACESHIP_RIGHT:
1499 case EL_SPACESHIP_UP:
1500 case EL_SPACESHIP_LEFT:
1501 case EL_SPACESHIP_DOWN:
1502 Feld[x][y] = EL_SPACESHIP;
1503 MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
1506 case EL_BD_BUTTERFLY_RIGHT:
1507 case EL_BD_BUTTERFLY_UP:
1508 case EL_BD_BUTTERFLY_LEFT:
1509 case EL_BD_BUTTERFLY_DOWN:
1510 Feld[x][y] = EL_BD_BUTTERFLY;
1511 MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
1514 case EL_BD_FIREFLY_RIGHT:
1515 case EL_BD_FIREFLY_UP:
1516 case EL_BD_FIREFLY_LEFT:
1517 case EL_BD_FIREFLY_DOWN:
1518 Feld[x][y] = EL_BD_FIREFLY;
1519 MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
1522 case EL_PACMAN_RIGHT:
1524 case EL_PACMAN_LEFT:
1525 case EL_PACMAN_DOWN:
1526 Feld[x][y] = EL_PACMAN;
1527 MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
1530 case EL_SP_SNIKSNAK:
1531 MovDir[x][y] = MV_UP;
1534 case EL_SP_ELECTRON:
1535 MovDir[x][y] = MV_LEFT;
1542 Feld[x][y] = EL_MOLE;
1543 MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
1547 if (IS_CUSTOM_ELEMENT(element))
1549 if (element_info[element].move_direction_initial != MV_NO_MOVING)
1550 MovDir[x][y] = element_info[element].move_direction_initial;
1551 else if (element_info[element].move_pattern == MV_ALL_DIRECTIONS ||
1552 element_info[element].move_pattern == MV_TURNING_LEFT ||
1553 element_info[element].move_pattern == MV_TURNING_RIGHT)
1554 MovDir[x][y] = 1 << RND(4);
1555 else if (element_info[element].move_pattern == MV_HORIZONTAL)
1556 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
1557 else if (element_info[element].move_pattern == MV_VERTICAL)
1558 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
1559 else if (element_info[element].move_pattern & MV_ANY_DIRECTION)
1560 MovDir[x][y] = element_info[element].move_pattern;
1561 else if (element_info[element].move_pattern == MV_ALONG_LEFT_SIDE ||
1562 element_info[element].move_pattern == MV_ALONG_RIGHT_SIDE)
1566 int x1 = x + xy[i][0];
1567 int y1 = y + xy[i][1];
1569 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
1571 if (element_info[element].move_pattern == MV_ALONG_RIGHT_SIDE)
1572 MovDir[x][y] = direction[0][i];
1574 MovDir[x][y] = direction[1][i];
1583 MovDir[x][y] = 1 << RND(4);
1585 if (element != EL_BUG &&
1586 element != EL_SPACESHIP &&
1587 element != EL_BD_BUTTERFLY &&
1588 element != EL_BD_FIREFLY)
1593 int x1 = x + xy[i][0];
1594 int y1 = y + xy[i][1];
1596 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
1598 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
1600 MovDir[x][y] = direction[0][i];
1603 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
1604 element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
1606 MovDir[x][y] = direction[1][i];
1615 GfxDir[x][y] = MovDir[x][y];
1618 void InitAmoebaNr(int x, int y)
1621 int group_nr = AmoebeNachbarNr(x, y);
1625 for (i=1; i<MAX_NUM_AMOEBA; i++)
1627 if (AmoebaCnt[i] == 0)
1635 AmoebaNr[x][y] = group_nr;
1636 AmoebaCnt[group_nr]++;
1637 AmoebaCnt2[group_nr]++;
1643 boolean raise_level = FALSE;
1645 if (local_player->MovPos)
1649 if (tape.auto_play) /* tape might already be stopped here */
1650 tape.auto_play_level_solved = TRUE;
1652 if (tape.playing && tape.auto_play)
1653 tape.auto_play_level_solved = TRUE;
1656 local_player->LevelSolved = FALSE;
1658 PlaySoundStereo(SND_GAME_WINNING, SOUND_MIDDLE);
1662 if (!tape.playing && setup.sound_loops)
1663 PlaySoundExt(SND_GAME_LEVELTIME_BONUS, SOUND_MAX_VOLUME, SOUND_MIDDLE,
1664 SND_CTRL_PLAY_LOOP);
1666 while (TimeLeft > 0)
1668 if (!tape.playing && !setup.sound_loops)
1669 PlaySoundStereo(SND_GAME_LEVELTIME_BONUS, SOUND_MIDDLE);
1670 if (TimeLeft > 0 && !(TimeLeft % 10))
1671 RaiseScore(level.score[SC_TIME_BONUS]);
1672 if (TimeLeft > 100 && !(TimeLeft % 10))
1676 DrawText(DX_TIME, DY_TIME, int2str(TimeLeft, 3), FONT_TEXT_2);
1683 if (!tape.playing && setup.sound_loops)
1684 StopSound(SND_GAME_LEVELTIME_BONUS);
1686 else if (level.time == 0) /* level without time limit */
1688 if (!tape.playing && setup.sound_loops)
1689 PlaySoundExt(SND_GAME_LEVELTIME_BONUS, SOUND_MAX_VOLUME, SOUND_MIDDLE,
1690 SND_CTRL_PLAY_LOOP);
1692 while (TimePlayed < 999)
1694 if (!tape.playing && !setup.sound_loops)
1695 PlaySoundStereo(SND_GAME_LEVELTIME_BONUS, SOUND_MIDDLE);
1696 if (TimePlayed < 999 && !(TimePlayed % 10))
1697 RaiseScore(level.score[SC_TIME_BONUS]);
1698 if (TimePlayed < 900 && !(TimePlayed % 10))
1702 DrawText(DX_TIME, DY_TIME, int2str(TimePlayed, 3), FONT_TEXT_2);
1709 if (!tape.playing && setup.sound_loops)
1710 StopSound(SND_GAME_LEVELTIME_BONUS);
1713 /* close exit door after last player */
1714 if ((Feld[ExitX][ExitY] == EL_EXIT_OPEN ||
1715 Feld[ExitX][ExitY] == EL_SP_EXIT_OPEN) && AllPlayersGone)
1717 int element = Feld[ExitX][ExitY];
1719 Feld[ExitX][ExitY] = (element == EL_EXIT_OPEN ? EL_EXIT_CLOSING :
1720 EL_SP_EXIT_CLOSING);
1722 PlayLevelSoundElementAction(ExitX, ExitY, element, ACTION_CLOSING);
1725 /* Hero disappears */
1726 DrawLevelField(ExitX, ExitY);
1732 CloseDoor(DOOR_CLOSE_1);
1737 SaveTape(tape.level_nr); /* Ask to save tape */
1740 if (level_nr == leveldir_current->handicap_level)
1742 leveldir_current->handicap_level++;
1743 SaveLevelSetup_SeriesInfo();
1746 if (level_editor_test_game)
1747 local_player->score = -1; /* no highscore when playing from editor */
1748 else if (level_nr < leveldir_current->last_level)
1749 raise_level = TRUE; /* advance to next level */
1751 if ((hi_pos = NewHiScore()) >= 0)
1753 game_status = GAME_MODE_SCORES;
1754 DrawHallOfFame(hi_pos);
1763 game_status = GAME_MODE_MAIN;
1780 LoadScore(level_nr);
1782 if (strcmp(setup.player_name, EMPTY_PLAYER_NAME) == 0 ||
1783 local_player->score < highscore[MAX_SCORE_ENTRIES - 1].Score)
1786 for (k=0; k<MAX_SCORE_ENTRIES; k++)
1788 if (local_player->score > highscore[k].Score)
1790 /* player has made it to the hall of fame */
1792 if (k < MAX_SCORE_ENTRIES - 1)
1794 int m = MAX_SCORE_ENTRIES - 1;
1797 for (l=k; l<MAX_SCORE_ENTRIES; l++)
1798 if (!strcmp(setup.player_name, highscore[l].Name))
1800 if (m == k) /* player's new highscore overwrites his old one */
1806 strcpy(highscore[l].Name, highscore[l - 1].Name);
1807 highscore[l].Score = highscore[l - 1].Score;
1814 strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
1815 highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
1816 highscore[k].Score = local_player->score;
1822 else if (!strncmp(setup.player_name, highscore[k].Name,
1823 MAX_PLAYER_NAME_LEN))
1824 break; /* player already there with a higher score */
1830 SaveScore(level_nr);
1835 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
1837 if (player->GfxAction != action || player->GfxDir != dir)
1840 printf("Player frame reset! (%d => %d, %d => %d)\n",
1841 player->GfxAction, action, player->GfxDir, dir);
1844 player->GfxAction = action;
1845 player->GfxDir = dir;
1847 player->StepFrame = 0;
1851 static void ResetRandomAnimationValue(int x, int y)
1853 GfxRandom[x][y] = INIT_GFX_RANDOM();
1856 static void ResetGfxAnimation(int x, int y)
1859 GfxAction[x][y] = ACTION_DEFAULT;
1860 GfxDir[x][y] = MovDir[x][y];
1863 void InitMovingField(int x, int y, int direction)
1865 int element = Feld[x][y];
1866 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
1867 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
1871 if (!WasJustMoving[x][y] || direction != MovDir[x][y])
1872 ResetGfxAnimation(x, y);
1874 MovDir[newx][newy] = MovDir[x][y] = direction;
1875 GfxDir[x][y] = direction;
1877 if (Feld[newx][newy] == EL_EMPTY)
1878 Feld[newx][newy] = EL_BLOCKED;
1880 if (direction == MV_DOWN && CAN_FALL(element))
1881 GfxAction[x][y] = ACTION_FALLING;
1883 GfxAction[x][y] = ACTION_MOVING;
1885 GfxFrame[newx][newy] = GfxFrame[x][y];
1886 GfxRandom[newx][newy] = GfxRandom[x][y];
1887 GfxAction[newx][newy] = GfxAction[x][y];
1888 GfxDir[newx][newy] = GfxDir[x][y];
1891 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
1893 int direction = MovDir[x][y];
1894 int newx = x + (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
1895 int newy = y + (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
1901 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
1903 int oldx = x, oldy = y;
1904 int direction = MovDir[x][y];
1906 if (direction == MV_LEFT)
1908 else if (direction == MV_RIGHT)
1910 else if (direction == MV_UP)
1912 else if (direction == MV_DOWN)
1915 *comes_from_x = oldx;
1916 *comes_from_y = oldy;
1919 int MovingOrBlocked2Element(int x, int y)
1921 int element = Feld[x][y];
1923 if (element == EL_BLOCKED)
1927 Blocked2Moving(x, y, &oldx, &oldy);
1928 return Feld[oldx][oldy];
1934 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
1936 /* like MovingOrBlocked2Element(), but if element is moving
1937 and (x,y) is the field the moving element is just leaving,
1938 return EL_BLOCKED instead of the element value */
1939 int element = Feld[x][y];
1941 if (IS_MOVING(x, y))
1943 if (element == EL_BLOCKED)
1947 Blocked2Moving(x, y, &oldx, &oldy);
1948 return Feld[oldx][oldy];
1957 static void RemoveField(int x, int y)
1959 Feld[x][y] = EL_EMPTY;
1966 ChangeDelay[x][y] = 0;
1967 ChangePage[x][y] = -1;
1968 Pushed[x][y] = FALSE;
1970 GfxElement[x][y] = EL_UNDEFINED;
1971 GfxAction[x][y] = ACTION_DEFAULT;
1972 GfxDir[x][y] = MV_NO_MOVING;
1975 void RemoveMovingField(int x, int y)
1977 int oldx = x, oldy = y, newx = x, newy = y;
1978 int element = Feld[x][y];
1979 int next_element = EL_UNDEFINED;
1981 if (element != EL_BLOCKED && !IS_MOVING(x, y))
1984 if (IS_MOVING(x, y))
1986 Moving2Blocked(x, y, &newx, &newy);
1987 if (Feld[newx][newy] != EL_BLOCKED)
1990 else if (element == EL_BLOCKED)
1992 Blocked2Moving(x, y, &oldx, &oldy);
1993 if (!IS_MOVING(oldx, oldy))
1997 if (element == EL_BLOCKED &&
1998 (Feld[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
1999 Feld[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
2000 Feld[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
2001 Feld[oldx][oldy] == EL_AMOEBA_DROPPING))
2002 next_element = get_next_element(Feld[oldx][oldy]);
2004 RemoveField(oldx, oldy);
2005 RemoveField(newx, newy);
2007 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
2009 if (next_element != EL_UNDEFINED)
2010 Feld[oldx][oldy] = next_element;
2012 DrawLevelField(oldx, oldy);
2013 DrawLevelField(newx, newy);
2016 void DrawDynamite(int x, int y)
2018 int sx = SCREENX(x), sy = SCREENY(y);
2019 int graphic = el2img(Feld[x][y]);
2022 if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
2025 if (IS_WALKABLE_INSIDE(Back[x][y]))
2029 DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
2030 else if (Store[x][y])
2031 DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
2033 frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
2036 if (Back[x][y] || Store[x][y])
2037 DrawGraphicThruMask(sx, sy, graphic, frame);
2039 DrawGraphic(sx, sy, graphic, frame);
2041 if (game.emulation == EMU_SUPAPLEX)
2042 DrawGraphic(sx, sy, IMG_SP_DISK_RED, frame);
2043 else if (Store[x][y])
2044 DrawGraphicThruMask(sx, sy, graphic, frame);
2046 DrawGraphic(sx, sy, graphic, frame);
2050 void CheckDynamite(int x, int y)
2052 if (MovDelay[x][y] != 0) /* dynamite is still waiting to explode */
2056 if (MovDelay[x][y] != 0)
2059 PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
2066 StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
2068 if (Feld[x][y] == EL_DYNAMITE_ACTIVE ||
2069 Feld[x][y] == EL_SP_DISK_RED_ACTIVE)
2070 StopSound(SND_DYNAMITE_ACTIVE);
2072 StopSound(SND_DYNABOMB_ACTIVE);
2078 void RelocatePlayer(int x, int y, int element)
2080 struct PlayerInfo *player = &stored_player[element - EL_PLAYER_1];
2083 RemoveField(x, y); /* temporarily remove newly placed player */
2084 DrawLevelField(x, y);
2087 if (player->present)
2089 while (player->MovPos)
2091 ScrollPlayer(player, SCROLL_GO_ON);
2092 ScrollScreen(NULL, SCROLL_GO_ON);
2098 Delay(GAME_FRAME_DELAY);
2101 DrawPlayer(player); /* needed here only to cleanup last field */
2102 DrawLevelField(player->jx, player->jy); /* remove player graphic */
2104 player->is_moving = FALSE;
2107 Feld[x][y] = element;
2108 InitPlayerField(x, y, element, TRUE);
2110 if (player == local_player)
2112 int scroll_xx = -999, scroll_yy = -999;
2114 while (scroll_xx != scroll_x || scroll_yy != scroll_y)
2117 int fx = FX, fy = FY;
2119 scroll_xx = (local_player->jx < SBX_Left + MIDPOSX ? SBX_Left :
2120 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
2121 local_player->jx - MIDPOSX);
2123 scroll_yy = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
2124 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
2125 local_player->jy - MIDPOSY);
2127 dx = (scroll_xx < scroll_x ? +1 : scroll_xx > scroll_x ? -1 : 0);
2128 dy = (scroll_yy < scroll_y ? +1 : scroll_yy > scroll_y ? -1 : 0);
2133 fx += dx * TILEX / 2;
2134 fy += dy * TILEY / 2;
2136 ScrollLevel(dx, dy);
2139 /* scroll in two steps of half tile size to make things smoother */
2140 BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
2142 Delay(GAME_FRAME_DELAY);
2144 /* scroll second step to align at full tile size */
2146 Delay(GAME_FRAME_DELAY);
2151 void Explode(int ex, int ey, int phase, int mode)
2155 int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
2156 int last_phase = num_phase * delay;
2157 int half_phase = (num_phase / 2) * delay;
2158 int first_phase_after_start = EX_PHASE_START + 1;
2160 if (game.explosions_delayed)
2162 ExplodeField[ex][ey] = mode;
2166 if (phase == EX_PHASE_START) /* initialize 'Store[][]' field */
2168 int center_element = Feld[ex][ey];
2171 /* --- This is only really needed (and now handled) in "Impact()". --- */
2172 /* do not explode moving elements that left the explode field in time */
2173 if (game.engine_version >= VERSION_IDENT(2,2,0,7) &&
2174 center_element == EL_EMPTY && (mode == EX_NORMAL || mode == EX_CENTER))
2178 if (mode == EX_NORMAL || mode == EX_CENTER)
2179 PlayLevelSoundAction(ex, ey, ACTION_EXPLODING);
2181 /* remove things displayed in background while burning dynamite */
2182 if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
2185 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
2187 /* put moving element to center field (and let it explode there) */
2188 center_element = MovingOrBlocked2Element(ex, ey);
2189 RemoveMovingField(ex, ey);
2190 Feld[ex][ey] = center_element;
2193 for (y = ey - 1; y <= ey + 1; y++) for(x = ex - 1; x <= ex + 1; x++)
2195 int xx = x - ex + 1;
2196 int yy = y - ey + 1;
2199 if (!IN_LEV_FIELD(x, y) ||
2200 ((mode != EX_NORMAL || center_element == EL_AMOEBA_TO_DIAMOND) &&
2201 (x != ex || y != ey)))
2204 element = Feld[x][y];
2206 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
2208 element = MovingOrBlocked2Element(x, y);
2210 if (!IS_EXPLOSION_PROOF(element))
2211 RemoveMovingField(x, y);
2217 if (IS_EXPLOSION_PROOF(element))
2220 /* indestructible elements can only explode in center (but not flames) */
2221 if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey)) ||
2222 element == EL_FLAMES)
2227 if ((IS_INDESTRUCTIBLE(element) &&
2228 (game.engine_version < VERSION_IDENT(2,2,0,0) ||
2229 (!IS_WALKABLE_OVER(element) && !IS_WALKABLE_UNDER(element)))) ||
2230 element == EL_FLAMES)
2234 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
2236 if (IS_ACTIVE_BOMB(element))
2238 /* re-activate things under the bomb like gate or penguin */
2239 Feld[x][y] = (Store[x][y] ? Store[x][y] : EL_EMPTY);
2246 /* save walkable background elements while explosion on same tile */
2248 if (IS_INDESTRUCTIBLE(element))
2249 Back[x][y] = element;
2251 if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element))
2252 Back[x][y] = element;
2255 /* ignite explodable elements reached by other explosion */
2256 if (element == EL_EXPLOSION)
2257 element = Store2[x][y];
2260 if (AmoebaNr[x][y] &&
2261 (element == EL_AMOEBA_FULL ||
2262 element == EL_BD_AMOEBA ||
2263 element == EL_AMOEBA_GROWING))
2265 AmoebaCnt[AmoebaNr[x][y]]--;
2266 AmoebaCnt2[AmoebaNr[x][y]]--;
2272 if (IS_PLAYER(ex, ey) && !PLAYER_PROTECTED(ex, ey))
2274 switch(StorePlayer[ex][ey])
2277 Store[x][y] = EL_EMERALD_RED;
2280 Store[x][y] = EL_EMERALD;
2283 Store[x][y] = EL_EMERALD_PURPLE;
2287 Store[x][y] = EL_EMERALD_YELLOW;
2291 if (game.emulation == EMU_SUPAPLEX)
2292 Store[x][y] = EL_EMPTY;
2294 else if (center_element == EL_MOLE)
2295 Store[x][y] = EL_EMERALD_RED;
2296 else if (center_element == EL_PENGUIN)
2297 Store[x][y] = EL_EMERALD_PURPLE;
2298 else if (center_element == EL_BUG)
2299 Store[x][y] = ((x == ex && y == ey) ? EL_DIAMOND : EL_EMERALD);
2300 else if (center_element == EL_BD_BUTTERFLY)
2301 Store[x][y] = EL_BD_DIAMOND;
2302 else if (center_element == EL_SP_ELECTRON)
2303 Store[x][y] = EL_SP_INFOTRON;
2304 else if (center_element == EL_AMOEBA_TO_DIAMOND)
2305 Store[x][y] = level.amoeba_content;
2306 else if (center_element == EL_YAMYAM)
2307 Store[x][y] = level.yamyam_content[game.yamyam_content_nr][xx][yy];
2308 else if (IS_CUSTOM_ELEMENT(center_element) &&
2309 element_info[center_element].content[xx][yy] != EL_EMPTY)
2310 Store[x][y] = element_info[center_element].content[xx][yy];
2311 else if (element == EL_WALL_EMERALD)
2312 Store[x][y] = EL_EMERALD;
2313 else if (element == EL_WALL_DIAMOND)
2314 Store[x][y] = EL_DIAMOND;
2315 else if (element == EL_WALL_BD_DIAMOND)
2316 Store[x][y] = EL_BD_DIAMOND;
2317 else if (element == EL_WALL_EMERALD_YELLOW)
2318 Store[x][y] = EL_EMERALD_YELLOW;
2319 else if (element == EL_WALL_EMERALD_RED)
2320 Store[x][y] = EL_EMERALD_RED;
2321 else if (element == EL_WALL_EMERALD_PURPLE)
2322 Store[x][y] = EL_EMERALD_PURPLE;
2323 else if (element == EL_WALL_PEARL)
2324 Store[x][y] = EL_PEARL;
2325 else if (element == EL_WALL_CRYSTAL)
2326 Store[x][y] = EL_CRYSTAL;
2327 else if (IS_CUSTOM_ELEMENT(element) && !CAN_EXPLODE(element))
2328 Store[x][y] = element_info[element].content[1][1];
2330 Store[x][y] = EL_EMPTY;
2332 if (x != ex || y != ey ||
2333 center_element == EL_AMOEBA_TO_DIAMOND || mode == EX_BORDER)
2334 Store2[x][y] = element;
2337 if (AmoebaNr[x][y] &&
2338 (element == EL_AMOEBA_FULL ||
2339 element == EL_BD_AMOEBA ||
2340 element == EL_AMOEBA_GROWING))
2342 AmoebaCnt[AmoebaNr[x][y]]--;
2343 AmoebaCnt2[AmoebaNr[x][y]]--;
2349 MovDir[x][y] = MovPos[x][y] = 0;
2350 GfxDir[x][y] = MovDir[x][y];
2355 Feld[x][y] = EL_EXPLOSION;
2357 GfxElement[x][y] = center_element;
2359 GfxElement[x][y] = EL_UNDEFINED;
2362 ExplodePhase[x][y] = 1;
2366 if (center_element == EL_YAMYAM)
2367 game.yamyam_content_nr =
2368 (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
2379 ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
2383 /* activate this even in non-DEBUG version until cause for crash in
2384 getGraphicAnimationFrame() (see below) is found and eliminated */
2388 if (GfxElement[x][y] == EL_UNDEFINED)
2391 printf("Explode(): x = %d, y = %d: GfxElement == EL_UNDEFINED\n", x, y);
2392 printf("Explode(): This should never happen!\n");
2395 GfxElement[x][y] = EL_EMPTY;
2399 if (phase == first_phase_after_start)
2401 int element = Store2[x][y];
2403 if (element == EL_BLACK_ORB)
2405 Feld[x][y] = Store2[x][y];
2410 else if (phase == half_phase)
2412 int element = Store2[x][y];
2414 if (IS_PLAYER(x, y))
2415 KillHeroUnlessProtected(x, y);
2416 else if (CAN_EXPLODE_BY_FIRE(element))
2418 Feld[x][y] = Store2[x][y];
2422 else if (element == EL_AMOEBA_TO_DIAMOND)
2423 AmoebeUmwandeln(x, y);
2426 if (phase == last_phase)
2430 element = Feld[x][y] = Store[x][y];
2431 Store[x][y] = Store2[x][y] = 0;
2432 GfxElement[x][y] = EL_UNDEFINED;
2434 if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
2435 element = Feld[x][y] = Back[x][y];
2438 MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
2439 GfxDir[x][y] = MV_NO_MOVING;
2440 ChangeDelay[x][y] = 0;
2441 ChangePage[x][y] = -1;
2443 InitField(x, y, FALSE);
2444 if (CAN_MOVE(element))
2446 DrawLevelField(x, y);
2448 TestIfElementTouchesCustomElement(x, y);
2450 if (GFX_CRUMBLED(element))
2451 DrawLevelFieldCrumbledSandNeighbours(x, y);
2453 if (IS_PLAYER(x, y) && !PLAYERINFO(x,y)->present)
2454 StorePlayer[x][y] = 0;
2456 if (ELEM_IS_PLAYER(element))
2457 RelocatePlayer(x, y, element);
2459 else if (phase >= delay && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
2462 int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
2464 int stored = Store[x][y];
2465 int graphic = (game.emulation != EMU_SUPAPLEX ? IMG_EXPLOSION :
2466 stored == EL_SP_INFOTRON ? IMG_SP_EXPLOSION_INFOTRON :
2469 int frame = getGraphicAnimationFrame(graphic, phase - delay);
2472 DrawLevelFieldCrumbledSand(x, y);
2474 if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
2476 DrawLevelElement(x, y, Back[x][y]);
2477 DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
2479 else if (IS_WALKABLE_UNDER(Back[x][y]))
2481 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
2482 DrawLevelElementThruMask(x, y, Back[x][y]);
2484 else if (!IS_WALKABLE_INSIDE(Back[x][y]))
2485 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
2489 void DynaExplode(int ex, int ey)
2492 int dynabomb_size = 1;
2493 boolean dynabomb_xl = FALSE;
2494 struct PlayerInfo *player;
2495 static int xy[4][2] =
2503 if (IS_ACTIVE_BOMB(Feld[ex][ey]))
2505 player = &stored_player[Feld[ex][ey] - EL_DYNABOMB_PLAYER_1_ACTIVE];
2506 dynabomb_size = player->dynabomb_size;
2507 dynabomb_xl = player->dynabomb_xl;
2508 player->dynabombs_left++;
2511 Explode(ex, ey, EX_PHASE_START, EX_CENTER);
2515 for (j=1; j<=dynabomb_size; j++)
2517 int x = ex + j * xy[i % 4][0];
2518 int y = ey + j * xy[i % 4][1];
2521 if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
2524 element = Feld[x][y];
2526 /* do not restart explosions of fields with active bombs */
2527 if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
2530 Explode(x, y, EX_PHASE_START, EX_BORDER);
2532 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
2533 if (element != EL_EMPTY &&
2534 element != EL_SAND &&
2535 element != EL_EXPLOSION &&
2542 void Bang(int x, int y)
2545 int element = MovingOrBlocked2Element(x, y);
2547 int element = Feld[x][y];
2551 if (IS_PLAYER(x, y) && !PLAYER_PROTECTED(x, y))
2553 if (IS_PLAYER(x, y))
2556 struct PlayerInfo *player = PLAYERINFO(x, y);
2558 element = Feld[x][y] = (player->use_murphy_graphic ? EL_SP_MURPHY :
2559 player->element_nr);
2564 PlayLevelSoundAction(x, y, ACTION_EXPLODING);
2566 if (game.emulation == EMU_SUPAPLEX)
2567 PlayLevelSound(x, y, SND_SP_ELEMENT_EXPLODING);
2569 PlayLevelSound(x, y, SND_ELEMENT_EXPLODING);
2574 if (IS_PLAYER(x, y)) /* remove objects that might cause smaller explosion */
2582 case EL_BD_BUTTERFLY:
2585 case EL_DARK_YAMYAM:
2589 RaiseScoreElement(element);
2590 Explode(x, y, EX_PHASE_START, EX_NORMAL);
2592 case EL_DYNABOMB_PLAYER_1_ACTIVE:
2593 case EL_DYNABOMB_PLAYER_2_ACTIVE:
2594 case EL_DYNABOMB_PLAYER_3_ACTIVE:
2595 case EL_DYNABOMB_PLAYER_4_ACTIVE:
2596 case EL_DYNABOMB_INCREASE_NUMBER:
2597 case EL_DYNABOMB_INCREASE_SIZE:
2598 case EL_DYNABOMB_INCREASE_POWER:
2603 case EL_LAMP_ACTIVE:
2604 if (IS_PLAYER(x, y))
2605 Explode(x, y, EX_PHASE_START, EX_NORMAL);
2607 Explode(x, y, EX_PHASE_START, EX_CENTER);
2610 if (CAN_EXPLODE_1X1(element))
2611 Explode(x, y, EX_PHASE_START, EX_CENTER);
2613 Explode(x, y, EX_PHASE_START, EX_NORMAL);
2617 CheckTriggeredElementChange(x, y, element, CE_OTHER_IS_EXPLODING);
2620 void SplashAcid(int x, int y)
2622 int element = Feld[x][y];
2624 if (element != EL_ACID_SPLASH_LEFT &&
2625 element != EL_ACID_SPLASH_RIGHT)
2627 PlayLevelSound(x, y, SND_ACID_SPLASHING);
2629 if (IN_LEV_FIELD(x-1, y) && IS_FREE(x-1, y) &&
2630 (!IN_LEV_FIELD(x-1, y-1) ||
2631 !CAN_FALL(MovingOrBlocked2Element(x-1, y-1))))
2632 Feld[x-1][y] = EL_ACID_SPLASH_LEFT;
2634 if (IN_LEV_FIELD(x+1, y) && IS_FREE(x+1, y) &&
2635 (!IN_LEV_FIELD(x+1, y-1) ||
2636 !CAN_FALL(MovingOrBlocked2Element(x+1, y-1))))
2637 Feld[x+1][y] = EL_ACID_SPLASH_RIGHT;
2641 static void InitBeltMovement()
2643 static int belt_base_element[4] =
2645 EL_CONVEYOR_BELT_1_LEFT,
2646 EL_CONVEYOR_BELT_2_LEFT,
2647 EL_CONVEYOR_BELT_3_LEFT,
2648 EL_CONVEYOR_BELT_4_LEFT
2650 static int belt_base_active_element[4] =
2652 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
2653 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
2654 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
2655 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
2660 /* set frame order for belt animation graphic according to belt direction */
2667 int element = belt_base_active_element[belt_nr] + j;
2668 int graphic = el2img(element);
2670 if (game.belt_dir[i] == MV_LEFT)
2671 graphic_info[graphic].anim_mode &= ~ANIM_REVERSE;
2673 graphic_info[graphic].anim_mode |= ANIM_REVERSE;
2677 for(y=0; y<lev_fieldy; y++)
2679 for(x=0; x<lev_fieldx; x++)
2681 int element = Feld[x][y];
2685 if (IS_BELT(element) && game.belt_dir[i] != MV_NO_MOVING)
2687 int e_belt_nr = getBeltNrFromBeltElement(element);
2690 if (e_belt_nr == belt_nr)
2692 int belt_part = Feld[x][y] - belt_base_element[belt_nr];
2694 Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
2702 static void ToggleBeltSwitch(int x, int y)
2704 static int belt_base_element[4] =
2706 EL_CONVEYOR_BELT_1_LEFT,
2707 EL_CONVEYOR_BELT_2_LEFT,
2708 EL_CONVEYOR_BELT_3_LEFT,
2709 EL_CONVEYOR_BELT_4_LEFT
2711 static int belt_base_active_element[4] =
2713 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
2714 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
2715 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
2716 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
2718 static int belt_base_switch_element[4] =
2720 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
2721 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
2722 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
2723 EL_CONVEYOR_BELT_4_SWITCH_LEFT
2725 static int belt_move_dir[4] =
2733 int element = Feld[x][y];
2734 int belt_nr = getBeltNrFromBeltSwitchElement(element);
2735 int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
2736 int belt_dir = belt_move_dir[belt_dir_nr];
2739 if (!IS_BELT_SWITCH(element))
2742 game.belt_dir_nr[belt_nr] = belt_dir_nr;
2743 game.belt_dir[belt_nr] = belt_dir;
2745 if (belt_dir_nr == 3)
2748 /* set frame order for belt animation graphic according to belt direction */
2751 int element = belt_base_active_element[belt_nr] + i;
2752 int graphic = el2img(element);
2754 if (belt_dir == MV_LEFT)
2755 graphic_info[graphic].anim_mode &= ~ANIM_REVERSE;
2757 graphic_info[graphic].anim_mode |= ANIM_REVERSE;
2760 for (yy=0; yy<lev_fieldy; yy++)
2762 for (xx=0; xx<lev_fieldx; xx++)
2764 int element = Feld[xx][yy];
2766 if (IS_BELT_SWITCH(element))
2768 int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
2770 if (e_belt_nr == belt_nr)
2772 Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
2773 DrawLevelField(xx, yy);
2776 else if (IS_BELT(element) && belt_dir != MV_NO_MOVING)
2778 int e_belt_nr = getBeltNrFromBeltElement(element);
2780 if (e_belt_nr == belt_nr)
2782 int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
2784 Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
2785 DrawLevelField(xx, yy);
2788 else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NO_MOVING)
2790 int e_belt_nr = getBeltNrFromBeltActiveElement(element);
2792 if (e_belt_nr == belt_nr)
2794 int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
2796 Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
2797 DrawLevelField(xx, yy);
2804 static void ToggleSwitchgateSwitch(int x, int y)
2808 game.switchgate_pos = !game.switchgate_pos;
2810 for (yy=0; yy<lev_fieldy; yy++)
2812 for (xx=0; xx<lev_fieldx; xx++)
2814 int element = Feld[xx][yy];
2816 if (element == EL_SWITCHGATE_SWITCH_UP ||
2817 element == EL_SWITCHGATE_SWITCH_DOWN)
2819 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
2820 DrawLevelField(xx, yy);
2822 else if (element == EL_SWITCHGATE_OPEN ||
2823 element == EL_SWITCHGATE_OPENING)
2825 Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
2827 PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
2829 PlayLevelSound(xx, yy, SND_SWITCHGATE_CLOSING);
2832 else if (element == EL_SWITCHGATE_CLOSED ||
2833 element == EL_SWITCHGATE_CLOSING)
2835 Feld[xx][yy] = EL_SWITCHGATE_OPENING;
2837 PlayLevelSoundAction(xx, yy, ACTION_OPENING);
2839 PlayLevelSound(xx, yy, SND_SWITCHGATE_OPENING);
2846 static int getInvisibleActiveFromInvisibleElement(int element)
2848 return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
2849 element == EL_INVISIBLE_WALL ? EL_INVISIBLE_WALL_ACTIVE :
2850 element == EL_INVISIBLE_SAND ? EL_INVISIBLE_SAND_ACTIVE :
2854 static int getInvisibleFromInvisibleActiveElement(int element)
2856 return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
2857 element == EL_INVISIBLE_WALL_ACTIVE ? EL_INVISIBLE_WALL :
2858 element == EL_INVISIBLE_SAND_ACTIVE ? EL_INVISIBLE_SAND :
2862 static void RedrawAllLightSwitchesAndInvisibleElements()
2866 for (y=0; y<lev_fieldy; y++)
2868 for (x=0; x<lev_fieldx; x++)
2870 int element = Feld[x][y];
2872 if (element == EL_LIGHT_SWITCH &&
2873 game.light_time_left > 0)
2875 Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
2876 DrawLevelField(x, y);
2878 else if (element == EL_LIGHT_SWITCH_ACTIVE &&
2879 game.light_time_left == 0)
2881 Feld[x][y] = EL_LIGHT_SWITCH;
2882 DrawLevelField(x, y);
2884 else if (element == EL_INVISIBLE_STEELWALL ||
2885 element == EL_INVISIBLE_WALL ||
2886 element == EL_INVISIBLE_SAND)
2888 if (game.light_time_left > 0)
2889 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
2891 DrawLevelField(x, y);
2893 else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
2894 element == EL_INVISIBLE_WALL_ACTIVE ||
2895 element == EL_INVISIBLE_SAND_ACTIVE)
2897 if (game.light_time_left == 0)
2898 Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
2900 DrawLevelField(x, y);
2906 static void ToggleLightSwitch(int x, int y)
2908 int element = Feld[x][y];
2910 game.light_time_left =
2911 (element == EL_LIGHT_SWITCH ?
2912 level.time_light * FRAMES_PER_SECOND : 0);
2914 RedrawAllLightSwitchesAndInvisibleElements();
2917 static void ActivateTimegateSwitch(int x, int y)
2921 game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
2923 for (yy=0; yy<lev_fieldy; yy++)
2925 for (xx=0; xx<lev_fieldx; xx++)
2927 int element = Feld[xx][yy];
2929 if (element == EL_TIMEGATE_CLOSED ||
2930 element == EL_TIMEGATE_CLOSING)
2932 Feld[xx][yy] = EL_TIMEGATE_OPENING;
2933 PlayLevelSound(xx, yy, SND_TIMEGATE_OPENING);
2937 else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
2939 Feld[xx][yy] = EL_TIMEGATE_SWITCH;
2940 DrawLevelField(xx, yy);
2947 Feld[x][y] = EL_TIMEGATE_SWITCH_ACTIVE;
2950 inline static int getElementMoveStepsize(int x, int y)
2952 int element = Feld[x][y];
2953 int direction = MovDir[x][y];
2954 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
2955 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
2956 int horiz_move = (dx != 0);
2957 int sign = (horiz_move ? dx : dy);
2958 int step = sign * element_info[element].move_stepsize;
2960 /* special values for move stepsize for spring and things on conveyor belt */
2963 if (CAN_FALL(element) &&
2964 y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
2965 step = sign * MOVE_STEPSIZE_NORMAL / 2;
2966 else if (element == EL_SPRING)
2967 step = sign * MOVE_STEPSIZE_NORMAL * 2;
2973 void Impact(int x, int y)
2975 boolean lastline = (y == lev_fieldy-1);
2976 boolean object_hit = FALSE;
2977 boolean impact = (lastline || object_hit);
2978 int element = Feld[x][y];
2979 int smashed = EL_UNDEFINED;
2981 if (!lastline) /* check if element below was hit */
2983 if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
2986 object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
2987 MovDir[x][y + 1] != MV_DOWN ||
2988 MovPos[x][y + 1] <= TILEY / 2));
2990 /* do not smash moving elements that left the smashed field in time */
2991 if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
2992 ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
2996 smashed = MovingOrBlocked2Element(x, y + 1);
2998 impact = (lastline || object_hit);
3001 if (!lastline && smashed == EL_ACID) /* element falls into acid */
3007 /* only reset graphic animation if graphic really changes after impact */
3009 el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
3011 ResetGfxAnimation(x, y);
3012 DrawLevelField(x, y);
3015 if (impact && CAN_EXPLODE_IMPACT(element))
3020 else if (impact && element == EL_PEARL)
3022 Feld[x][y] = EL_PEARL_BREAKING;
3023 PlayLevelSound(x, y, SND_PEARL_BREAKING);
3026 else if (impact && CheckElementChange(x, y, element, CE_IMPACT))
3028 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
3033 if (impact && element == EL_AMOEBA_DROP)
3035 if (object_hit && IS_PLAYER(x, y + 1))
3036 KillHeroUnlessProtected(x, y + 1);
3037 else if (object_hit && smashed == EL_PENGUIN)
3041 Feld[x][y] = EL_AMOEBA_GROWING;
3042 Store[x][y] = EL_AMOEBA_WET;
3044 ResetRandomAnimationValue(x, y);
3049 if (object_hit) /* check which object was hit */
3051 if (CAN_PASS_MAGIC_WALL(element) &&
3052 (smashed == EL_MAGIC_WALL ||
3053 smashed == EL_BD_MAGIC_WALL))
3056 int activated_magic_wall =
3057 (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
3058 EL_BD_MAGIC_WALL_ACTIVE);
3060 /* activate magic wall / mill */
3061 for (yy=0; yy<lev_fieldy; yy++)
3062 for (xx=0; xx<lev_fieldx; xx++)
3063 if (Feld[xx][yy] == smashed)
3064 Feld[xx][yy] = activated_magic_wall;
3066 game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
3067 game.magic_wall_active = TRUE;
3069 PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
3070 SND_MAGIC_WALL_ACTIVATING :
3071 SND_BD_MAGIC_WALL_ACTIVATING));
3074 if (IS_PLAYER(x, y + 1))
3076 if (CAN_SMASH_PLAYER(element))
3078 KillHeroUnlessProtected(x, y + 1);
3082 else if (smashed == EL_PENGUIN)
3084 if (CAN_SMASH_PLAYER(element))
3090 else if (element == EL_BD_DIAMOND)
3092 if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
3098 else if ((element == EL_SP_INFOTRON ||
3099 element == EL_SP_ZONK) &&
3100 (smashed == EL_SP_SNIKSNAK ||
3101 smashed == EL_SP_ELECTRON ||
3102 smashed == EL_SP_DISK_ORANGE))
3108 else if (CAN_SMASH_ENEMIES(element) && IS_CLASSIC_ENEMY(smashed))
3114 else if (CAN_SMASH_EVERYTHING(element))
3116 if (IS_CLASSIC_ENEMY(smashed) ||
3117 CAN_EXPLODE_SMASHED(smashed))
3122 else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
3124 if (smashed == EL_LAMP ||
3125 smashed == EL_LAMP_ACTIVE)
3130 else if (smashed == EL_NUT)
3132 Feld[x][y + 1] = EL_NUT_BREAKING;
3133 PlayLevelSound(x, y, SND_NUT_BREAKING);
3134 RaiseScoreElement(EL_NUT);
3137 else if (smashed == EL_PEARL)
3139 Feld[x][y + 1] = EL_PEARL_BREAKING;
3140 PlayLevelSound(x, y, SND_PEARL_BREAKING);
3143 else if (smashed == EL_DIAMOND)
3145 Feld[x][y + 1] = EL_DIAMOND_BREAKING;
3146 PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
3149 else if (IS_BELT_SWITCH(smashed))
3151 ToggleBeltSwitch(x, y + 1);
3153 else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
3154 smashed == EL_SWITCHGATE_SWITCH_DOWN)
3156 ToggleSwitchgateSwitch(x, y + 1);
3158 else if (smashed == EL_LIGHT_SWITCH ||
3159 smashed == EL_LIGHT_SWITCH_ACTIVE)
3161 ToggleLightSwitch(x, y + 1);
3165 CheckElementChange(x, y + 1, smashed, CE_SMASHED);
3167 CheckTriggeredElementSideChange(x, y + 1, smashed, CH_SIDE_TOP,
3168 CE_OTHER_IS_SWITCHING);
3169 CheckElementSideChange(x, y + 1, smashed, CH_SIDE_TOP,
3175 CheckElementChange(x, y + 1, smashed, CE_SMASHED);
3180 /* play sound of magic wall / mill */
3182 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
3183 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE))
3185 if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
3186 PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
3187 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
3188 PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
3193 /* play sound of object that hits the ground */
3194 if (lastline || object_hit)
3195 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
3198 inline static void TurnRoundExt(int x, int y)
3210 { 0, 0 }, { 0, 0 }, { 0, 0 },
3215 int left, right, back;
3219 { MV_DOWN, MV_UP, MV_RIGHT },
3220 { MV_UP, MV_DOWN, MV_LEFT },
3222 { MV_LEFT, MV_RIGHT, MV_DOWN },
3226 { MV_RIGHT, MV_LEFT, MV_UP }
3229 int element = Feld[x][y];
3230 int old_move_dir = MovDir[x][y];
3231 int left_dir = turn[old_move_dir].left;
3232 int right_dir = turn[old_move_dir].right;
3233 int back_dir = turn[old_move_dir].back;
3235 int left_dx = move_xy[left_dir].x, left_dy = move_xy[left_dir].y;
3236 int right_dx = move_xy[right_dir].x, right_dy = move_xy[right_dir].y;
3237 int move_dx = move_xy[old_move_dir].x, move_dy = move_xy[old_move_dir].y;
3238 int back_dx = move_xy[back_dir].x, back_dy = move_xy[back_dir].y;
3240 int left_x = x + left_dx, left_y = y + left_dy;
3241 int right_x = x + right_dx, right_y = y + right_dy;
3242 int move_x = x + move_dx, move_y = y + move_dy;
3246 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
3248 TestIfBadThingTouchesOtherBadThing(x, y);
3250 if (ENEMY_CAN_ENTER_FIELD(right_x, right_y))
3251 MovDir[x][y] = right_dir;
3252 else if (!ENEMY_CAN_ENTER_FIELD(move_x, move_y))
3253 MovDir[x][y] = left_dir;
3255 if (element == EL_BUG && MovDir[x][y] != old_move_dir)
3257 else if (element == EL_BD_BUTTERFLY) /* && MovDir[x][y] == left_dir) */
3260 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
3261 element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
3263 TestIfBadThingTouchesOtherBadThing(x, y);
3265 if (ENEMY_CAN_ENTER_FIELD(left_x, left_y))
3266 MovDir[x][y] = left_dir;
3267 else if (!ENEMY_CAN_ENTER_FIELD(move_x, move_y))
3268 MovDir[x][y] = right_dir;
3270 if ((element == EL_SPACESHIP ||
3271 element == EL_SP_SNIKSNAK ||
3272 element == EL_SP_ELECTRON)
3273 && MovDir[x][y] != old_move_dir)
3275 else if (element == EL_BD_FIREFLY) /* && MovDir[x][y] == right_dir) */
3278 else if (element == EL_YAMYAM)
3280 boolean can_turn_left = YAMYAM_CAN_ENTER_FIELD(left_x, left_y);
3281 boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(right_x, right_y);
3283 if (can_turn_left && can_turn_right)
3284 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
3285 else if (can_turn_left)
3286 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
3287 else if (can_turn_right)
3288 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
3290 MovDir[x][y] = back_dir;
3292 MovDelay[x][y] = 16 + 16 * RND(3);
3294 else if (element == EL_DARK_YAMYAM)
3296 boolean can_turn_left = DARK_YAMYAM_CAN_ENTER_FIELD(left_x, left_y);
3297 boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(right_x, right_y);
3299 if (can_turn_left && can_turn_right)
3300 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
3301 else if (can_turn_left)
3302 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
3303 else if (can_turn_right)
3304 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
3306 MovDir[x][y] = back_dir;
3308 MovDelay[x][y] = 16 + 16 * RND(3);
3310 else if (element == EL_PACMAN)
3312 boolean can_turn_left = PACMAN_CAN_ENTER_FIELD(left_x, left_y);
3313 boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(right_x, right_y);
3315 if (can_turn_left && can_turn_right)
3316 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
3317 else if (can_turn_left)
3318 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
3319 else if (can_turn_right)
3320 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
3322 MovDir[x][y] = back_dir;
3324 MovDelay[x][y] = 6 + RND(40);
3326 else if (element == EL_PIG)
3328 boolean can_turn_left = PIG_CAN_ENTER_FIELD(left_x, left_y);
3329 boolean can_turn_right = PIG_CAN_ENTER_FIELD(right_x, right_y);
3330 boolean can_move_on = PIG_CAN_ENTER_FIELD(move_x, move_y);
3331 boolean should_turn_left, should_turn_right, should_move_on;
3333 int rnd = RND(rnd_value);
3335 should_turn_left = (can_turn_left &&
3337 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
3338 y + back_dy + left_dy)));
3339 should_turn_right = (can_turn_right &&
3341 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
3342 y + back_dy + right_dy)));
3343 should_move_on = (can_move_on &&
3346 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
3347 y + move_dy + left_dy) ||
3348 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
3349 y + move_dy + right_dy)));
3351 if (should_turn_left || should_turn_right || should_move_on)
3353 if (should_turn_left && should_turn_right && should_move_on)
3354 MovDir[x][y] = (rnd < rnd_value / 3 ? left_dir :
3355 rnd < 2 * rnd_value / 3 ? right_dir :
3357 else if (should_turn_left && should_turn_right)
3358 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
3359 else if (should_turn_left && should_move_on)
3360 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
3361 else if (should_turn_right && should_move_on)
3362 MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
3363 else if (should_turn_left)
3364 MovDir[x][y] = left_dir;
3365 else if (should_turn_right)
3366 MovDir[x][y] = right_dir;
3367 else if (should_move_on)
3368 MovDir[x][y] = old_move_dir;
3370 else if (can_move_on && rnd > rnd_value / 8)
3371 MovDir[x][y] = old_move_dir;
3372 else if (can_turn_left && can_turn_right)
3373 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
3374 else if (can_turn_left && rnd > rnd_value / 8)
3375 MovDir[x][y] = left_dir;
3376 else if (can_turn_right && rnd > rnd_value/8)
3377 MovDir[x][y] = right_dir;
3379 MovDir[x][y] = back_dir;
3381 xx = x + move_xy[MovDir[x][y]].x;
3382 yy = y + move_xy[MovDir[x][y]].y;
3384 if (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy]))
3385 MovDir[x][y] = old_move_dir;
3389 else if (element == EL_DRAGON)
3391 boolean can_turn_left = IN_LEV_FIELD_AND_IS_FREE(left_x, left_y);
3392 boolean can_turn_right = IN_LEV_FIELD_AND_IS_FREE(right_x, right_y);
3393 boolean can_move_on = IN_LEV_FIELD_AND_IS_FREE(move_x, move_y);
3395 int rnd = RND(rnd_value);
3398 if (FrameCounter < 1 && x == 0 && y == 29)
3399 printf(":2: %d/%d: %d [%d]\n", x, y, MovDir[x][y], FrameCounter);
3402 if (can_move_on && rnd > rnd_value / 8)
3403 MovDir[x][y] = old_move_dir;
3404 else if (can_turn_left && can_turn_right)
3405 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
3406 else if (can_turn_left && rnd > rnd_value / 8)
3407 MovDir[x][y] = left_dir;
3408 else if (can_turn_right && rnd > rnd_value / 8)
3409 MovDir[x][y] = right_dir;
3411 MovDir[x][y] = back_dir;
3413 xx = x + move_xy[MovDir[x][y]].x;
3414 yy = y + move_xy[MovDir[x][y]].y;
3417 if (FrameCounter < 1 && x == 0 && y == 29)
3418 printf(":3: %d/%d: %d (%d/%d: %d) [%d]\n", x, y, MovDir[x][y],
3419 xx, yy, Feld[xx][yy],
3424 if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
3425 MovDir[x][y] = old_move_dir;
3427 if (!IS_FREE(xx, yy))
3428 MovDir[x][y] = old_move_dir;
3432 if (FrameCounter < 1 && x == 0 && y == 29)
3433 printf(":4: %d/%d: %d [%d]\n", x, y, MovDir[x][y], FrameCounter);
3438 else if (element == EL_MOLE)
3440 boolean can_move_on =
3441 (MOLE_CAN_ENTER_FIELD(move_x, move_y,
3442 IS_AMOEBOID(Feld[move_x][move_y]) ||
3443 Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
3446 boolean can_turn_left =
3447 (MOLE_CAN_ENTER_FIELD(left_x, left_y,
3448 IS_AMOEBOID(Feld[left_x][left_y])));
3450 boolean can_turn_right =
3451 (MOLE_CAN_ENTER_FIELD(right_x, right_y,
3452 IS_AMOEBOID(Feld[right_x][right_y])));
3454 if (can_turn_left && can_turn_right)
3455 MovDir[x][y] = (RND(2) ? left_dir : right_dir);
3456 else if (can_turn_left)
3457 MovDir[x][y] = left_dir;
3459 MovDir[x][y] = right_dir;
3462 if (MovDir[x][y] != old_move_dir)
3465 else if (element == EL_BALLOON)
3467 MovDir[x][y] = game.balloon_dir;
3470 else if (element == EL_SPRING)
3472 if (MovDir[x][y] & MV_HORIZONTAL &&
3473 (!IN_LEV_FIELD_AND_IS_FREE(move_x, move_y) ||
3474 IN_LEV_FIELD_AND_IS_FREE(x, y + 1)))
3475 MovDir[x][y] = MV_NO_MOVING;
3479 else if (element == EL_ROBOT ||
3480 element == EL_SATELLITE ||
3481 element == EL_PENGUIN)
3483 int attr_x = -1, attr_y = -1;
3494 for (i=0; i<MAX_PLAYERS; i++)
3496 struct PlayerInfo *player = &stored_player[i];
3497 int jx = player->jx, jy = player->jy;
3499 if (!player->active)
3503 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
3511 if (element == EL_ROBOT && ZX >= 0 && ZY >= 0)
3517 if (element == EL_PENGUIN)
3520 static int xy[4][2] =
3530 int ex = x + xy[i % 4][0];
3531 int ey = y + xy[i % 4][1];
3533 if (IN_LEV_FIELD(ex, ey) && Feld[ex][ey] == EL_EXIT_OPEN)
3542 MovDir[x][y] = MV_NO_MOVING;
3544 MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
3545 else if (attr_x > x)
3546 MovDir[x][y] |= (AllPlayersGone ? MV_LEFT : MV_RIGHT);
3548 MovDir[x][y] |= (AllPlayersGone ? MV_DOWN : MV_UP);
3549 else if (attr_y > y)
3550 MovDir[x][y] |= (AllPlayersGone ? MV_UP : MV_DOWN);
3552 if (element == EL_ROBOT)
3556 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
3557 MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
3558 Moving2Blocked(x, y, &newx, &newy);
3560 if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
3561 MovDelay[x][y] = 8 + 8 * !RND(3);
3563 MovDelay[x][y] = 16;
3565 else if (element == EL_PENGUIN)
3571 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
3573 boolean first_horiz = RND(2);
3574 int new_move_dir = MovDir[x][y];
3577 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
3578 Moving2Blocked(x, y, &newx, &newy);
3580 if (PENGUIN_CAN_ENTER_FIELD(newx, newy))
3584 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
3585 Moving2Blocked(x, y, &newx, &newy);
3587 if (PENGUIN_CAN_ENTER_FIELD(newx, newy))
3590 MovDir[x][y] = old_move_dir;
3594 else /* (element == EL_SATELLITE) */
3600 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
3602 boolean first_horiz = RND(2);
3603 int new_move_dir = MovDir[x][y];
3606 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
3607 Moving2Blocked(x, y, &newx, &newy);
3609 if (ELEMENT_CAN_ENTER_FIELD_OR_ACID_2(newx, newy))
3613 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
3614 Moving2Blocked(x, y, &newx, &newy);
3616 if (ELEMENT_CAN_ENTER_FIELD_OR_ACID_2(newx, newy))
3619 MovDir[x][y] = old_move_dir;
3624 else if (element_info[element].move_pattern == MV_ALL_DIRECTIONS ||
3625 element_info[element].move_pattern == MV_TURNING_LEFT ||
3626 element_info[element].move_pattern == MV_TURNING_RIGHT)
3628 boolean can_turn_left = ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
3629 boolean can_turn_right = ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
3631 if (element_info[element].move_pattern == MV_TURNING_LEFT)
3632 MovDir[x][y] = left_dir;
3633 else if (element_info[element].move_pattern == MV_TURNING_RIGHT)
3634 MovDir[x][y] = right_dir;
3635 else if (can_turn_left && can_turn_right)
3636 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
3637 else if (can_turn_left)
3638 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
3639 else if (can_turn_right)
3640 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
3642 MovDir[x][y] = back_dir;
3644 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
3646 else if (element_info[element].move_pattern == MV_HORIZONTAL ||
3647 element_info[element].move_pattern == MV_VERTICAL)
3649 if (element_info[element].move_pattern & old_move_dir)
3650 MovDir[x][y] = back_dir;
3651 else if (element_info[element].move_pattern == MV_HORIZONTAL)
3652 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
3653 else if (element_info[element].move_pattern == MV_VERTICAL)
3654 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
3656 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
3658 else if (element_info[element].move_pattern & MV_ANY_DIRECTION)
3660 MovDir[x][y] = element_info[element].move_pattern;
3661 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
3663 else if (element_info[element].move_pattern == MV_ALONG_LEFT_SIDE)
3665 if (ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
3666 MovDir[x][y] = left_dir;
3667 else if (!ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
3668 MovDir[x][y] = right_dir;
3670 if (MovDir[x][y] != old_move_dir)
3671 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
3673 else if (element_info[element].move_pattern == MV_ALONG_RIGHT_SIDE)
3675 if (ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
3676 MovDir[x][y] = right_dir;
3677 else if (!ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
3678 MovDir[x][y] = left_dir;
3680 if (MovDir[x][y] != old_move_dir)
3681 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
3683 else if (element_info[element].move_pattern == MV_TOWARDS_PLAYER ||
3684 element_info[element].move_pattern == MV_AWAY_FROM_PLAYER)
3686 int attr_x = -1, attr_y = -1;
3689 (element_info[element].move_pattern == MV_AWAY_FROM_PLAYER);
3700 for (i=0; i<MAX_PLAYERS; i++)
3702 struct PlayerInfo *player = &stored_player[i];
3703 int jx = player->jx, jy = player->jy;
3705 if (!player->active)
3709 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
3717 MovDir[x][y] = MV_NO_MOVING;
3719 MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
3720 else if (attr_x > x)
3721 MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
3723 MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
3724 else if (attr_y > y)
3725 MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
3727 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
3729 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
3731 boolean first_horiz = RND(2);
3732 int new_move_dir = MovDir[x][y];
3735 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
3736 Moving2Blocked(x, y, &newx, &newy);
3738 if (ELEMENT_CAN_ENTER_FIELD_OR_ACID(element, newx, newy))
3742 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
3743 Moving2Blocked(x, y, &newx, &newy);
3745 if (ELEMENT_CAN_ENTER_FIELD_OR_ACID(element, newx, newy))
3748 MovDir[x][y] = old_move_dir;
3751 else if (element_info[element].move_pattern == MV_WHEN_PUSHED)
3753 if (!IN_LEV_FIELD_AND_IS_FREE(move_x, move_y))
3754 MovDir[x][y] = MV_NO_MOVING;
3760 static void TurnRound(int x, int y)
3762 int direction = MovDir[x][y];
3765 GfxDir[x][y] = MovDir[x][y];
3771 GfxDir[x][y] = MovDir[x][y];
3774 if (direction != MovDir[x][y])
3779 GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_BIT(direction);
3782 GfxAction[x][y] = ACTION_WAITING;
3786 static boolean JustBeingPushed(int x, int y)
3790 for (i=0; i<MAX_PLAYERS; i++)
3792 struct PlayerInfo *player = &stored_player[i];
3794 if (player->active && player->is_pushing && player->MovPos)
3796 int next_jx = player->jx + (player->jx - player->last_jx);
3797 int next_jy = player->jy + (player->jy - player->last_jy);
3799 if (x == next_jx && y == next_jy)
3807 void StartMoving(int x, int y)
3809 boolean use_spring_bug = (game.engine_version < VERSION_IDENT(2,2,0,0));
3810 boolean started_moving = FALSE; /* some elements can fall _and_ move */
3811 int element = Feld[x][y];
3817 if (MovDelay[x][y] == 0)
3818 GfxAction[x][y] = ACTION_DEFAULT;
3820 /* !!! this should be handled more generic (not only for mole) !!! */
3821 if (element != EL_MOLE && GfxAction[x][y] != ACTION_DIGGING)
3822 GfxAction[x][y] = ACTION_DEFAULT;
3825 if (CAN_FALL(element) && y < lev_fieldy - 1)
3827 if ((x > 0 && IS_PLAYER(x - 1, y)) ||
3828 (x < lev_fieldx-1 && IS_PLAYER(x + 1, y)))
3829 if (JustBeingPushed(x, y))
3832 if (element == EL_QUICKSAND_FULL)
3834 if (IS_FREE(x, y + 1))
3836 InitMovingField(x, y, MV_DOWN);
3837 started_moving = TRUE;
3839 Feld[x][y] = EL_QUICKSAND_EMPTYING;
3840 Store[x][y] = EL_ROCK;
3842 PlayLevelSoundAction(x, y, ACTION_EMPTYING);
3844 PlayLevelSound(x, y, SND_QUICKSAND_EMPTYING);
3847 else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
3849 if (!MovDelay[x][y])
3850 MovDelay[x][y] = TILEY + 1;
3859 Feld[x][y] = EL_QUICKSAND_EMPTY;
3860 Feld[x][y + 1] = EL_QUICKSAND_FULL;
3861 Store[x][y + 1] = Store[x][y];
3864 PlayLevelSoundAction(x, y, ACTION_FILLING);
3866 PlayLevelSound(x, y, SND_QUICKSAND_FILLING);
3870 else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
3871 Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
3873 InitMovingField(x, y, MV_DOWN);
3874 started_moving = TRUE;
3876 Feld[x][y] = EL_QUICKSAND_FILLING;
3877 Store[x][y] = element;
3879 PlayLevelSoundAction(x, y, ACTION_FILLING);
3881 PlayLevelSound(x, y, SND_QUICKSAND_FILLING);
3884 else if (element == EL_MAGIC_WALL_FULL)
3886 if (IS_FREE(x, y + 1))
3888 InitMovingField(x, y, MV_DOWN);
3889 started_moving = TRUE;
3891 Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
3892 Store[x][y] = EL_CHANGED(Store[x][y]);
3894 else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
3896 if (!MovDelay[x][y])
3897 MovDelay[x][y] = TILEY/4 + 1;
3906 Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
3907 Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
3908 Store[x][y + 1] = EL_CHANGED(Store[x][y]);
3912 else if (element == EL_BD_MAGIC_WALL_FULL)
3914 if (IS_FREE(x, y + 1))
3916 InitMovingField(x, y, MV_DOWN);
3917 started_moving = TRUE;
3919 Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
3920 Store[x][y] = EL_CHANGED2(Store[x][y]);
3922 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
3924 if (!MovDelay[x][y])
3925 MovDelay[x][y] = TILEY/4 + 1;
3934 Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
3935 Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
3936 Store[x][y + 1] = EL_CHANGED2(Store[x][y]);
3940 else if (CAN_PASS_MAGIC_WALL(element) &&
3941 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
3942 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE))
3944 InitMovingField(x, y, MV_DOWN);
3945 started_moving = TRUE;
3948 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
3949 EL_BD_MAGIC_WALL_FILLING);
3950 Store[x][y] = element;
3953 else if (CAN_SMASH(element) && Feld[x][y + 1] == EL_ACID)
3955 else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
3960 InitMovingField(x, y, MV_DOWN);
3961 started_moving = TRUE;
3963 Store[x][y] = EL_ACID;
3965 /* !!! TEST !!! better use "_FALLING" etc. !!! */
3966 GfxAction[x][y + 1] = ACTION_ACTIVE;
3970 else if ((game.engine_version < VERSION_IDENT(2,2,0,7) &&
3971 CAN_SMASH(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
3972 (Feld[x][y + 1] == EL_BLOCKED)) ||
3973 (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
3974 CAN_SMASH(element) && WasJustFalling[x][y] &&
3975 (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))))
3979 else if (game.engine_version < VERSION_IDENT(2,2,0,7) &&
3980 CAN_SMASH(element) && Feld[x][y + 1] == EL_BLOCKED &&
3981 WasJustMoving[x][y] && !Pushed[x][y + 1])
3983 else if (CAN_SMASH(element) && Feld[x][y + 1] == EL_BLOCKED &&
3984 WasJustMoving[x][y])
3989 /* this is needed for a special case not covered by calling "Impact()"
3990 from "ContinueMoving()": if an element moves to a tile directly below
3991 another element which was just falling on that tile (which was empty
3992 in the previous frame), the falling element above would just stop
3993 instead of smashing the element below (in previous version, the above
3994 element was just checked for "moving" instead of "falling", resulting
3995 in incorrect smashes caused by horizontal movement of the above
3996 element; also, the case of the player being the element to smash was
3997 simply not covered here... :-/ ) */
4001 else if (IS_FREE(x, y + 1) && element == EL_SPRING && use_spring_bug)
4003 if (MovDir[x][y] == MV_NO_MOVING)
4005 InitMovingField(x, y, MV_DOWN);
4006 started_moving = TRUE;
4009 else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
4011 if (WasJustFalling[x][y]) /* prevent animation from being restarted */
4012 MovDir[x][y] = MV_DOWN;
4014 InitMovingField(x, y, MV_DOWN);
4015 started_moving = TRUE;
4017 else if (element == EL_AMOEBA_DROP)
4019 Feld[x][y] = EL_AMOEBA_GROWING;
4020 Store[x][y] = EL_AMOEBA_WET;
4022 /* Store[x][y + 1] must be zero, because:
4023 (EL_QUICKSAND_FULL -> EL_ROCK): Store[x][y + 1] == EL_QUICKSAND_EMPTY
4026 #if OLD_GAME_BEHAVIOUR
4027 else if (IS_SLIPPERY(Feld[x][y + 1]) && !Store[x][y + 1])
4029 else if (IS_SLIPPERY(Feld[x][y + 1]) && !Store[x][y + 1] &&
4030 !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
4031 element != EL_DX_SUPABOMB)
4034 else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
4035 (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
4036 !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
4037 element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
4040 boolean can_fall_left = (x > 0 && IS_FREE(x - 1, y) &&
4041 (IS_FREE(x - 1, y + 1) ||
4042 Feld[x - 1][y + 1] == EL_ACID));
4043 boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
4044 (IS_FREE(x + 1, y + 1) ||
4045 Feld[x + 1][y + 1] == EL_ACID));
4046 boolean can_fall_any = (can_fall_left || can_fall_right);
4047 boolean can_fall_both = (can_fall_left && can_fall_right);
4049 if (can_fall_any && IS_CUSTOM_ELEMENT(Feld[x][y + 1]))
4051 int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
4053 if (slippery_type == SLIPPERY_ONLY_LEFT)
4054 can_fall_right = FALSE;
4055 else if (slippery_type == SLIPPERY_ONLY_RIGHT)
4056 can_fall_left = FALSE;
4057 else if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
4058 can_fall_right = FALSE;
4059 else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
4060 can_fall_left = FALSE;
4062 can_fall_any = (can_fall_left || can_fall_right);
4063 can_fall_both = (can_fall_left && can_fall_right);
4068 if (can_fall_both &&
4069 (game.emulation != EMU_BOULDERDASH &&
4070 element != EL_BD_ROCK && element != EL_BD_DIAMOND))
4071 can_fall_left = !(can_fall_right = RND(2));
4073 InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
4074 started_moving = TRUE;
4077 else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
4079 boolean left_is_free = (x > 0 && IS_FREE(x - 1, y));
4080 boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
4081 int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
4082 int belt_dir = game.belt_dir[belt_nr];
4084 if ((belt_dir == MV_LEFT && left_is_free) ||
4085 (belt_dir == MV_RIGHT && right_is_free))
4087 InitMovingField(x, y, belt_dir);
4088 started_moving = TRUE;
4090 GfxAction[x][y] = ACTION_DEFAULT;
4095 /* not "else if" because of elements that can fall and move (EL_SPRING) */
4096 if (CAN_MOVE(element) && !started_moving)
4101 if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
4104 if ((element == EL_SATELLITE ||
4105 element == EL_BALLOON ||
4106 element == EL_SPRING)
4107 && JustBeingPushed(x, y))
4113 if (element == EL_SPRING && MovDir[x][y] == MV_DOWN)
4114 Feld[x][y + 1] = EL_EMPTY; /* was set to EL_BLOCKED above */
4116 if (element == EL_SPRING && MovDir[x][y] != MV_NO_MOVING)
4118 Moving2Blocked(x, y, &newx, &newy);
4119 if (Feld[newx][newy] == EL_BLOCKED)
4120 Feld[newx][newy] = EL_EMPTY; /* was set to EL_BLOCKED above */
4126 if (FrameCounter < 1 && x == 0 && y == 29)
4127 printf(":1: %d/%d: %d [%d]\n", x, y, MovDir[x][y], FrameCounter);
4130 if (!MovDelay[x][y]) /* start new movement phase */
4132 /* all objects that can change their move direction after each step
4133 (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall */
4135 if (element != EL_YAMYAM &&
4136 element != EL_DARK_YAMYAM &&
4137 element != EL_PACMAN &&
4138 !(element_info[element].move_pattern & MV_ANY_DIRECTION) &&
4139 element_info[element].move_pattern != MV_TURNING_LEFT &&
4140 element_info[element].move_pattern != MV_TURNING_RIGHT)
4145 if (FrameCounter < 1 && x == 0 && y == 29)
4146 printf(":9: %d: %d [%d]\n", y, MovDir[x][y], FrameCounter);
4149 if (MovDelay[x][y] && (element == EL_BUG ||
4150 element == EL_SPACESHIP ||
4151 element == EL_SP_SNIKSNAK ||
4152 element == EL_SP_ELECTRON ||
4153 element == EL_MOLE))
4154 DrawLevelField(x, y);
4158 if (MovDelay[x][y]) /* wait some time before next movement */
4163 if (element == EL_YAMYAM)
4166 el_act_dir2img(EL_YAMYAM, ACTION_WAITING, MV_LEFT));
4167 DrawLevelElementAnimation(x, y, element);
4171 if (MovDelay[x][y]) /* element still has to wait some time */
4174 /* !!! PLACE THIS SOMEWHERE AFTER "TurnRound()" !!! */
4175 ResetGfxAnimation(x, y);
4179 if (GfxAction[x][y] != ACTION_WAITING)
4180 printf("::: %d: %d != ACTION_WAITING\n", element, GfxAction[x][y]);
4182 GfxAction[x][y] = ACTION_WAITING;
4186 if (element == EL_ROBOT ||
4188 element == EL_PACMAN ||
4190 element == EL_YAMYAM ||
4191 element == EL_DARK_YAMYAM)
4194 DrawLevelElementAnimation(x, y, element);
4196 DrawLevelElementAnimationIfNeeded(x, y, element);
4198 PlayLevelSoundAction(x, y, ACTION_WAITING);
4200 else if (element == EL_SP_ELECTRON)
4201 DrawLevelElementAnimationIfNeeded(x, y, element);
4202 else if (element == EL_DRAGON)
4205 int dir = MovDir[x][y];
4206 int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
4207 int dy = (dir == MV_UP ? -1 : dir == MV_DOWN ? +1 : 0);
4208 int graphic = (dir == MV_LEFT ? IMG_FLAMES_1_LEFT :
4209 dir == MV_RIGHT ? IMG_FLAMES_1_RIGHT :
4210 dir == MV_UP ? IMG_FLAMES_1_UP :
4211 dir == MV_DOWN ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
4212 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
4215 printf("::: %d, %d\n", GfxAction[x][y], GfxFrame[x][y]);
4218 GfxAction[x][y] = ACTION_ATTACKING;
4220 if (IS_PLAYER(x, y))
4221 DrawPlayerField(x, y);
4223 DrawLevelField(x, y);
4225 PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
4227 for (i=1; i <= 3; i++)
4229 int xx = x + i * dx;
4230 int yy = y + i * dy;
4231 int sx = SCREENX(xx);
4232 int sy = SCREENY(yy);
4233 int flame_graphic = graphic + (i - 1);
4235 if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
4240 int flamed = MovingOrBlocked2Element(xx, yy);
4242 if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_FIRE(flamed))
4245 RemoveMovingField(xx, yy);
4247 Feld[xx][yy] = EL_FLAMES;
4248 if (IN_SCR_FIELD(sx, sy))
4250 DrawLevelFieldCrumbledSand(xx, yy);
4251 DrawGraphic(sx, sy, flame_graphic, frame);
4256 if (Feld[xx][yy] == EL_FLAMES)
4257 Feld[xx][yy] = EL_EMPTY;
4258 DrawLevelField(xx, yy);
4263 if (MovDelay[x][y]) /* element still has to wait some time */
4265 PlayLevelSoundAction(x, y, ACTION_WAITING);
4271 /* special case of "moving" animation of waiting elements (FIX THIS !!!);
4272 for all other elements GfxAction will be set by InitMovingField() */
4273 if (element == EL_BD_BUTTERFLY || element == EL_BD_FIREFLY)
4274 GfxAction[x][y] = ACTION_MOVING;
4278 /* now make next step */
4280 Moving2Blocked(x, y, &newx, &newy); /* get next screen position */
4282 if (DONT_COLLIDE_WITH(element) &&
4283 IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
4284 !PLAYER_PROTECTED(newx, newy))
4287 TestIfBadThingRunsIntoHero(x, y, MovDir[x][y]);
4290 /* player killed by element which is deadly when colliding with */
4292 KillHero(PLAYERINFO(newx, newy));
4297 else if ((element == EL_PENGUIN ||
4298 element == EL_ROBOT ||
4299 element == EL_SATELLITE ||
4300 element == EL_BALLOON ||
4301 IS_CUSTOM_ELEMENT(element)) &&
4302 IN_LEV_FIELD(newx, newy) &&
4303 MovDir[x][y] == MV_DOWN && Feld[newx][newy] == EL_ACID)
4306 Store[x][y] = EL_ACID;
4308 else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
4310 if (Feld[newx][newy] == EL_EXIT_OPEN)
4312 Feld[x][y] = EL_EMPTY;
4313 DrawLevelField(x, y);
4315 PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
4316 if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
4317 DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
4319 local_player->friends_still_needed--;
4320 if (!local_player->friends_still_needed &&
4321 !local_player->GameOver && AllPlayersGone)
4322 local_player->LevelSolved = local_player->GameOver = TRUE;
4326 else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
4328 if (DigField(local_player, newx, newy, 0, 0, DF_DIG) == MF_MOVING)
4329 DrawLevelField(newx, newy);
4331 GfxDir[x][y] = MovDir[x][y] = MV_NO_MOVING;
4333 else if (!IS_FREE(newx, newy))
4335 GfxAction[x][y] = ACTION_WAITING;
4337 if (IS_PLAYER(x, y))
4338 DrawPlayerField(x, y);
4340 DrawLevelField(x, y);
4344 else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
4346 if (IS_FOOD_PIG(Feld[newx][newy]))
4348 if (IS_MOVING(newx, newy))
4349 RemoveMovingField(newx, newy);
4352 Feld[newx][newy] = EL_EMPTY;
4353 DrawLevelField(newx, newy);
4356 PlayLevelSound(x, y, SND_PIG_DIGGING);
4358 else if (!IS_FREE(newx, newy))
4360 if (IS_PLAYER(x, y))
4361 DrawPlayerField(x, y);
4363 DrawLevelField(x, y);
4367 else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
4369 if (!IS_FREE(newx, newy))
4371 if (IS_PLAYER(x, y))
4372 DrawPlayerField(x, y);
4374 DrawLevelField(x, y);
4380 boolean wanna_flame = !RND(10);
4381 int dx = newx - x, dy = newy - y;
4382 int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
4383 int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
4384 int element1 = (IN_LEV_FIELD(newx1, newy1) ?
4385 MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
4386 int element2 = (IN_LEV_FIELD(newx2, newy2) ?
4387 MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
4390 IS_CLASSIC_ENEMY(element1) ||
4391 IS_CLASSIC_ENEMY(element2)) &&
4392 element1 != EL_DRAGON && element2 != EL_DRAGON &&
4393 element1 != EL_FLAMES && element2 != EL_FLAMES)
4396 ResetGfxAnimation(x, y);
4397 GfxAction[x][y] = ACTION_ATTACKING;
4400 if (IS_PLAYER(x, y))
4401 DrawPlayerField(x, y);
4403 DrawLevelField(x, y);
4405 PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
4407 MovDelay[x][y] = 50;
4409 Feld[newx][newy] = EL_FLAMES;
4410 if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
4411 Feld[newx1][newy1] = EL_FLAMES;
4412 if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
4413 Feld[newx2][newy2] = EL_FLAMES;
4419 else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
4420 Feld[newx][newy] == EL_DIAMOND)
4422 if (IS_MOVING(newx, newy))
4423 RemoveMovingField(newx, newy);
4426 Feld[newx][newy] = EL_EMPTY;
4427 DrawLevelField(newx, newy);
4430 PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
4432 else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
4433 IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
4435 if (AmoebaNr[newx][newy])
4437 AmoebaCnt2[AmoebaNr[newx][newy]]--;
4438 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
4439 Feld[newx][newy] == EL_BD_AMOEBA)
4440 AmoebaCnt[AmoebaNr[newx][newy]]--;
4443 if (IS_MOVING(newx, newy))
4444 RemoveMovingField(newx, newy);
4447 Feld[newx][newy] = EL_EMPTY;
4448 DrawLevelField(newx, newy);
4451 PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
4453 else if ((element == EL_PACMAN || element == EL_MOLE)
4454 && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
4456 if (AmoebaNr[newx][newy])
4458 AmoebaCnt2[AmoebaNr[newx][newy]]--;
4459 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
4460 Feld[newx][newy] == EL_BD_AMOEBA)
4461 AmoebaCnt[AmoebaNr[newx][newy]]--;
4464 if (element == EL_MOLE)
4466 Feld[newx][newy] = EL_AMOEBA_SHRINKING;
4467 PlayLevelSound(x, y, SND_MOLE_DIGGING);
4469 ResetGfxAnimation(x, y);
4470 GfxAction[x][y] = ACTION_DIGGING;
4471 DrawLevelField(x, y);
4473 MovDelay[newx][newy] = 0; /* start amoeba shrinking delay */
4474 return; /* wait for shrinking amoeba */
4476 else /* element == EL_PACMAN */
4478 Feld[newx][newy] = EL_EMPTY;
4479 DrawLevelField(newx, newy);
4480 PlayLevelSound(x, y, SND_PACMAN_DIGGING);
4483 else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
4484 (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
4485 (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
4487 /* wait for shrinking amoeba to completely disappear */
4490 else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
4492 /* object was running against a wall */
4497 if (GFX_ELEMENT(element) != EL_SAND) /* !!! FIX THIS (crumble) !!! */
4498 DrawLevelElementAnimation(x, y, element);
4500 if (element == EL_BUG ||
4501 element == EL_SPACESHIP ||
4502 element == EL_SP_SNIKSNAK)
4503 DrawLevelField(x, y);
4504 else if (element == EL_MOLE)
4505 DrawLevelField(x, y);
4506 else if (element == EL_BD_BUTTERFLY ||
4507 element == EL_BD_FIREFLY)
4508 DrawLevelElementAnimationIfNeeded(x, y, element);
4509 else if (element == EL_SATELLITE)
4510 DrawLevelElementAnimationIfNeeded(x, y, element);
4511 else if (element == EL_SP_ELECTRON)
4512 DrawLevelElementAnimationIfNeeded(x, y, element);
4515 if (DONT_TOUCH(element))
4516 TestIfBadThingTouchesHero(x, y);
4519 PlayLevelSoundAction(x, y, ACTION_WAITING);
4525 InitMovingField(x, y, MovDir[x][y]);
4527 PlayLevelSoundAction(x, y, ACTION_MOVING);
4531 ContinueMoving(x, y);
4534 void ContinueMoving(int x, int y)
4536 int element = Feld[x][y];
4537 int direction = MovDir[x][y];
4538 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
4539 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
4540 int newx = x + dx, newy = y + dy;
4541 int nextx = newx + dx, nexty = newy + dy;
4542 boolean pushed = Pushed[x][y];
4544 MovPos[x][y] += getElementMoveStepsize(x, y);
4546 if (pushed) /* special case: moving object pushed by player */
4547 MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
4549 if (ABS(MovPos[x][y]) < TILEX)
4551 DrawLevelField(x, y);
4553 return; /* element is still moving */
4556 /* element reached destination field */
4558 Feld[x][y] = EL_EMPTY;
4559 Feld[newx][newy] = element;
4560 MovPos[x][y] = 0; /* force "not moving" for "crumbled sand" */
4562 if (element == EL_MOLE)
4564 Feld[x][y] = EL_SAND;
4566 DrawLevelFieldCrumbledSandNeighbours(x, y);
4568 else if (element == EL_QUICKSAND_FILLING)
4570 element = Feld[newx][newy] = get_next_element(element);
4571 Store[newx][newy] = Store[x][y];
4573 else if (element == EL_QUICKSAND_EMPTYING)
4575 Feld[x][y] = get_next_element(element);
4576 element = Feld[newx][newy] = Store[x][y];
4578 else if (element == EL_MAGIC_WALL_FILLING)
4580 element = Feld[newx][newy] = get_next_element(element);
4581 if (!game.magic_wall_active)
4582 element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
4583 Store[newx][newy] = Store[x][y];
4585 else if (element == EL_MAGIC_WALL_EMPTYING)
4587 Feld[x][y] = get_next_element(element);
4588 if (!game.magic_wall_active)
4589 Feld[x][y] = EL_MAGIC_WALL_DEAD;
4590 element = Feld[newx][newy] = Store[x][y];
4592 else if (element == EL_BD_MAGIC_WALL_FILLING)
4594 element = Feld[newx][newy] = get_next_element(element);
4595 if (!game.magic_wall_active)
4596 element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
4597 Store[newx][newy] = Store[x][y];
4599 else if (element == EL_BD_MAGIC_WALL_EMPTYING)
4601 Feld[x][y] = get_next_element(element);
4602 if (!game.magic_wall_active)
4603 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
4604 element = Feld[newx][newy] = Store[x][y];
4606 else if (element == EL_AMOEBA_DROPPING)
4608 Feld[x][y] = get_next_element(element);
4609 element = Feld[newx][newy] = Store[x][y];
4611 else if (element == EL_SOKOBAN_OBJECT)
4614 Feld[x][y] = Back[x][y];
4616 if (Back[newx][newy])
4617 Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
4619 Back[x][y] = Back[newx][newy] = 0;
4621 else if (Store[x][y] == EL_ACID)
4623 element = Feld[newx][newy] = EL_ACID;
4627 MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
4628 MovDelay[newx][newy] = 0;
4630 /* copy element change control values to new field */
4631 ChangeDelay[newx][newy] = ChangeDelay[x][y];
4632 ChangePage[newx][newy] = ChangePage[x][y];
4633 Changed[newx][newy] = Changed[x][y];
4634 ChangeEvent[newx][newy] = ChangeEvent[x][y];
4636 ChangeDelay[x][y] = 0;
4637 ChangePage[x][y] = -1;
4638 Changed[x][y] = CE_BITMASK_DEFAULT;
4639 ChangeEvent[x][y] = CE_BITMASK_DEFAULT;
4641 /* copy animation control values to new field */
4642 GfxFrame[newx][newy] = GfxFrame[x][y];
4643 GfxRandom[newx][newy] = GfxRandom[x][y]; /* keep same random value */
4644 GfxAction[newx][newy] = GfxAction[x][y]; /* keep action one frame */
4645 GfxDir[newx][newy] = GfxDir[x][y]; /* keep element direction */
4647 Pushed[x][y] = Pushed[newx][newy] = FALSE;
4649 ResetGfxAnimation(x, y); /* reset animation values for old field */
4652 /* 2.1.1 (does not work correctly for spring) */
4653 if (!CAN_MOVE(element))
4654 MovDir[newx][newy] = 0;
4658 /* (does not work for falling objects that slide horizontally) */
4659 if (CAN_FALL(element) && MovDir[newx][newy] == MV_DOWN)
4660 MovDir[newx][newy] = 0;
4663 if (!CAN_MOVE(element) ||
4664 (element == EL_SPRING && MovDir[newx][newy] == MV_DOWN))
4665 MovDir[newx][newy] = 0;
4668 if (!CAN_MOVE(element) ||
4669 (CAN_FALL(element) && direction == MV_DOWN))
4670 GfxDir[x][y] = MovDir[newx][newy] = 0;
4675 DrawLevelField(x, y);
4676 DrawLevelField(newx, newy);
4678 Stop[newx][newy] = TRUE; /* ignore this element until the next frame */
4680 /* prevent pushed element from moving on in pushed direction */
4681 if (pushed && CAN_MOVE(element) &&
4682 element_info[element].move_pattern & MV_ANY_DIRECTION &&
4683 !(element_info[element].move_pattern & direction))
4684 TurnRound(newx, newy);
4686 if (!pushed) /* special case: moving object pushed by player */
4688 WasJustMoving[newx][newy] = 3;
4690 if (CAN_FALL(element) && direction == MV_DOWN)
4691 WasJustFalling[newx][newy] = 3;
4694 if (DONT_TOUCH(element)) /* object may be nasty to player or others */
4696 TestIfBadThingTouchesHero(newx, newy);
4697 TestIfBadThingTouchesFriend(newx, newy);
4699 if (!IS_CUSTOM_ELEMENT(element))
4700 TestIfBadThingTouchesOtherBadThing(newx, newy);
4702 else if (element == EL_PENGUIN)
4703 TestIfFriendTouchesBadThing(newx, newy);
4705 if (CAN_FALL(element) && direction == MV_DOWN &&
4706 (newy == lev_fieldy - 1 || !IS_FREE(x, newy + 1)))
4710 TestIfElementTouchesCustomElement(x, y); /* for empty space */
4714 if (ChangePage[newx][newy] != -1) /* delayed change */
4715 ChangeElement(newx, newy, ChangePage[newx][newy]);
4718 if (!IN_LEV_FIELD(nextx, nexty) || !IS_FREE(nextx, nexty))
4719 CheckElementSideChange(newx, newy, Feld[newx][newy], direction,
4722 TestIfPlayerTouchesCustomElement(newx, newy);
4723 TestIfElementTouchesCustomElement(newx, newy);
4726 int AmoebeNachbarNr(int ax, int ay)
4729 int element = Feld[ax][ay];
4731 static int xy[4][2] =
4741 int x = ax + xy[i][0];
4742 int y = ay + xy[i][1];
4744 if (!IN_LEV_FIELD(x, y))
4747 if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
4748 group_nr = AmoebaNr[x][y];
4754 void AmoebenVereinigen(int ax, int ay)
4756 int i, x, y, xx, yy;
4757 int new_group_nr = AmoebaNr[ax][ay];
4758 static int xy[4][2] =
4766 if (new_group_nr == 0)
4774 if (!IN_LEV_FIELD(x, y))
4777 if ((Feld[x][y] == EL_AMOEBA_FULL ||
4778 Feld[x][y] == EL_BD_AMOEBA ||
4779 Feld[x][y] == EL_AMOEBA_DEAD) &&
4780 AmoebaNr[x][y] != new_group_nr)
4782 int old_group_nr = AmoebaNr[x][y];
4784 if (old_group_nr == 0)
4787 AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
4788 AmoebaCnt[old_group_nr] = 0;
4789 AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
4790 AmoebaCnt2[old_group_nr] = 0;
4792 for (yy=0; yy<lev_fieldy; yy++)
4794 for (xx=0; xx<lev_fieldx; xx++)
4796 if (AmoebaNr[xx][yy] == old_group_nr)
4797 AmoebaNr[xx][yy] = new_group_nr;
4804 void AmoebeUmwandeln(int ax, int ay)
4808 if (Feld[ax][ay] == EL_AMOEBA_DEAD)
4810 int group_nr = AmoebaNr[ax][ay];
4815 printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
4816 printf("AmoebeUmwandeln(): This should never happen!\n");
4821 for (y=0; y<lev_fieldy; y++)
4823 for (x=0; x<lev_fieldx; x++)
4825 if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
4828 Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
4832 PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
4833 SND_AMOEBA_TURNING_TO_GEM :
4834 SND_AMOEBA_TURNING_TO_ROCK));
4839 static int xy[4][2] =
4852 if (!IN_LEV_FIELD(x, y))
4855 if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
4857 PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
4858 SND_AMOEBA_TURNING_TO_GEM :
4859 SND_AMOEBA_TURNING_TO_ROCK));
4866 void AmoebeUmwandelnBD(int ax, int ay, int new_element)
4869 int group_nr = AmoebaNr[ax][ay];
4870 boolean done = FALSE;
4875 printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
4876 printf("AmoebeUmwandelnBD(): This should never happen!\n");
4881 for (y=0; y<lev_fieldy; y++)
4883 for (x=0; x<lev_fieldx; x++)
4885 if (AmoebaNr[x][y] == group_nr &&
4886 (Feld[x][y] == EL_AMOEBA_DEAD ||
4887 Feld[x][y] == EL_BD_AMOEBA ||
4888 Feld[x][y] == EL_AMOEBA_GROWING))
4891 Feld[x][y] = new_element;
4892 InitField(x, y, FALSE);
4893 DrawLevelField(x, y);
4900 PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
4901 SND_BD_AMOEBA_TURNING_TO_ROCK :
4902 SND_BD_AMOEBA_TURNING_TO_GEM));
4905 void AmoebeWaechst(int x, int y)
4907 static unsigned long sound_delay = 0;
4908 static unsigned long sound_delay_value = 0;
4910 if (!MovDelay[x][y]) /* start new growing cycle */
4914 if (DelayReached(&sound_delay, sound_delay_value))
4917 PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
4919 if (Store[x][y] == EL_BD_AMOEBA)
4920 PlayLevelSound(x, y, SND_BD_AMOEBA_GROWING);
4922 PlayLevelSound(x, y, SND_AMOEBA_GROWING);
4924 sound_delay_value = 30;
4928 if (MovDelay[x][y]) /* wait some time before growing bigger */
4931 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
4933 int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
4934 6 - MovDelay[x][y]);
4936 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
4939 if (!MovDelay[x][y])
4941 Feld[x][y] = Store[x][y];
4943 DrawLevelField(x, y);
4948 void AmoebaDisappearing(int x, int y)
4950 static unsigned long sound_delay = 0;
4951 static unsigned long sound_delay_value = 0;
4953 if (!MovDelay[x][y]) /* start new shrinking cycle */
4957 if (DelayReached(&sound_delay, sound_delay_value))
4958 sound_delay_value = 30;
4961 if (MovDelay[x][y]) /* wait some time before shrinking */
4964 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
4966 int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
4967 6 - MovDelay[x][y]);
4969 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
4972 if (!MovDelay[x][y])
4974 Feld[x][y] = EL_EMPTY;
4975 DrawLevelField(x, y);
4977 /* don't let mole enter this field in this cycle;
4978 (give priority to objects falling to this field from above) */
4984 void AmoebeAbleger(int ax, int ay)
4987 int element = Feld[ax][ay];
4988 int graphic = el2img(element);
4989 int newax = ax, neway = ay;
4990 static int xy[4][2] =
4998 if (!level.amoeba_speed)
5000 Feld[ax][ay] = EL_AMOEBA_DEAD;
5001 DrawLevelField(ax, ay);
5005 if (IS_ANIMATED(graphic))
5006 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
5008 if (!MovDelay[ax][ay]) /* start making new amoeba field */
5009 MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
5011 if (MovDelay[ax][ay]) /* wait some time before making new amoeba */
5014 if (MovDelay[ax][ay])
5018 if (element == EL_AMOEBA_WET) /* object is an acid / amoeba drop */
5021 int x = ax + xy[start][0];
5022 int y = ay + xy[start][1];
5024 if (!IN_LEV_FIELD(x, y))
5027 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
5028 if (IS_FREE(x, y) ||
5029 Feld[x][y] == EL_SAND || Feld[x][y] == EL_QUICKSAND_EMPTY)
5035 if (newax == ax && neway == ay)
5038 else /* normal or "filled" (BD style) amoeba */
5041 boolean waiting_for_player = FALSE;
5045 int j = (start + i) % 4;
5046 int x = ax + xy[j][0];
5047 int y = ay + xy[j][1];
5049 if (!IN_LEV_FIELD(x, y))
5052 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
5053 if (IS_FREE(x, y) ||
5054 Feld[x][y] == EL_SAND || Feld[x][y] == EL_QUICKSAND_EMPTY)
5060 else if (IS_PLAYER(x, y))
5061 waiting_for_player = TRUE;
5064 if (newax == ax && neway == ay) /* amoeba cannot grow */
5066 if (i == 4 && (!waiting_for_player || game.emulation == EMU_BOULDERDASH))
5068 Feld[ax][ay] = EL_AMOEBA_DEAD;
5069 DrawLevelField(ax, ay);
5070 AmoebaCnt[AmoebaNr[ax][ay]]--;
5072 if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0) /* amoeba is completely dead */
5074 if (element == EL_AMOEBA_FULL)
5075 AmoebeUmwandeln(ax, ay);
5076 else if (element == EL_BD_AMOEBA)
5077 AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
5082 else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
5084 /* amoeba gets larger by growing in some direction */
5086 int new_group_nr = AmoebaNr[ax][ay];
5089 if (new_group_nr == 0)
5091 printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
5092 printf("AmoebeAbleger(): This should never happen!\n");
5097 AmoebaNr[newax][neway] = new_group_nr;
5098 AmoebaCnt[new_group_nr]++;
5099 AmoebaCnt2[new_group_nr]++;
5101 /* if amoeba touches other amoeba(s) after growing, unify them */
5102 AmoebenVereinigen(newax, neway);
5104 if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
5106 AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
5112 if (element != EL_AMOEBA_WET || neway < ay || !IS_FREE(newax, neway) ||
5113 (neway == lev_fieldy - 1 && newax != ax))
5115 Feld[newax][neway] = EL_AMOEBA_GROWING; /* creation of new amoeba */
5116 Store[newax][neway] = element;
5118 else if (neway == ay)
5120 Feld[newax][neway] = EL_AMOEBA_DROP; /* drop left/right of amoeba */
5122 PlayLevelSoundAction(newax, neway, ACTION_GROWING);
5124 PlayLevelSound(newax, neway, SND_AMOEBA_GROWING);
5129 InitMovingField(ax, ay, MV_DOWN); /* drop dripping from amoeba */
5130 Feld[ax][ay] = EL_AMOEBA_DROPPING;
5131 Store[ax][ay] = EL_AMOEBA_DROP;
5132 ContinueMoving(ax, ay);
5136 DrawLevelField(newax, neway);
5139 void Life(int ax, int ay)
5142 static int life[4] = { 2, 3, 3, 3 }; /* parameters for "game of life" */
5144 int element = Feld[ax][ay];
5145 int graphic = el2img(element);
5146 boolean changed = FALSE;
5148 if (IS_ANIMATED(graphic))
5149 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
5154 if (!MovDelay[ax][ay]) /* start new "game of life" cycle */
5155 MovDelay[ax][ay] = life_time;
5157 if (MovDelay[ax][ay]) /* wait some time before next cycle */
5160 if (MovDelay[ax][ay])
5164 for (y1=-1; y1<2; y1++) for(x1=-1; x1<2; x1++)
5166 int xx = ax+x1, yy = ay+y1;
5169 if (!IN_LEV_FIELD(xx, yy))
5172 for (y2=-1; y2<2; y2++) for (x2=-1; x2<2; x2++)
5174 int x = xx+x2, y = yy+y2;
5176 if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
5179 if (((Feld[x][y] == element ||
5180 (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y))) &&
5182 (IS_FREE(x, y) && Stop[x][y]))
5186 if (xx == ax && yy == ay) /* field in the middle */
5188 if (nachbarn < life[0] || nachbarn > life[1])
5190 Feld[xx][yy] = EL_EMPTY;
5192 DrawLevelField(xx, yy);
5193 Stop[xx][yy] = TRUE;
5197 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
5198 else if (IS_FREE(xx, yy) || Feld[xx][yy] == EL_SAND)
5199 { /* free border field */
5200 if (nachbarn >= life[2] && nachbarn <= life[3])
5202 Feld[xx][yy] = element;
5203 MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
5205 DrawLevelField(xx, yy);
5206 Stop[xx][yy] = TRUE;
5213 PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
5214 SND_GAME_OF_LIFE_GROWING);
5217 static void InitRobotWheel(int x, int y)
5219 ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
5222 static void RunRobotWheel(int x, int y)
5224 PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
5227 static void StopRobotWheel(int x, int y)
5229 if (ZX == x && ZY == y)
5233 static void InitTimegateWheel(int x, int y)
5235 ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
5238 static void RunTimegateWheel(int x, int y)
5240 PlayLevelSound(x, y, SND_TIMEGATE_SWITCH_ACTIVE);
5243 void CheckExit(int x, int y)
5245 if (local_player->gems_still_needed > 0 ||
5246 local_player->sokobanfields_still_needed > 0 ||
5247 local_player->lights_still_needed > 0)
5249 int element = Feld[x][y];
5250 int graphic = el2img(element);
5252 if (IS_ANIMATED(graphic))
5253 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
5258 if (AllPlayersGone) /* do not re-open exit door closed after last player */
5261 Feld[x][y] = EL_EXIT_OPENING;
5263 PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
5266 void CheckExitSP(int x, int y)
5268 if (local_player->gems_still_needed > 0)
5270 int element = Feld[x][y];
5271 int graphic = el2img(element);
5273 if (IS_ANIMATED(graphic))
5274 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
5279 if (AllPlayersGone) /* do not re-open exit door closed after last player */
5282 Feld[x][y] = EL_SP_EXIT_OPENING;
5284 PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
5287 static void CloseAllOpenTimegates()
5291 for (y=0; y<lev_fieldy; y++)
5293 for (x=0; x<lev_fieldx; x++)
5295 int element = Feld[x][y];
5297 if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
5299 Feld[x][y] = EL_TIMEGATE_CLOSING;
5301 PlayLevelSoundAction(x, y, ACTION_CLOSING);
5303 PlayLevelSound(x, y, SND_TIMEGATE_CLOSING);
5310 void EdelsteinFunkeln(int x, int y)
5312 if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
5315 if (Feld[x][y] == EL_BD_DIAMOND)
5318 if (MovDelay[x][y] == 0) /* next animation frame */
5319 MovDelay[x][y] = 11 * !SimpleRND(500);
5321 if (MovDelay[x][y] != 0) /* wait some time before next frame */
5325 if (setup.direct_draw && MovDelay[x][y])
5326 SetDrawtoField(DRAW_BUFFERED);
5328 DrawLevelElementAnimation(x, y, Feld[x][y]);
5330 if (MovDelay[x][y] != 0)
5332 int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
5333 10 - MovDelay[x][y]);
5335 DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
5337 if (setup.direct_draw)
5341 dest_x = FX + SCREENX(x) * TILEX;
5342 dest_y = FY + SCREENY(y) * TILEY;
5344 BlitBitmap(drawto_field, window,
5345 dest_x, dest_y, TILEX, TILEY, dest_x, dest_y);
5346 SetDrawtoField(DRAW_DIRECT);
5352 void MauerWaechst(int x, int y)
5356 if (!MovDelay[x][y]) /* next animation frame */
5357 MovDelay[x][y] = 3 * delay;
5359 if (MovDelay[x][y]) /* wait some time before next frame */
5363 if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
5365 int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
5366 int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
5368 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5371 if (!MovDelay[x][y])
5373 if (MovDir[x][y] == MV_LEFT)
5375 if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
5376 DrawLevelField(x - 1, y);
5378 else if (MovDir[x][y] == MV_RIGHT)
5380 if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
5381 DrawLevelField(x + 1, y);
5383 else if (MovDir[x][y] == MV_UP)
5385 if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
5386 DrawLevelField(x, y - 1);
5390 if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
5391 DrawLevelField(x, y + 1);
5394 Feld[x][y] = Store[x][y];
5396 GfxDir[x][y] = MovDir[x][y] = MV_NO_MOVING;
5397 DrawLevelField(x, y);
5402 void MauerAbleger(int ax, int ay)
5404 int element = Feld[ax][ay];
5405 int graphic = el2img(element);
5406 boolean oben_frei = FALSE, unten_frei = FALSE;
5407 boolean links_frei = FALSE, rechts_frei = FALSE;
5408 boolean oben_massiv = FALSE, unten_massiv = FALSE;
5409 boolean links_massiv = FALSE, rechts_massiv = FALSE;
5410 boolean new_wall = FALSE;
5412 if (IS_ANIMATED(graphic))
5413 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
5415 if (!MovDelay[ax][ay]) /* start building new wall */
5416 MovDelay[ax][ay] = 6;
5418 if (MovDelay[ax][ay]) /* wait some time before building new wall */
5421 if (MovDelay[ax][ay])
5425 if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
5427 if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
5429 if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
5431 if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
5434 if (element == EL_EXPANDABLE_WALL_VERTICAL ||
5435 element == EL_EXPANDABLE_WALL_ANY)
5439 Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
5440 Store[ax][ay-1] = element;
5441 GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
5442 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
5443 DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
5444 IMG_EXPANDABLE_WALL_GROWING_UP, 0);
5449 Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
5450 Store[ax][ay+1] = element;
5451 GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
5452 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
5453 DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
5454 IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
5459 if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
5460 element == EL_EXPANDABLE_WALL_ANY ||
5461 element == EL_EXPANDABLE_WALL)
5465 Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
5466 Store[ax-1][ay] = element;
5467 GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
5468 if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
5469 DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
5470 IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
5476 Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
5477 Store[ax+1][ay] = element;
5478 GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
5479 if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
5480 DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
5481 IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
5486 if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
5487 DrawLevelField(ax, ay);
5489 if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
5491 if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
5492 unten_massiv = TRUE;
5493 if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
5494 links_massiv = TRUE;
5495 if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
5496 rechts_massiv = TRUE;
5498 if (((oben_massiv && unten_massiv) ||
5499 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
5500 element == EL_EXPANDABLE_WALL) &&
5501 ((links_massiv && rechts_massiv) ||
5502 element == EL_EXPANDABLE_WALL_VERTICAL))
5503 Feld[ax][ay] = EL_WALL;
5507 PlayLevelSoundAction(ax, ay, ACTION_GROWING);
5509 PlayLevelSound(ax, ay, SND_EXPANDABLE_WALL_GROWING);
5513 void CheckForDragon(int x, int y)
5516 boolean dragon_found = FALSE;
5517 static int xy[4][2] =
5529 int xx = x + j*xy[i][0], yy = y + j*xy[i][1];
5531 if (IN_LEV_FIELD(xx, yy) &&
5532 (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
5534 if (Feld[xx][yy] == EL_DRAGON)
5535 dragon_found = TRUE;
5548 int xx = x + j*xy[i][0], yy = y + j*xy[i][1];
5550 if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
5552 Feld[xx][yy] = EL_EMPTY;
5553 DrawLevelField(xx, yy);
5562 static void InitBuggyBase(int x, int y)
5564 int element = Feld[x][y];
5565 int activating_delay = FRAMES_PER_SECOND / 4;
5568 (element == EL_SP_BUGGY_BASE ?
5569 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
5570 element == EL_SP_BUGGY_BASE_ACTIVATING ?
5572 element == EL_SP_BUGGY_BASE_ACTIVE ?
5573 1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
5576 static void WarnBuggyBase(int x, int y)
5579 static int xy[4][2] =
5589 int xx = x + xy[i][0], yy = y + xy[i][1];
5591 if (IS_PLAYER(xx, yy))
5593 PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
5600 static void InitTrap(int x, int y)
5602 ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
5605 static void ActivateTrap(int x, int y)
5607 PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
5610 static void ChangeActiveTrap(int x, int y)
5612 int graphic = IMG_TRAP_ACTIVE;
5614 /* if new animation frame was drawn, correct crumbled sand border */
5615 if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
5616 DrawLevelFieldCrumbledSand(x, y);
5619 static void ChangeElementNowExt(int x, int y, int target_element)
5621 /* check if element under player changes from accessible to unaccessible
5622 (needed for special case of dropping element which then changes) */
5623 if (IS_PLAYER(x, y) && !PLAYER_PROTECTED(x, y) &&
5624 IS_ACCESSIBLE(Feld[x][y]) && !IS_ACCESSIBLE(target_element))
5631 Feld[x][y] = target_element;
5633 Changed[x][y] |= ChangeEvent[x][y]; /* ignore same changes in this frame */
5635 ResetGfxAnimation(x, y);
5636 ResetRandomAnimationValue(x, y);
5638 InitField(x, y, FALSE);
5639 if (CAN_MOVE(Feld[x][y]))
5642 DrawLevelField(x, y);
5644 if (GFX_CRUMBLED(Feld[x][y]))
5645 DrawLevelFieldCrumbledSandNeighbours(x, y);
5647 TestIfBadThingTouchesHero(x, y);
5648 TestIfPlayerTouchesCustomElement(x, y);
5649 TestIfElementTouchesCustomElement(x, y);
5651 if (ELEM_IS_PLAYER(target_element))
5652 RelocatePlayer(x, y, target_element);
5655 static boolean ChangeElementNow(int x, int y, int element, int page)
5657 struct ElementChangeInfo *change = &element_info[element].change_page[page];
5659 /* always use default change event to prevent running into a loop */
5660 if (ChangeEvent[x][y] == CE_BITMASK_DEFAULT)
5661 ChangeEvent[x][y] = CH_EVENT_BIT(CE_DELAY);
5663 /* do not change already changed elements with same change event */
5665 if (Changed[x][y] & ChangeEvent[x][y])
5672 Changed[x][y] |= ChangeEvent[x][y]; /* ignore same changes in this frame */
5674 CheckTriggeredElementChange(x, y, Feld[x][y], CE_OTHER_IS_CHANGING);
5676 if (change->explode)
5683 if (change->use_content)
5685 boolean complete_change = TRUE;
5686 boolean can_change[3][3];
5689 for (yy = 0; yy < 3; yy++) for(xx = 0; xx < 3 ; xx++)
5691 boolean half_destructible;
5692 int ex = x + xx - 1;
5693 int ey = y + yy - 1;
5696 can_change[xx][yy] = TRUE;
5698 if (ex == x && ey == y) /* do not check changing element itself */
5701 if (change->content[xx][yy] == EL_EMPTY_SPACE)
5703 can_change[xx][yy] = FALSE; /* do not change empty borders */
5708 if (!IN_LEV_FIELD(ex, ey))
5710 can_change[xx][yy] = FALSE;
5711 complete_change = FALSE;
5718 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
5719 e = MovingOrBlocked2Element(ex, ey);
5721 half_destructible = (IS_FREE(ex, ey) || IS_DIGGABLE(e));
5723 if ((change->power <= CP_NON_DESTRUCTIVE && !IS_FREE(ex, ey)) ||
5724 (change->power <= CP_HALF_DESTRUCTIVE && !half_destructible) ||
5725 (change->power <= CP_FULL_DESTRUCTIVE && IS_INDESTRUCTIBLE(e)))
5727 can_change[xx][yy] = FALSE;
5728 complete_change = FALSE;
5732 if (!change->only_complete || complete_change)
5734 boolean something_has_changed = FALSE;
5736 if (change->only_complete && change->use_random_change &&
5737 RND(100) < change->random)
5740 for (yy = 0; yy < 3; yy++) for(xx = 0; xx < 3 ; xx++)
5742 int ex = x + xx - 1;
5743 int ey = y + yy - 1;
5745 if (can_change[xx][yy] && (!change->use_random_change ||
5746 RND(100) < change->random))
5748 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
5749 RemoveMovingField(ex, ey);
5751 ChangeEvent[ex][ey] = ChangeEvent[x][y];
5753 ChangeElementNowExt(ex, ey, change->content[xx][yy]);
5755 something_has_changed = TRUE;
5757 /* for symmetry reasons, freeze newly created border elements */
5758 if (ex != x || ey != y)
5759 Stop[ex][ey] = TRUE; /* no more moving in this frame */
5763 if (something_has_changed)
5764 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
5769 ChangeElementNowExt(x, y, change->target_element);
5771 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
5777 static void ChangeElement(int x, int y, int page)
5779 int element = MovingOrBlocked2Element(x, y);
5780 struct ElementInfo *ei = &element_info[element];
5781 struct ElementChangeInfo *change = &ei->change_page[page];
5785 if (!CAN_CHANGE(element))
5788 printf("ChangeElement(): %d,%d: element = %d ('%s')\n",
5789 x, y, element, element_info[element].token_name);
5790 printf("ChangeElement(): This should never happen!\n");
5796 if (ChangeDelay[x][y] == 0) /* initialize element change */
5798 ChangeDelay[x][y] = ( change->delay_fixed * change->delay_frames +
5799 RND(change->delay_random * change->delay_frames)) + 1;
5801 ResetGfxAnimation(x, y);
5802 ResetRandomAnimationValue(x, y);
5804 if (change->pre_change_function)
5805 change->pre_change_function(x, y);
5808 ChangeDelay[x][y]--;
5810 if (ChangeDelay[x][y] != 0) /* continue element change */
5812 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
5814 if (IS_ANIMATED(graphic))
5815 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
5817 if (change->change_function)
5818 change->change_function(x, y);
5820 else /* finish element change */
5822 if (ChangePage[x][y] != -1) /* remember page from delayed change */
5824 page = ChangePage[x][y];
5825 ChangePage[x][y] = -1;
5828 if (IS_MOVING(x, y)) /* never change a running system ;-) */
5830 ChangeDelay[x][y] = 1; /* try change after next move step */
5831 ChangePage[x][y] = page; /* remember page to use for change */
5836 if (ChangeElementNow(x, y, element, page))
5838 if (change->post_change_function)
5839 change->post_change_function(x, y);
5844 static boolean CheckTriggeredElementSideChange(int lx, int ly,
5845 int trigger_element,
5851 if (!(trigger_events[trigger_element] & CH_EVENT_BIT(trigger_event)))
5854 for (i=0; i < NUM_CUSTOM_ELEMENTS; i++)
5856 int element = EL_CUSTOM_START + i;
5858 boolean change_element = FALSE;
5861 if (!CAN_CHANGE(element) || !HAS_ANY_CHANGE_EVENT(element, trigger_event))
5864 for (j=0; j < element_info[element].num_change_pages; j++)
5866 struct ElementChangeInfo *change = &element_info[element].change_page[j];
5868 if (change->can_change &&
5870 change->events & CH_EVENT_BIT(trigger_event) &&
5872 change->sides & trigger_side &&
5873 change->trigger_element == trigger_element)
5876 if (!(change->events & CH_EVENT_BIT(trigger_event)))
5877 printf("::: !!! %d triggers %d: using wrong page %d [event %d]\n",
5878 trigger_element-EL_CUSTOM_START+1, i+1, j, trigger_event);
5881 change_element = TRUE;
5888 if (!change_element)
5891 for (y=0; y<lev_fieldy; y++) for (x=0; x<lev_fieldx; x++)
5894 if (x == lx && y == ly) /* do not change trigger element itself */
5898 if (Feld[x][y] == element)
5900 ChangeDelay[x][y] = 1;
5901 ChangeEvent[x][y] = CH_EVENT_BIT(trigger_event);
5902 ChangeElement(x, y, page);
5910 static boolean CheckTriggeredElementChange(int lx, int ly, int trigger_element,
5913 return CheckTriggeredElementSideChange(lx, ly, trigger_element, CH_SIDE_ANY,
5917 static boolean CheckElementSideChange(int x, int y, int element, int side,
5918 int trigger_event, int page)
5920 if (!CAN_CHANGE(element) || !HAS_ANY_CHANGE_EVENT(element, trigger_event))
5923 if (Feld[x][y] == EL_BLOCKED)
5925 Blocked2Moving(x, y, &x, &y);
5926 element = Feld[x][y];
5930 page = element_info[element].event_page_nr[trigger_event];
5932 if (!(element_info[element].change_page[page].sides & side))
5935 ChangeDelay[x][y] = 1;
5936 ChangeEvent[x][y] = CH_EVENT_BIT(trigger_event);
5937 ChangeElement(x, y, page);
5942 static boolean CheckElementChange(int x, int y, int element, int trigger_event)
5944 return CheckElementSideChange(x, y, element, CH_SIDE_ANY, trigger_event, -1);
5948 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
5951 static byte stored_player_action[MAX_PLAYERS];
5952 static int num_stored_actions = 0;
5954 boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
5955 int left = player_action & JOY_LEFT;
5956 int right = player_action & JOY_RIGHT;
5957 int up = player_action & JOY_UP;
5958 int down = player_action & JOY_DOWN;
5959 int button1 = player_action & JOY_BUTTON_1;
5960 int button2 = player_action & JOY_BUTTON_2;
5961 int dx = (left ? -1 : right ? 1 : 0);
5962 int dy = (up ? -1 : down ? 1 : 0);
5965 stored_player_action[player->index_nr] = 0;
5966 num_stored_actions++;
5970 printf("::: player %d [%d]\n", player->index_nr, FrameCounter);
5973 if (!player->active || tape.pausing)
5979 printf("::: player %d acts [%d]\n", player->index_nr, FrameCounter);
5983 snapped = SnapField(player, dx, dy);
5987 dropped = DropElement(player);
5989 moved = MovePlayer(player, dx, dy);
5992 if (tape.single_step && tape.recording && !tape.pausing)
5994 if (button1 || (dropped && !moved))
5996 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
5997 SnapField(player, 0, 0); /* stop snapping */
6002 return player_action;
6004 stored_player_action[player->index_nr] = player_action;
6010 printf("::: player %d waits [%d]\n", player->index_nr, FrameCounter);
6013 /* no actions for this player (no input at player's configured device) */
6015 DigField(player, 0, 0, 0, 0, DF_NO_PUSH);
6016 SnapField(player, 0, 0);
6017 CheckGravityMovement(player);
6019 if (player->MovPos == 0)
6020 InitPlayerGfxAnimation(player, ACTION_DEFAULT, player->MovDir);
6022 if (player->MovPos == 0) /* needed for tape.playing */
6023 player->is_moving = FALSE;
6029 if (tape.recording && num_stored_actions >= MAX_PLAYERS)
6031 printf("::: player %d recorded [%d]\n", player->index_nr, FrameCounter);
6033 TapeRecordAction(stored_player_action);
6034 num_stored_actions = 0;
6041 static void PlayerActions(struct PlayerInfo *player, byte player_action)
6043 static byte stored_player_action[MAX_PLAYERS];
6044 static int num_stored_actions = 0;
6045 boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
6046 int left = player_action & JOY_LEFT;
6047 int right = player_action & JOY_RIGHT;
6048 int up = player_action & JOY_UP;
6049 int down = player_action & JOY_DOWN;
6050 int button1 = player_action & JOY_BUTTON_1;
6051 int button2 = player_action & JOY_BUTTON_2;
6052 int dx = (left ? -1 : right ? 1 : 0);
6053 int dy = (up ? -1 : down ? 1 : 0);
6055 stored_player_action[player->index_nr] = 0;
6056 num_stored_actions++;
6058 printf("::: player %d [%d]\n", player->index_nr, FrameCounter);
6060 if (!player->active || tape.pausing)
6065 printf("::: player %d acts [%d]\n", player->index_nr, FrameCounter);
6068 snapped = SnapField(player, dx, dy);
6072 dropped = DropElement(player);
6074 moved = MovePlayer(player, dx, dy);
6077 if (tape.single_step && tape.recording && !tape.pausing)
6079 if (button1 || (dropped && !moved))
6081 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
6082 SnapField(player, 0, 0); /* stop snapping */
6086 stored_player_action[player->index_nr] = player_action;
6090 printf("::: player %d waits [%d]\n", player->index_nr, FrameCounter);
6092 /* no actions for this player (no input at player's configured device) */
6094 DigField(player, 0, 0, 0, 0, DF_NO_PUSH);
6095 SnapField(player, 0, 0);
6096 CheckGravityMovement(player);
6098 if (player->MovPos == 0)
6099 InitPlayerGfxAnimation(player, ACTION_DEFAULT, player->MovDir);
6101 if (player->MovPos == 0) /* needed for tape.playing */
6102 player->is_moving = FALSE;
6105 if (tape.recording && num_stored_actions >= MAX_PLAYERS)
6107 printf("::: player %d recorded [%d]\n", player->index_nr, FrameCounter);
6109 TapeRecordAction(stored_player_action);
6110 num_stored_actions = 0;
6117 static unsigned long action_delay = 0;
6118 unsigned long action_delay_value;
6119 int magic_wall_x = 0, magic_wall_y = 0;
6120 int i, x, y, element, graphic;
6121 byte *recorded_player_action;
6122 byte summarized_player_action = 0;
6124 byte tape_action[MAX_PLAYERS];
6127 if (game_status != GAME_MODE_PLAYING)
6130 action_delay_value =
6131 (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
6133 if (tape.playing && tape.index_search && !tape.pausing)
6134 action_delay_value = 0;
6136 /* ---------- main game synchronization point ---------- */
6138 WaitUntilDelayReached(&action_delay, action_delay_value);
6140 if (network_playing && !network_player_action_received)
6144 printf("DEBUG: try to get network player actions in time\n");
6148 #if defined(PLATFORM_UNIX)
6149 /* last chance to get network player actions without main loop delay */
6153 if (game_status != GAME_MODE_PLAYING)
6156 if (!network_player_action_received)
6160 printf("DEBUG: failed to get network player actions in time\n");
6171 printf("::: getting new tape action [%d]\n", FrameCounter);
6174 recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
6176 for (i=0; i<MAX_PLAYERS; i++)
6178 summarized_player_action |= stored_player[i].action;
6180 if (!network_playing)
6181 stored_player[i].effective_action = stored_player[i].action;
6184 #if defined(PLATFORM_UNIX)
6185 if (network_playing)
6186 SendToServer_MovePlayer(summarized_player_action);
6189 if (!options.network && !setup.team_mode)
6190 local_player->effective_action = summarized_player_action;
6192 for (i=0; i < MAX_PLAYERS; i++)
6194 int actual_player_action = stored_player[i].effective_action;
6196 if (stored_player[i].programmed_action)
6197 actual_player_action = stored_player[i].programmed_action;
6199 if (recorded_player_action)
6200 actual_player_action = recorded_player_action[i];
6202 tape_action[i] = PlayerActions(&stored_player[i], actual_player_action);
6204 ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
6209 TapeRecordAction(tape_action);
6212 network_player_action_received = FALSE;
6214 ScrollScreen(NULL, SCROLL_GO_ON);
6220 for (i=0; i<MAX_PLAYERS; i++)
6221 stored_player[i].Frame++;
6225 if (game.engine_version < VERSION_IDENT(2,2,0,7))
6227 for (i=0; i<MAX_PLAYERS; i++)
6229 struct PlayerInfo *player = &stored_player[i];
6233 if (player->active && player->is_pushing && player->is_moving &&
6236 ContinueMoving(x, y);
6238 /* continue moving after pushing (this is actually a bug) */
6239 if (!IS_MOVING(x, y))
6248 for (y=0; y<lev_fieldy; y++) for (x=0; x<lev_fieldx; x++)
6250 Changed[x][y] = CE_BITMASK_DEFAULT;
6251 ChangeEvent[x][y] = CE_BITMASK_DEFAULT;
6254 if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
6256 printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
6257 printf("GameActions(): This should never happen!\n");
6259 ChangePage[x][y] = -1;
6264 if (WasJustMoving[x][y] > 0)
6265 WasJustMoving[x][y]--;
6266 if (WasJustFalling[x][y] > 0)
6267 WasJustFalling[x][y]--;
6272 /* reset finished pushing action (not done in ContinueMoving() to allow
6273 continous pushing animation for elements with zero push delay) */
6274 if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
6276 ResetGfxAnimation(x, y);
6277 DrawLevelField(x, y);
6282 if (IS_BLOCKED(x, y))
6286 Blocked2Moving(x, y, &oldx, &oldy);
6287 if (!IS_MOVING(oldx, oldy))
6289 printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
6290 printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
6291 printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
6292 printf("GameActions(): This should never happen!\n");
6298 for (y=0; y<lev_fieldy; y++) for (x=0; x<lev_fieldx; x++)
6300 element = Feld[x][y];
6302 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
6304 graphic = el2img(element);
6310 printf("::: %d,%d: %d [%d]\n", x, y, element, FrameCounter);
6312 element = graphic = 0;
6316 if (graphic_info[graphic].anim_global_sync)
6317 GfxFrame[x][y] = FrameCounter;
6319 if (ANIM_MODE(graphic) == ANIM_RANDOM &&
6320 IS_NEXT_FRAME(GfxFrame[x][y], graphic))
6321 ResetRandomAnimationValue(x, y);
6323 SetRandomAnimationValue(x, y);
6326 PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
6329 if (IS_INACTIVE(element))
6331 if (IS_ANIMATED(graphic))
6332 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
6338 /* this may take place after moving, so 'element' may have changed */
6340 if (IS_CHANGING(x, y))
6342 if (IS_CHANGING(x, y) &&
6343 (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
6347 ChangeElement(x, y, ChangePage[x][y] != -1 ? ChangePage[x][y] :
6348 element_info[element].event_page_nr[CE_DELAY]);
6350 ChangeElement(x, y, element_info[element].event_page_nr[CE_DELAY]);
6353 element = Feld[x][y];
6354 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
6358 if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
6363 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
6365 if (element == EL_MOLE)
6366 printf("::: %d, %d, %d [%d]\n",
6367 IS_ANIMATED(graphic), IS_MOVING(x, y), Stop[x][y],
6371 if (element == EL_YAMYAM)
6372 printf("::: %d, %d, %d\n",
6373 IS_ANIMATED(graphic), IS_MOVING(x, y), Stop[x][y]);
6377 if (IS_ANIMATED(graphic) &&
6381 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
6384 if (element == EL_BUG)
6385 printf("::: %d, %d\n", graphic, GfxFrame[x][y]);
6389 if (element == EL_MOLE)
6390 printf("::: %d, %d\n", graphic, GfxFrame[x][y]);
6394 if (IS_GEM(element) || element == EL_SP_INFOTRON)
6395 EdelsteinFunkeln(x, y);
6397 else if ((element == EL_ACID ||
6398 element == EL_EXIT_OPEN ||
6399 element == EL_SP_EXIT_OPEN ||
6400 element == EL_SP_TERMINAL ||
6401 element == EL_SP_TERMINAL_ACTIVE ||
6402 element == EL_EXTRA_TIME ||
6403 element == EL_SHIELD_NORMAL ||
6404 element == EL_SHIELD_DEADLY) &&
6405 IS_ANIMATED(graphic))
6406 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
6407 else if (IS_MOVING(x, y))
6408 ContinueMoving(x, y);
6409 else if (IS_ACTIVE_BOMB(element))
6410 CheckDynamite(x, y);
6412 else if (element == EL_EXPLOSION && !game.explosions_delayed)
6413 Explode(x, y, ExplodePhase[x][y], EX_NORMAL);
6415 else if (element == EL_AMOEBA_GROWING)
6416 AmoebeWaechst(x, y);
6417 else if (element == EL_AMOEBA_SHRINKING)
6418 AmoebaDisappearing(x, y);
6420 #if !USE_NEW_AMOEBA_CODE
6421 else if (IS_AMOEBALIVE(element))
6422 AmoebeAbleger(x, y);
6425 else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
6427 else if (element == EL_EXIT_CLOSED)
6429 else if (element == EL_SP_EXIT_CLOSED)
6431 else if (element == EL_EXPANDABLE_WALL_GROWING)
6433 else if (element == EL_EXPANDABLE_WALL ||
6434 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
6435 element == EL_EXPANDABLE_WALL_VERTICAL ||
6436 element == EL_EXPANDABLE_WALL_ANY)
6438 else if (element == EL_FLAMES)
6439 CheckForDragon(x, y);
6441 else if (IS_AUTO_CHANGING(element))
6442 ChangeElement(x, y);
6444 else if (element == EL_EXPLOSION)
6445 ; /* drawing of correct explosion animation is handled separately */
6446 else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
6447 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
6450 /* this may take place after moving, so 'element' may have changed */
6451 if (IS_AUTO_CHANGING(Feld[x][y]))
6452 ChangeElement(x, y);
6455 if (IS_BELT_ACTIVE(element))
6456 PlayLevelSoundAction(x, y, ACTION_ACTIVE);
6458 if (game.magic_wall_active)
6460 int jx = local_player->jx, jy = local_player->jy;
6462 /* play the element sound at the position nearest to the player */
6463 if ((element == EL_MAGIC_WALL_FULL ||
6464 element == EL_MAGIC_WALL_ACTIVE ||
6465 element == EL_MAGIC_WALL_EMPTYING ||
6466 element == EL_BD_MAGIC_WALL_FULL ||
6467 element == EL_BD_MAGIC_WALL_ACTIVE ||
6468 element == EL_BD_MAGIC_WALL_EMPTYING) &&
6469 ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
6477 #if USE_NEW_AMOEBA_CODE
6478 /* new experimental amoeba growth stuff */
6480 if (!(FrameCounter % 8))
6483 static unsigned long random = 1684108901;
6485 for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
6488 x = (random >> 10) % lev_fieldx;
6489 y = (random >> 20) % lev_fieldy;
6491 x = RND(lev_fieldx);
6492 y = RND(lev_fieldy);
6494 element = Feld[x][y];
6496 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
6497 if (!IS_PLAYER(x,y) &&
6498 (element == EL_EMPTY ||
6499 element == EL_SAND ||
6500 element == EL_QUICKSAND_EMPTY ||
6501 element == EL_ACID_SPLASH_LEFT ||
6502 element == EL_ACID_SPLASH_RIGHT))
6504 if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
6505 (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
6506 (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
6507 (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
6508 Feld[x][y] = EL_AMOEBA_DROP;
6511 random = random * 129 + 1;
6517 if (game.explosions_delayed)
6520 game.explosions_delayed = FALSE;
6522 for (y=0; y<lev_fieldy; y++) for (x=0; x<lev_fieldx; x++)
6524 element = Feld[x][y];
6526 if (ExplodeField[x][y])
6527 Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
6528 else if (element == EL_EXPLOSION)
6529 Explode(x, y, ExplodePhase[x][y], EX_NORMAL);
6531 ExplodeField[x][y] = EX_NO_EXPLOSION;
6534 game.explosions_delayed = TRUE;
6537 if (game.magic_wall_active)
6539 if (!(game.magic_wall_time_left % 4))
6541 int element = Feld[magic_wall_x][magic_wall_y];
6543 if (element == EL_BD_MAGIC_WALL_FULL ||
6544 element == EL_BD_MAGIC_WALL_ACTIVE ||
6545 element == EL_BD_MAGIC_WALL_EMPTYING)
6546 PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
6548 PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
6551 if (game.magic_wall_time_left > 0)
6553 game.magic_wall_time_left--;
6554 if (!game.magic_wall_time_left)
6556 for (y=0; y<lev_fieldy; y++) for (x=0; x<lev_fieldx; x++)
6558 element = Feld[x][y];
6560 if (element == EL_MAGIC_WALL_ACTIVE ||
6561 element == EL_MAGIC_WALL_FULL)
6563 Feld[x][y] = EL_MAGIC_WALL_DEAD;
6564 DrawLevelField(x, y);
6566 else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
6567 element == EL_BD_MAGIC_WALL_FULL)
6569 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
6570 DrawLevelField(x, y);
6574 game.magic_wall_active = FALSE;
6579 if (game.light_time_left > 0)
6581 game.light_time_left--;
6583 if (game.light_time_left == 0)
6584 RedrawAllLightSwitchesAndInvisibleElements();
6587 if (game.timegate_time_left > 0)
6589 game.timegate_time_left--;
6591 if (game.timegate_time_left == 0)
6592 CloseAllOpenTimegates();
6595 for (i=0; i<MAX_PLAYERS; i++)
6597 struct PlayerInfo *player = &stored_player[i];
6599 if (SHIELD_ON(player))
6601 if (player->shield_deadly_time_left)
6602 PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
6603 else if (player->shield_normal_time_left)
6604 PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
6608 if (TimeFrames >= FRAMES_PER_SECOND)
6613 for (i=0; i<MAX_PLAYERS; i++)
6615 struct PlayerInfo *player = &stored_player[i];
6617 if (SHIELD_ON(player))
6619 player->shield_normal_time_left--;
6621 if (player->shield_deadly_time_left > 0)
6622 player->shield_deadly_time_left--;
6626 if (tape.recording || tape.playing)
6627 DrawVideoDisplay(VIDEO_STATE_TIME_ON, TimePlayed);
6633 if (TimeLeft <= 10 && setup.time_limit)
6634 PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, SOUND_MIDDLE);
6636 DrawText(DX_TIME, DY_TIME, int2str(TimeLeft, 3), FONT_TEXT_2);
6638 if (!TimeLeft && setup.time_limit)
6639 for (i=0; i<MAX_PLAYERS; i++)
6640 KillHero(&stored_player[i]);
6642 else if (level.time == 0 && !AllPlayersGone) /* level without time limit */
6643 DrawText(DX_TIME, DY_TIME, int2str(TimePlayed, 3), FONT_TEXT_2);
6648 if (options.debug) /* calculate frames per second */
6650 static unsigned long fps_counter = 0;
6651 static int fps_frames = 0;
6652 unsigned long fps_delay_ms = Counter() - fps_counter;
6656 if (fps_delay_ms >= 500) /* calculate fps every 0.5 seconds */
6658 global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
6661 fps_counter = Counter();
6664 redraw_mask |= REDRAW_FPS;
6668 if (stored_player[0].jx != stored_player[0].last_jx ||
6669 stored_player[0].jy != stored_player[0].last_jy)
6670 printf("::: %d, %d, %d, %d, %d\n",
6671 stored_player[0].MovDir,
6672 stored_player[0].MovPos,
6673 stored_player[0].GfxPos,
6674 stored_player[0].Frame,
6675 stored_player[0].StepFrame);
6682 for (i=0; i<MAX_PLAYERS; i++)
6685 MOVE_DELAY_NORMAL_SPEED / stored_player[i].move_delay_value;
6687 stored_player[i].Frame += move_frames;
6689 if (stored_player[i].MovPos != 0)
6690 stored_player[i].StepFrame += move_frames;
6695 if (local_player->show_envelope != 0 && local_player->MovPos == 0)
6697 ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
6699 local_player->show_envelope = 0;
6704 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
6706 int min_x = x, min_y = y, max_x = x, max_y = y;
6709 for (i=0; i<MAX_PLAYERS; i++)
6711 int jx = stored_player[i].jx, jy = stored_player[i].jy;
6713 if (!stored_player[i].active || &stored_player[i] == player)
6716 min_x = MIN(min_x, jx);
6717 min_y = MIN(min_y, jy);
6718 max_x = MAX(max_x, jx);
6719 max_y = MAX(max_y, jy);
6722 return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
6725 static boolean AllPlayersInVisibleScreen()
6729 for (i=0; i<MAX_PLAYERS; i++)
6731 int jx = stored_player[i].jx, jy = stored_player[i].jy;
6733 if (!stored_player[i].active)
6736 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
6743 void ScrollLevel(int dx, int dy)
6745 int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
6748 BlitBitmap(drawto_field, drawto_field,
6749 FX + TILEX * (dx == -1) - softscroll_offset,
6750 FY + TILEY * (dy == -1) - softscroll_offset,
6751 SXSIZE - TILEX * (dx!=0) + 2 * softscroll_offset,
6752 SYSIZE - TILEY * (dy!=0) + 2 * softscroll_offset,
6753 FX + TILEX * (dx == 1) - softscroll_offset,
6754 FY + TILEY * (dy == 1) - softscroll_offset);
6758 x = (dx == 1 ? BX1 : BX2);
6759 for (y=BY1; y <= BY2; y++)
6760 DrawScreenField(x, y);
6765 y = (dy == 1 ? BY1 : BY2);
6766 for (x=BX1; x <= BX2; x++)
6767 DrawScreenField(x, y);
6770 redraw_mask |= REDRAW_FIELD;
6773 static void CheckGravityMovement(struct PlayerInfo *player)
6775 if (game.gravity && !player->programmed_action)
6777 int move_dir_vertical = player->action & (MV_UP | MV_DOWN);
6778 int move_dir_horizontal = player->action & (MV_LEFT | MV_RIGHT);
6780 (player->last_move_dir & (MV_LEFT | MV_RIGHT) ?
6781 (move_dir_vertical ? move_dir_vertical : move_dir_horizontal) :
6782 (move_dir_horizontal ? move_dir_horizontal : move_dir_vertical));
6783 int jx = player->jx, jy = player->jy;
6784 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
6785 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
6786 int new_jx = jx + dx, new_jy = jy + dy;
6787 boolean field_under_player_is_free =
6788 (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
6789 boolean player_is_moving_to_valid_field =
6790 (IN_LEV_FIELD(new_jx, new_jy) &&
6791 (Feld[new_jx][new_jy] == EL_SP_BASE ||
6792 Feld[new_jx][new_jy] == EL_SAND));
6793 /* !!! extend EL_SAND to anything diggable !!! */
6795 if (field_under_player_is_free &&
6796 !player_is_moving_to_valid_field &&
6797 !IS_WALKABLE_INSIDE(Feld[jx][jy]))
6798 player->programmed_action = MV_DOWN;
6804 -----------------------------------------------------------------------------
6805 dx, dy: direction (non-diagonal) to try to move the player to
6806 real_dx, real_dy: direction as read from input device (can be diagonal)
6809 boolean MovePlayerOneStep(struct PlayerInfo *player,
6810 int dx, int dy, int real_dx, int real_dy)
6813 static int change_sides[4][2] =
6815 /* enter side leave side */
6816 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* moving left */
6817 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* moving right */
6818 { CH_SIDE_BOTTOM, CH_SIDE_TOP }, /* moving up */
6819 { CH_SIDE_TOP, CH_SIDE_BOTTOM } /* moving down */
6821 int move_direction = (dx == -1 ? MV_LEFT :
6822 dx == +1 ? MV_RIGHT :
6824 dy == +1 ? MV_DOWN : MV_NO_MOVING);
6825 int enter_side = change_sides[MV_DIR_BIT(move_direction)][0];
6826 int leave_side = change_sides[MV_DIR_BIT(move_direction)][1];
6828 int jx = player->jx, jy = player->jy;
6829 int new_jx = jx + dx, new_jy = jy + dy;
6833 if (!player->active || (!dx && !dy))
6834 return MF_NO_ACTION;
6836 player->MovDir = (dx < 0 ? MV_LEFT :
6839 dy > 0 ? MV_DOWN : MV_NO_MOVING);
6841 if (!IN_LEV_FIELD(new_jx, new_jy))
6842 return MF_NO_ACTION;
6844 if (!options.network && !AllPlayersInSight(player, new_jx, new_jy))
6845 return MF_NO_ACTION;
6848 element = MovingOrBlocked2Element(new_jx, new_jy);
6850 element = MovingOrBlocked2ElementIfNotLeaving(new_jx, new_jy);
6853 if (DONT_RUN_INTO(element))
6855 if (element == EL_ACID && dx == 0 && dy == 1)
6858 Feld[jx][jy] = EL_PLAYER_1;
6859 InitMovingField(jx, jy, MV_DOWN);
6860 Store[jx][jy] = EL_ACID;
6861 ContinueMoving(jx, jy);
6865 TestIfHeroRunsIntoBadThing(jx, jy, player->MovDir);
6870 can_move = DigField(player, new_jx, new_jy, real_dx, real_dy, DF_DIG);
6871 if (can_move != MF_MOVING)
6874 /* check if DigField() has caused relocation of the player */
6875 if (player->jx != jx || player->jy != jy)
6876 return MF_NO_ACTION;
6878 StorePlayer[jx][jy] = 0;
6879 player->last_jx = jx;
6880 player->last_jy = jy;
6881 player->jx = new_jx;
6882 player->jy = new_jy;
6883 StorePlayer[new_jx][new_jy] = player->element_nr;
6886 (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
6888 ScrollPlayer(player, SCROLL_INIT);
6891 if (IS_CUSTOM_ELEMENT(Feld[jx][jy]))
6893 CheckTriggeredElementSideChange(jx, jy, Feld[jx][jy], leave_side,
6894 CE_OTHER_GETS_LEFT);
6895 CheckElementSideChange(jx, jy, Feld[jx][jy], leave_side,
6896 CE_LEFT_BY_PLAYER, -1);
6899 if (IS_CUSTOM_ELEMENT(Feld[new_jx][new_jy]))
6901 CheckTriggeredElementSideChange(new_jx, new_jy, Feld[new_jx][new_jy],
6902 enter_side, CE_OTHER_GETS_ENTERED);
6903 CheckElementSideChange(new_jx, new_jy, Feld[new_jx][new_jy], enter_side,
6904 CE_ENTERED_BY_PLAYER, -1);
6911 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
6913 int jx = player->jx, jy = player->jy;
6914 int old_jx = jx, old_jy = jy;
6915 int moved = MF_NO_ACTION;
6917 if (!player->active || (!dx && !dy))
6921 if (!FrameReached(&player->move_delay, player->move_delay_value) &&
6925 if (!FrameReached(&player->move_delay, player->move_delay_value) &&
6926 !(tape.playing && tape.file_version < FILE_VERSION_2_0))
6930 /* remove the last programmed player action */
6931 player->programmed_action = 0;
6935 /* should only happen if pre-1.2 tape recordings are played */
6936 /* this is only for backward compatibility */
6938 int original_move_delay_value = player->move_delay_value;
6941 printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%ld]\n",
6945 /* scroll remaining steps with finest movement resolution */
6946 player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
6948 while (player->MovPos)
6950 ScrollPlayer(player, SCROLL_GO_ON);
6951 ScrollScreen(NULL, SCROLL_GO_ON);
6957 player->move_delay_value = original_move_delay_value;
6960 if (player->last_move_dir & (MV_LEFT | MV_RIGHT))
6962 if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
6963 moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
6967 if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
6968 moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
6974 if (moved & MF_MOVING && !ScreenMovPos &&
6975 (player == local_player || !options.network))
6977 int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
6978 int offset = (setup.scroll_delay ? 3 : 0);
6980 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
6982 /* actual player has left the screen -- scroll in that direction */
6983 if (jx != old_jx) /* player has moved horizontally */
6984 scroll_x += (jx - old_jx);
6985 else /* player has moved vertically */
6986 scroll_y += (jy - old_jy);
6990 if (jx != old_jx) /* player has moved horizontally */
6992 if ((player->MovDir == MV_LEFT && scroll_x > jx - MIDPOSX + offset) ||
6993 (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
6994 scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
6996 /* don't scroll over playfield boundaries */
6997 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
6998 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
7000 /* don't scroll more than one field at a time */
7001 scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
7003 /* don't scroll against the player's moving direction */
7004 if ((player->MovDir == MV_LEFT && scroll_x > old_scroll_x) ||
7005 (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
7006 scroll_x = old_scroll_x;
7008 else /* player has moved vertically */
7010 if ((player->MovDir == MV_UP && scroll_y > jy - MIDPOSY + offset) ||
7011 (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
7012 scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
7014 /* don't scroll over playfield boundaries */
7015 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
7016 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
7018 /* don't scroll more than one field at a time */
7019 scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
7021 /* don't scroll against the player's moving direction */
7022 if ((player->MovDir == MV_UP && scroll_y > old_scroll_y) ||
7023 (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
7024 scroll_y = old_scroll_y;
7028 if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
7030 if (!options.network && !AllPlayersInVisibleScreen())
7032 scroll_x = old_scroll_x;
7033 scroll_y = old_scroll_y;
7037 ScrollScreen(player, SCROLL_INIT);
7038 ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
7045 InitPlayerGfxAnimation(player, ACTION_DEFAULT);
7047 if (!(moved & MF_MOVING) && !player->is_pushing)
7052 player->StepFrame = 0;
7054 if (moved & MF_MOVING)
7056 if (old_jx != jx && old_jy == jy)
7057 player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
7058 else if (old_jx == jx && old_jy != jy)
7059 player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
7061 DrawLevelField(jx, jy); /* for "crumbled sand" */
7063 player->last_move_dir = player->MovDir;
7064 player->is_moving = TRUE;
7066 player->is_snapping = FALSE;
7070 player->is_switching = FALSE;
7076 static int change_sides[4][2] =
7078 /* enter side leave side */
7079 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* moving left */
7080 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* moving right */
7081 { CH_SIDE_BOTTOM, CH_SIDE_TOP }, /* moving up */
7082 { CH_SIDE_TOP, CH_SIDE_BOTTOM } /* moving down */
7084 int move_direction = player->MovDir;
7085 int enter_side = change_sides[MV_DIR_BIT(move_direction)][0];
7086 int leave_side = change_sides[MV_DIR_BIT(move_direction)][1];
7089 if (IS_CUSTOM_ELEMENT(Feld[old_jx][old_jy]))
7091 CheckTriggeredElementSideChange(old_jx, old_jy, Feld[old_jx][old_jy],
7092 leave_side, CE_OTHER_GETS_LEFT);
7093 CheckElementSideChange(old_jx, old_jy, Feld[old_jx][old_jy],
7094 leave_side, CE_LEFT_BY_PLAYER, -1);
7097 if (IS_CUSTOM_ELEMENT(Feld[jx][jy]))
7099 CheckTriggeredElementSideChange(jx, jy, Feld[jx][jy],
7100 enter_side, CE_OTHER_GETS_ENTERED);
7101 CheckElementSideChange(jx, jy, Feld[jx][jy],
7102 enter_side, CE_ENTERED_BY_PLAYER, -1);
7113 CheckGravityMovement(player);
7116 player->last_move_dir = MV_NO_MOVING;
7118 player->is_moving = FALSE;
7121 if (game.engine_version < VERSION_IDENT(3,0,7,0))
7123 TestIfHeroTouchesBadThing(jx, jy);
7124 TestIfPlayerTouchesCustomElement(jx, jy);
7127 if (!player->active)
7133 void ScrollPlayer(struct PlayerInfo *player, int mode)
7135 int jx = player->jx, jy = player->jy;
7136 int last_jx = player->last_jx, last_jy = player->last_jy;
7137 int move_stepsize = TILEX / player->move_delay_value;
7139 if (!player->active || !player->MovPos)
7142 if (mode == SCROLL_INIT)
7144 player->actual_frame_counter = FrameCounter;
7145 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
7147 if (Feld[last_jx][last_jy] == EL_EMPTY)
7148 Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
7155 else if (!FrameReached(&player->actual_frame_counter, 1))
7158 player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
7159 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
7161 if (Feld[last_jx][last_jy] == EL_PLAYER_IS_LEAVING)
7162 Feld[last_jx][last_jy] = EL_EMPTY;
7164 /* before DrawPlayer() to draw correct player graphic for this case */
7165 if (player->MovPos == 0)
7166 CheckGravityMovement(player);
7169 DrawPlayer(player); /* needed here only to cleanup last field */
7172 if (player->MovPos == 0) /* player reached destination field */
7174 if (IS_PASSABLE(Feld[last_jx][last_jy]))
7176 /* continue with normal speed after quickly moving through gate */
7177 HALVE_PLAYER_SPEED(player);
7179 /* be able to make the next move without delay */
7180 player->move_delay = 0;
7183 player->last_jx = jx;
7184 player->last_jy = jy;
7186 if (Feld[jx][jy] == EL_EXIT_OPEN ||
7187 Feld[jx][jy] == EL_SP_EXIT_OPEN ||
7188 Feld[jx][jy] == EL_SP_EXIT_OPENING) /* <-- special case */
7190 DrawPlayer(player); /* needed here only to cleanup last field */
7193 if (local_player->friends_still_needed == 0 ||
7194 IS_SP_ELEMENT(Feld[jx][jy]))
7195 player->LevelSolved = player->GameOver = TRUE;
7198 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
7200 TestIfHeroTouchesBadThing(jx, jy);
7201 TestIfPlayerTouchesCustomElement(jx, jy);
7203 TestIfElementTouchesCustomElement(jx, jy); /* for empty space */
7206 if (!player->active)
7210 if (tape.single_step && tape.recording && !tape.pausing &&
7211 !player->programmed_action)
7212 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
7216 void ScrollScreen(struct PlayerInfo *player, int mode)
7218 static unsigned long screen_frame_counter = 0;
7220 if (mode == SCROLL_INIT)
7222 /* set scrolling step size according to actual player's moving speed */
7223 ScrollStepSize = TILEX / player->move_delay_value;
7225 screen_frame_counter = FrameCounter;
7226 ScreenMovDir = player->MovDir;
7227 ScreenMovPos = player->MovPos;
7228 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
7231 else if (!FrameReached(&screen_frame_counter, 1))
7236 ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
7237 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
7238 redraw_mask |= REDRAW_FIELD;
7241 ScreenMovDir = MV_NO_MOVING;
7244 void TestIfPlayerTouchesCustomElement(int x, int y)
7246 static int xy[4][2] =
7253 static int change_sides[4][2] =
7255 /* center side border side */
7256 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
7257 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
7258 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
7259 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
7261 static int touch_dir[4] =
7268 int center_element = Feld[x][y]; /* should always be non-moving! */
7273 int xx = x + xy[i][0];
7274 int yy = y + xy[i][1];
7275 int center_side = change_sides[i][0];
7276 int border_side = change_sides[i][1];
7279 if (!IN_LEV_FIELD(xx, yy))
7282 if (IS_PLAYER(x, y))
7284 if (game.engine_version < VERSION_IDENT(3,0,7,0))
7285 border_element = Feld[xx][yy]; /* may be moving! */
7286 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
7287 border_element = Feld[xx][yy];
7288 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
7289 border_element = MovingOrBlocked2Element(xx, yy);
7291 continue; /* center and border element do not touch */
7293 CheckTriggeredElementSideChange(xx, yy, border_element, border_side,
7294 CE_OTHER_GETS_TOUCHED);
7295 CheckElementSideChange(xx, yy, border_element, border_side,
7296 CE_TOUCHED_BY_PLAYER, -1);
7298 else if (IS_PLAYER(xx, yy))
7300 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
7302 struct PlayerInfo *player = PLAYERINFO(xx, yy);
7304 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
7305 continue; /* center and border element do not touch */
7308 CheckTriggeredElementSideChange(x, y, center_element, center_side,
7309 CE_OTHER_GETS_TOUCHED);
7310 CheckElementSideChange(x, y, center_element, center_side,
7311 CE_TOUCHED_BY_PLAYER, -1);
7318 void TestIfElementTouchesCustomElement(int x, int y)
7320 static int xy[4][2] =
7327 static int change_sides[4][2] =
7329 /* center side border side */
7330 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
7331 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
7332 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
7333 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
7335 static int touch_dir[4] =
7342 boolean change_center_element = FALSE;
7343 int center_element_change_page = 0;
7344 int center_element = Feld[x][y]; /* should always be non-moving! */
7349 int xx = x + xy[i][0];
7350 int yy = y + xy[i][1];
7351 int center_side = change_sides[i][0];
7352 int border_side = change_sides[i][1];
7355 if (!IN_LEV_FIELD(xx, yy))
7358 if (game.engine_version < VERSION_IDENT(3,0,7,0))
7359 border_element = Feld[xx][yy]; /* may be moving! */
7360 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
7361 border_element = Feld[xx][yy];
7362 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
7363 border_element = MovingOrBlocked2Element(xx, yy);
7365 continue; /* center and border element do not touch */
7367 /* check for change of center element (but change it only once) */
7368 if (IS_CUSTOM_ELEMENT(center_element) &&
7369 HAS_ANY_CHANGE_EVENT(center_element, CE_OTHER_IS_TOUCHING) &&
7370 !change_center_element)
7372 for (j=0; j < element_info[center_element].num_change_pages; j++)
7374 struct ElementChangeInfo *change =
7375 &element_info[center_element].change_page[j];
7377 if (change->can_change &&
7378 change->events & CH_EVENT_BIT(CE_OTHER_IS_TOUCHING) &&
7379 change->sides & border_side &&
7380 change->trigger_element == border_element)
7382 change_center_element = TRUE;
7383 center_element_change_page = j;
7390 /* check for change of border element */
7391 if (IS_CUSTOM_ELEMENT(border_element) &&
7392 HAS_ANY_CHANGE_EVENT(border_element, CE_OTHER_IS_TOUCHING))
7394 for (j=0; j < element_info[border_element].num_change_pages; j++)
7396 struct ElementChangeInfo *change =
7397 &element_info[border_element].change_page[j];
7399 if (change->can_change &&
7400 change->events & CH_EVENT_BIT(CE_OTHER_IS_TOUCHING) &&
7401 change->sides & center_side &&
7402 change->trigger_element == center_element)
7404 CheckElementSideChange(xx, yy, border_element, CH_SIDE_ANY,
7405 CE_OTHER_IS_TOUCHING, j);
7412 if (change_center_element)
7413 CheckElementSideChange(x, y, center_element, CH_SIDE_ANY,
7414 CE_OTHER_IS_TOUCHING, center_element_change_page);
7417 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
7419 int i, kill_x = -1, kill_y = -1;
7420 static int test_xy[4][2] =
7427 static int test_dir[4] =
7437 int test_x, test_y, test_move_dir, test_element;
7439 test_x = good_x + test_xy[i][0];
7440 test_y = good_y + test_xy[i][1];
7441 if (!IN_LEV_FIELD(test_x, test_y))
7445 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NO_MOVING);
7448 test_element = Feld[test_x][test_y];
7450 test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
7453 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
7454 2nd case: DONT_TOUCH style bad thing does not move away from good thing
7456 if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
7457 (DONT_TOUCH(test_element) && test_move_dir != test_dir[i]))
7465 if (kill_x != -1 || kill_y != -1)
7467 if (IS_PLAYER(good_x, good_y))
7469 struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
7471 if (player->shield_deadly_time_left > 0)
7472 Bang(kill_x, kill_y);
7473 else if (!PLAYER_PROTECTED(good_x, good_y))
7477 Bang(good_x, good_y);
7481 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
7483 int i, kill_x = -1, kill_y = -1;
7484 int bad_element = Feld[bad_x][bad_y];
7485 static int test_xy[4][2] =
7492 static int touch_dir[4] =
7499 static int test_dir[4] =
7507 if (bad_element == EL_EXPLOSION) /* skip just exploding bad things */
7512 int test_x, test_y, test_move_dir, test_element;
7514 test_x = bad_x + test_xy[i][0];
7515 test_y = bad_y + test_xy[i][1];
7516 if (!IN_LEV_FIELD(test_x, test_y))
7520 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NO_MOVING);
7522 test_element = Feld[test_x][test_y];
7524 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
7525 2nd case: DONT_TOUCH style bad thing does not move away from good thing
7527 if ((DONT_RUN_INTO(bad_element) && bad_move_dir == test_dir[i]) ||
7528 (DONT_TOUCH(bad_element) && test_move_dir != test_dir[i]))
7530 /* good thing is player or penguin that does not move away */
7531 if (IS_PLAYER(test_x, test_y))
7533 struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
7535 if (bad_element == EL_ROBOT && player->is_moving)
7536 continue; /* robot does not kill player if he is moving */
7538 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
7540 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
7541 continue; /* center and border element do not touch */
7548 else if (test_element == EL_PENGUIN)
7557 if (kill_x != -1 || kill_y != -1)
7559 if (IS_PLAYER(kill_x, kill_y))
7561 struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
7563 if (player->shield_deadly_time_left > 0)
7565 else if (!PLAYER_PROTECTED(kill_x, kill_y))
7569 Bang(kill_x, kill_y);
7573 void TestIfHeroTouchesBadThing(int x, int y)
7575 TestIfGoodThingHitsBadThing(x, y, MV_NO_MOVING);
7578 void TestIfHeroRunsIntoBadThing(int x, int y, int move_dir)
7580 TestIfGoodThingHitsBadThing(x, y, move_dir);
7583 void TestIfBadThingTouchesHero(int x, int y)
7585 TestIfBadThingHitsGoodThing(x, y, MV_NO_MOVING);
7588 void TestIfBadThingRunsIntoHero(int x, int y, int move_dir)
7590 TestIfBadThingHitsGoodThing(x, y, move_dir);
7593 void TestIfFriendTouchesBadThing(int x, int y)
7595 TestIfGoodThingHitsBadThing(x, y, MV_NO_MOVING);
7598 void TestIfBadThingTouchesFriend(int x, int y)
7600 TestIfBadThingHitsGoodThing(x, y, MV_NO_MOVING);
7603 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
7605 int i, kill_x = bad_x, kill_y = bad_y;
7606 static int xy[4][2] =
7618 x = bad_x + xy[i][0];
7619 y = bad_y + xy[i][1];
7620 if (!IN_LEV_FIELD(x, y))
7623 element = Feld[x][y];
7624 if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
7625 element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
7633 if (kill_x != bad_x || kill_y != bad_y)
7637 void KillHero(struct PlayerInfo *player)
7639 int jx = player->jx, jy = player->jy;
7641 if (!player->active)
7644 /* remove accessible field at the player's position */
7645 Feld[jx][jy] = EL_EMPTY;
7647 /* deactivate shield (else Bang()/Explode() would not work right) */
7648 player->shield_normal_time_left = 0;
7649 player->shield_deadly_time_left = 0;
7655 static void KillHeroUnlessProtected(int x, int y)
7657 if (!PLAYER_PROTECTED(x, y))
7658 KillHero(PLAYERINFO(x, y));
7661 void BuryHero(struct PlayerInfo *player)
7663 int jx = player->jx, jy = player->jy;
7665 if (!player->active)
7669 PlayLevelSoundElementAction(jx, jy, player->element_nr, ACTION_DYING);
7671 PlayLevelSound(jx, jy, SND_CLASS_PLAYER_DYING);
7673 PlayLevelSound(jx, jy, SND_GAME_LOSING);
7675 player->GameOver = TRUE;
7679 void RemoveHero(struct PlayerInfo *player)
7681 int jx = player->jx, jy = player->jy;
7682 int i, found = FALSE;
7684 player->present = FALSE;
7685 player->active = FALSE;
7687 if (!ExplodeField[jx][jy])
7688 StorePlayer[jx][jy] = 0;
7690 for (i=0; i<MAX_PLAYERS; i++)
7691 if (stored_player[i].active)
7695 AllPlayersGone = TRUE;
7702 =============================================================================
7703 checkDiagonalPushing()
7704 -----------------------------------------------------------------------------
7705 check if diagonal input device direction results in pushing of object
7706 (by checking if the alternative direction is walkable, diggable, ...)
7707 =============================================================================
7710 static boolean checkDiagonalPushing(struct PlayerInfo *player,
7711 int x, int y, int real_dx, int real_dy)
7713 int jx, jy, dx, dy, xx, yy;
7715 if (real_dx == 0 || real_dy == 0) /* no diagonal direction => push */
7718 /* diagonal direction: check alternative direction */
7723 xx = jx + (dx == 0 ? real_dx : 0);
7724 yy = jy + (dy == 0 ? real_dy : 0);
7726 return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
7730 =============================================================================
7732 -----------------------------------------------------------------------------
7733 x, y: field next to player (non-diagonal) to try to dig to
7734 real_dx, real_dy: direction as read from input device (can be diagonal)
7735 =============================================================================
7738 int DigField(struct PlayerInfo *player,
7739 int x, int y, int real_dx, int real_dy, int mode)
7741 static int change_sides[4] =
7743 CH_SIDE_RIGHT, /* moving left */
7744 CH_SIDE_LEFT, /* moving right */
7745 CH_SIDE_BOTTOM, /* moving up */
7746 CH_SIDE_TOP, /* moving down */
7748 boolean use_spring_bug = (game.engine_version < VERSION_IDENT(2,2,0,0));
7749 int jx = player->jx, jy = player->jy;
7750 int dx = x - jx, dy = y - jy;
7751 int nextx = x + dx, nexty = y + dy;
7752 int move_direction = (dx == -1 ? MV_LEFT :
7753 dx == +1 ? MV_RIGHT :
7755 dy == +1 ? MV_DOWN : MV_NO_MOVING);
7756 int dig_side = change_sides[MV_DIR_BIT(move_direction)];
7759 if (player->MovPos == 0)
7761 player->is_digging = FALSE;
7762 player->is_collecting = FALSE;
7765 if (player->MovPos == 0) /* last pushing move finished */
7766 player->is_pushing = FALSE;
7768 if (mode == DF_NO_PUSH) /* player just stopped pushing */
7770 player->is_switching = FALSE;
7771 player->push_delay = 0;
7773 return MF_NO_ACTION;
7776 if (IS_MOVING(x, y) || IS_PLAYER(x, y))
7777 return MF_NO_ACTION;
7780 if (IS_TUBE(Feld[jx][jy]) || IS_TUBE(Back[jx][jy]))
7782 if (IS_TUBE(Feld[jx][jy]) ||
7783 (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0)))
7787 int tube_element = (IS_TUBE(Feld[jx][jy]) ? Feld[jx][jy] : Back[jx][jy]);
7788 int tube_leave_directions[][2] =
7790 { EL_TUBE_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
7791 { EL_TUBE_VERTICAL, MV_UP | MV_DOWN },
7792 { EL_TUBE_HORIZONTAL, MV_LEFT | MV_RIGHT },
7793 { EL_TUBE_VERTICAL_LEFT, MV_LEFT | MV_UP | MV_DOWN },
7794 { EL_TUBE_VERTICAL_RIGHT, MV_RIGHT | MV_UP | MV_DOWN },
7795 { EL_TUBE_HORIZONTAL_UP, MV_LEFT | MV_RIGHT | MV_UP },
7796 { EL_TUBE_HORIZONTAL_DOWN, MV_LEFT | MV_RIGHT | MV_DOWN },
7797 { EL_TUBE_LEFT_UP, MV_LEFT | MV_UP },
7798 { EL_TUBE_LEFT_DOWN, MV_LEFT | MV_DOWN },
7799 { EL_TUBE_RIGHT_UP, MV_RIGHT | MV_UP },
7800 { EL_TUBE_RIGHT_DOWN, MV_RIGHT | MV_DOWN },
7801 { -1, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN }
7804 while (tube_leave_directions[i][0] != tube_element)
7807 if (tube_leave_directions[i][0] == -1) /* should not happen */
7811 if (!(tube_leave_directions[i][1] & move_direction))
7812 return MF_NO_ACTION; /* tube has no opening in this direction */
7815 element = Feld[x][y];
7817 if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
7818 game.engine_version >= VERSION_IDENT(2,2,0,0))
7819 return MF_NO_ACTION;
7823 case EL_SP_PORT_LEFT:
7824 case EL_SP_PORT_RIGHT:
7826 case EL_SP_PORT_DOWN:
7827 case EL_SP_PORT_HORIZONTAL:
7828 case EL_SP_PORT_VERTICAL:
7829 case EL_SP_PORT_ANY:
7830 case EL_SP_GRAVITY_PORT_LEFT:
7831 case EL_SP_GRAVITY_PORT_RIGHT:
7832 case EL_SP_GRAVITY_PORT_UP:
7833 case EL_SP_GRAVITY_PORT_DOWN:
7835 element != EL_SP_PORT_LEFT &&
7836 element != EL_SP_GRAVITY_PORT_LEFT &&
7837 element != EL_SP_PORT_HORIZONTAL &&
7838 element != EL_SP_PORT_ANY) ||
7840 element != EL_SP_PORT_RIGHT &&
7841 element != EL_SP_GRAVITY_PORT_RIGHT &&
7842 element != EL_SP_PORT_HORIZONTAL &&
7843 element != EL_SP_PORT_ANY) ||
7845 element != EL_SP_PORT_UP &&
7846 element != EL_SP_GRAVITY_PORT_UP &&
7847 element != EL_SP_PORT_VERTICAL &&
7848 element != EL_SP_PORT_ANY) ||
7850 element != EL_SP_PORT_DOWN &&
7851 element != EL_SP_GRAVITY_PORT_DOWN &&
7852 element != EL_SP_PORT_VERTICAL &&
7853 element != EL_SP_PORT_ANY) ||
7854 !IN_LEV_FIELD(nextx, nexty) ||
7855 !IS_FREE(nextx, nexty))
7856 return MF_NO_ACTION;
7858 if (element == EL_SP_GRAVITY_PORT_LEFT ||
7859 element == EL_SP_GRAVITY_PORT_RIGHT ||
7860 element == EL_SP_GRAVITY_PORT_UP ||
7861 element == EL_SP_GRAVITY_PORT_DOWN)
7862 game.gravity = !game.gravity;
7864 /* automatically move to the next field with double speed */
7865 player->programmed_action = move_direction;
7866 DOUBLE_PLAYER_SPEED(player);
7868 PlayLevelSound(x, y, SND_CLASS_SP_PORT_PASSING);
7872 case EL_TUBE_VERTICAL:
7873 case EL_TUBE_HORIZONTAL:
7874 case EL_TUBE_VERTICAL_LEFT:
7875 case EL_TUBE_VERTICAL_RIGHT:
7876 case EL_TUBE_HORIZONTAL_UP:
7877 case EL_TUBE_HORIZONTAL_DOWN:
7878 case EL_TUBE_LEFT_UP:
7879 case EL_TUBE_LEFT_DOWN:
7880 case EL_TUBE_RIGHT_UP:
7881 case EL_TUBE_RIGHT_DOWN:
7884 int tube_enter_directions[][2] =
7886 { EL_TUBE_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
7887 { EL_TUBE_VERTICAL, MV_UP | MV_DOWN },
7888 { EL_TUBE_HORIZONTAL, MV_LEFT | MV_RIGHT },
7889 { EL_TUBE_VERTICAL_LEFT, MV_RIGHT | MV_UP | MV_DOWN },
7890 { EL_TUBE_VERTICAL_RIGHT, MV_LEFT | MV_UP | MV_DOWN },
7891 { EL_TUBE_HORIZONTAL_UP, MV_LEFT | MV_RIGHT | MV_DOWN },
7892 { EL_TUBE_HORIZONTAL_DOWN, MV_LEFT | MV_RIGHT | MV_UP },
7893 { EL_TUBE_LEFT_UP, MV_RIGHT | MV_DOWN },
7894 { EL_TUBE_LEFT_DOWN, MV_RIGHT | MV_UP },
7895 { EL_TUBE_RIGHT_UP, MV_LEFT | MV_DOWN },
7896 { EL_TUBE_RIGHT_DOWN, MV_LEFT | MV_UP },
7897 { -1, MV_NO_MOVING }
7900 while (tube_enter_directions[i][0] != element)
7903 if (tube_enter_directions[i][0] == -1) /* should not happen */
7907 if (!(tube_enter_directions[i][1] & move_direction))
7908 return MF_NO_ACTION; /* tube has no opening in this direction */
7910 PlayLevelSound(x, y, SND_CLASS_TUBE_WALKING);
7916 if (IS_WALKABLE(element))
7918 int sound_action = ACTION_WALKING;
7920 if (element >= EL_GATE_1 && element <= EL_GATE_4)
7922 if (!player->key[element - EL_GATE_1])
7923 return MF_NO_ACTION;
7925 else if (element >= EL_GATE_1_GRAY && element <= EL_GATE_4_GRAY)
7927 if (!player->key[element - EL_GATE_1_GRAY])
7928 return MF_NO_ACTION;
7930 else if (element == EL_EXIT_OPEN ||
7931 element == EL_SP_EXIT_OPEN ||
7932 element == EL_SP_EXIT_OPENING)
7934 sound_action = ACTION_PASSING; /* player is passing exit */
7936 else if (element == EL_EMPTY)
7938 sound_action = ACTION_MOVING; /* nothing to walk on */
7941 /* play sound from background or player, whatever is available */
7942 if (element_info[element].sound[sound_action] != SND_UNDEFINED)
7943 PlayLevelSoundElementAction(x, y, element, sound_action);
7945 PlayLevelSoundElementAction(x, y, player->element_nr, sound_action);
7949 else if (IS_PASSABLE(element))
7951 if (!IN_LEV_FIELD(nextx, nexty) || !IS_FREE(nextx, nexty))
7952 return MF_NO_ACTION;
7955 if (CAN_MOVE(element)) /* only fixed elements can be passed! */
7956 return MF_NO_ACTION;
7959 if (element >= EL_EM_GATE_1 && element <= EL_EM_GATE_4)
7961 if (!player->key[element - EL_EM_GATE_1])
7962 return MF_NO_ACTION;
7964 else if (element >= EL_EM_GATE_1_GRAY && element <= EL_EM_GATE_4_GRAY)
7966 if (!player->key[element - EL_EM_GATE_1_GRAY])
7967 return MF_NO_ACTION;
7970 /* automatically move to the next field with double speed */
7971 player->programmed_action = move_direction;
7972 DOUBLE_PLAYER_SPEED(player);
7974 PlayLevelSoundAction(x, y, ACTION_PASSING);
7978 else if (IS_DIGGABLE(element))
7982 if (mode != DF_SNAP)
7985 GfxElement[x][y] = GFX_ELEMENT(element);
7988 (GFX_CRUMBLED(element) ? EL_SAND : GFX_ELEMENT(element));
7990 player->is_digging = TRUE;
7993 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
7995 CheckTriggeredElementChange(x, y, element, CE_OTHER_GETS_DIGGED);
7998 if (mode == DF_SNAP)
7999 TestIfElementTouchesCustomElement(x, y); /* for empty space */
8004 else if (IS_COLLECTIBLE(element))
8008 if (mode != DF_SNAP)
8010 GfxElement[x][y] = element;
8011 player->is_collecting = TRUE;
8014 if (element == EL_SPEED_PILL)
8015 player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
8016 else if (element == EL_EXTRA_TIME && level.time > 0)
8019 DrawText(DX_TIME, DY_TIME, int2str(TimeLeft, 3), FONT_TEXT_2);
8021 else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
8023 player->shield_normal_time_left += 10;
8024 if (element == EL_SHIELD_DEADLY)
8025 player->shield_deadly_time_left += 10;
8027 else if (element == EL_DYNAMITE || element == EL_SP_DISK_RED)
8029 if (player->inventory_size < MAX_INVENTORY_SIZE)
8030 player->inventory_element[player->inventory_size++] = element;
8032 DrawText(DX_DYNAMITE, DY_DYNAMITE,
8033 int2str(local_player->inventory_size, 3), FONT_TEXT_2);
8035 else if (element == EL_DYNABOMB_INCREASE_NUMBER)
8037 player->dynabomb_count++;
8038 player->dynabombs_left++;
8040 else if (element == EL_DYNABOMB_INCREASE_SIZE)
8042 player->dynabomb_size++;
8044 else if (element == EL_DYNABOMB_INCREASE_POWER)
8046 player->dynabomb_xl = TRUE;
8048 else if ((element >= EL_KEY_1 && element <= EL_KEY_4) ||
8049 (element >= EL_EM_KEY_1 && element <= EL_EM_KEY_4))
8051 int key_nr = (element >= EL_KEY_1 && element <= EL_KEY_4 ?
8052 element - EL_KEY_1 : element - EL_EM_KEY_1);
8054 player->key[key_nr] = TRUE;
8056 DrawMiniGraphicExt(drawto, DX_KEYS + key_nr * MINI_TILEX, DY_KEYS,
8057 el2edimg(EL_KEY_1 + key_nr));
8058 redraw_mask |= REDRAW_DOOR_1;
8060 else if (IS_ENVELOPE(element))
8063 player->show_envelope = element;
8065 ShowEnvelope(element - EL_ENVELOPE_1);
8068 else if (IS_DROPPABLE(element)) /* can be collected and dropped */
8072 for (i=0; i < element_info[element].collect_count; i++)
8073 if (player->inventory_size < MAX_INVENTORY_SIZE)
8074 player->inventory_element[player->inventory_size++] = element;
8076 DrawText(DX_DYNAMITE, DY_DYNAMITE,
8077 int2str(local_player->inventory_size, 3), FONT_TEXT_2);
8079 else if (element_info[element].collect_count > 0)
8081 local_player->gems_still_needed -=
8082 element_info[element].collect_count;
8083 if (local_player->gems_still_needed < 0)
8084 local_player->gems_still_needed = 0;
8086 DrawText(DX_EMERALDS, DY_EMERALDS,
8087 int2str(local_player->gems_still_needed, 3), FONT_TEXT_2);
8090 RaiseScoreElement(element);
8091 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
8093 CheckTriggeredElementChange(x, y, element, CE_OTHER_GETS_COLLECTED);
8096 if (mode == DF_SNAP)
8097 TestIfElementTouchesCustomElement(x, y); /* for empty space */
8102 else if (IS_PUSHABLE(element))
8104 if (mode == DF_SNAP && element != EL_BD_ROCK)
8105 return MF_NO_ACTION;
8107 if (CAN_FALL(element) && dy)
8108 return MF_NO_ACTION;
8110 if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
8111 !(element == EL_SPRING && use_spring_bug))
8112 return MF_NO_ACTION;
8115 if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
8116 ((move_direction & MV_VERTICAL &&
8117 ((element_info[element].move_pattern & MV_LEFT &&
8118 IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
8119 (element_info[element].move_pattern & MV_RIGHT &&
8120 IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
8121 (move_direction & MV_HORIZONTAL &&
8122 ((element_info[element].move_pattern & MV_UP &&
8123 IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
8124 (element_info[element].move_pattern & MV_DOWN &&
8125 IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
8126 return MF_NO_ACTION;
8130 /* do not push elements already moving away faster than player */
8131 if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
8132 ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
8133 return MF_NO_ACTION;
8135 if (element == EL_SPRING && MovDir[x][y] != MV_NO_MOVING)
8136 return MF_NO_ACTION;
8140 if (game.engine_version >= VERSION_IDENT(3,0,7,1))
8142 if (player->push_delay_value == -1)
8143 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
8145 else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
8147 if (!player->is_pushing)
8148 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
8152 if (game.engine_version >= VERSION_IDENT(2,2,0,7) &&
8153 (game.engine_version < VERSION_IDENT(3,0,7,1) ||
8154 !player_is_pushing))
8155 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
8158 if (!player->is_pushing &&
8159 game.engine_version >= VERSION_IDENT(2,2,0,7))
8160 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
8164 printf("::: push delay: %ld [%d, %d] [%d]\n",
8165 player->push_delay_value, FrameCounter, game.engine_version,
8166 player->is_pushing);
8169 player->is_pushing = TRUE;
8171 if (!(IN_LEV_FIELD(nextx, nexty) &&
8172 (IS_FREE(nextx, nexty) ||
8173 (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY &&
8174 IS_SB_ELEMENT(element)))))
8175 return MF_NO_ACTION;
8177 if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
8178 return MF_NO_ACTION;
8180 if (player->push_delay == 0) /* new pushing; restart delay */
8181 player->push_delay = FrameCounter;
8183 if (!FrameReached(&player->push_delay, player->push_delay_value) &&
8184 !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
8185 element != EL_SPRING && element != EL_BALLOON)
8187 /* make sure that there is no move delay before next try to push */
8188 if (game.engine_version >= VERSION_IDENT(3,0,7,1))
8189 player->move_delay = INITIAL_MOVE_DELAY_OFF;
8191 return MF_NO_ACTION;
8195 printf("::: NOW PUSHING... [%d]\n", FrameCounter);
8198 if (IS_SB_ELEMENT(element))
8200 if (element == EL_SOKOBAN_FIELD_FULL)
8202 Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
8203 local_player->sokobanfields_still_needed++;
8206 if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
8208 Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
8209 local_player->sokobanfields_still_needed--;
8212 Feld[x][y] = EL_SOKOBAN_OBJECT;
8214 if (Back[x][y] == Back[nextx][nexty])
8215 PlayLevelSoundAction(x, y, ACTION_PUSHING);
8216 else if (Back[x][y] != 0)
8217 PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
8220 PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
8223 if (local_player->sokobanfields_still_needed == 0 &&
8224 game.emulation == EMU_SOKOBAN)
8226 player->LevelSolved = player->GameOver = TRUE;
8227 PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
8231 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
8233 InitMovingField(x, y, move_direction);
8234 GfxAction[x][y] = ACTION_PUSHING;
8236 if (mode == DF_SNAP)
8237 ContinueMoving(x, y);
8239 MovPos[x][y] = (dx != 0 ? dx : dy);
8241 Pushed[x][y] = TRUE;
8242 Pushed[nextx][nexty] = TRUE;
8244 if (game.engine_version < VERSION_IDENT(2,2,0,7))
8245 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
8247 player->push_delay_value = -1; /* get new value later */
8249 CheckTriggeredElementSideChange(x, y, element, dig_side,
8250 CE_OTHER_GETS_PUSHED);
8251 CheckElementSideChange(x, y, element, dig_side,
8252 CE_PUSHED_BY_PLAYER, -1);
8256 else if (IS_SWITCHABLE(element))
8258 if (PLAYER_SWITCHING(player, x, y))
8261 player->is_switching = TRUE;
8262 player->switch_x = x;
8263 player->switch_y = y;
8265 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
8267 if (element == EL_ROBOT_WHEEL)
8269 Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
8273 DrawLevelField(x, y);
8275 else if (element == EL_SP_TERMINAL)
8279 for (yy=0; yy < lev_fieldy; yy++) for (xx=0; xx < lev_fieldx; xx++)
8281 if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
8283 else if (Feld[xx][yy] == EL_SP_TERMINAL)
8284 Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
8287 else if (IS_BELT_SWITCH(element))
8289 ToggleBeltSwitch(x, y);
8291 else if (element == EL_SWITCHGATE_SWITCH_UP ||
8292 element == EL_SWITCHGATE_SWITCH_DOWN)
8294 ToggleSwitchgateSwitch(x, y);
8296 else if (element == EL_LIGHT_SWITCH ||
8297 element == EL_LIGHT_SWITCH_ACTIVE)
8299 ToggleLightSwitch(x, y);
8302 PlayLevelSound(x, y, element == EL_LIGHT_SWITCH ?
8303 SND_LIGHT_SWITCH_ACTIVATING :
8304 SND_LIGHT_SWITCH_DEACTIVATING);
8307 else if (element == EL_TIMEGATE_SWITCH)
8309 ActivateTimegateSwitch(x, y);
8311 else if (element == EL_BALLOON_SWITCH_LEFT ||
8312 element == EL_BALLOON_SWITCH_RIGHT ||
8313 element == EL_BALLOON_SWITCH_UP ||
8314 element == EL_BALLOON_SWITCH_DOWN ||
8315 element == EL_BALLOON_SWITCH_ANY)
8317 if (element == EL_BALLOON_SWITCH_ANY)
8318 game.balloon_dir = move_direction;
8320 game.balloon_dir = (element == EL_BALLOON_SWITCH_LEFT ? MV_LEFT :
8321 element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
8322 element == EL_BALLOON_SWITCH_UP ? MV_UP :
8323 element == EL_BALLOON_SWITCH_DOWN ? MV_DOWN :
8326 else if (element == EL_LAMP)
8328 Feld[x][y] = EL_LAMP_ACTIVE;
8329 local_player->lights_still_needed--;
8331 DrawLevelField(x, y);
8333 else if (element == EL_TIME_ORB_FULL)
8335 Feld[x][y] = EL_TIME_ORB_EMPTY;
8337 DrawText(DX_TIME, DY_TIME, int2str(TimeLeft, 3), FONT_TEXT_2);
8339 DrawLevelField(x, y);
8342 PlaySoundStereo(SND_TIME_ORB_FULL_COLLECTING, SOUND_MIDDLE);
8350 if (!PLAYER_SWITCHING(player, x, y))
8352 player->is_switching = TRUE;
8353 player->switch_x = x;
8354 player->switch_y = y;
8356 CheckTriggeredElementSideChange(x, y, element, dig_side,
8357 CE_OTHER_IS_SWITCHING);
8358 CheckElementSideChange(x, y, element, dig_side, CE_SWITCHED, -1);
8361 CheckTriggeredElementSideChange(x, y, element, dig_side,
8362 CE_OTHER_GETS_PRESSED);
8363 CheckElementSideChange(x, y, element, dig_side,
8364 CE_PRESSED_BY_PLAYER, -1);
8367 return MF_NO_ACTION;
8370 player->push_delay = 0;
8372 if (Feld[x][y] != element) /* really digged/collected something */
8373 player->is_collecting = !player->is_digging;
8378 boolean SnapField(struct PlayerInfo *player, int dx, int dy)
8380 int jx = player->jx, jy = player->jy;
8381 int x = jx + dx, y = jy + dy;
8382 int snap_direction = (dx == -1 ? MV_LEFT :
8383 dx == +1 ? MV_RIGHT :
8385 dy == +1 ? MV_DOWN : MV_NO_MOVING);
8387 if (player->MovPos && game.engine_version >= VERSION_IDENT(2,2,0,0))
8390 if (!player->active || !IN_LEV_FIELD(x, y))
8398 if (player->MovPos == 0)
8399 player->is_pushing = FALSE;
8401 player->is_snapping = FALSE;
8403 if (player->MovPos == 0)
8405 player->is_moving = FALSE;
8406 player->is_digging = FALSE;
8407 player->is_collecting = FALSE;
8413 if (player->is_snapping)
8416 player->MovDir = snap_direction;
8418 player->is_moving = FALSE;
8419 player->is_digging = FALSE;
8420 player->is_collecting = FALSE;
8422 if (DigField(player, x, y, 0, 0, DF_SNAP) == MF_NO_ACTION)
8425 player->is_snapping = TRUE;
8427 player->is_moving = FALSE;
8428 player->is_digging = FALSE;
8429 player->is_collecting = FALSE;
8431 DrawLevelField(x, y);
8437 boolean DropElement(struct PlayerInfo *player)
8439 int jx = player->jx, jy = player->jy;
8442 if (!player->active || player->MovPos)
8445 old_element = Feld[jx][jy];
8447 /* check if player has anything that can be dropped */
8448 if (player->inventory_size == 0 && player->dynabombs_left == 0)
8451 /* check if anything can be dropped at the current position */
8452 if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
8455 /* collected custom elements can only be dropped on empty fields */
8456 if (player->inventory_size > 0 &&
8457 IS_CUSTOM_ELEMENT(player->inventory_element[player->inventory_size - 1])
8458 && old_element != EL_EMPTY)
8461 if (old_element != EL_EMPTY)
8462 Back[jx][jy] = old_element; /* store old element on this field */
8464 MovDelay[jx][jy] = 96;
8466 ResetGfxAnimation(jx, jy);
8467 ResetRandomAnimationValue(jx, jy);
8469 if (player->inventory_size > 0)
8471 int new_element = player->inventory_element[--player->inventory_size];
8473 Feld[jx][jy] = (new_element == EL_DYNAMITE ? EL_DYNAMITE_ACTIVE :
8474 new_element == EL_SP_DISK_RED ? EL_SP_DISK_RED_ACTIVE :
8477 DrawText(DX_DYNAMITE, DY_DYNAMITE,
8478 int2str(local_player->inventory_size, 3), FONT_TEXT_2);
8480 if (IN_SCR_FIELD(SCREENX(jx), SCREENY(jy)))
8481 DrawGraphicThruMask(SCREENX(jx), SCREENY(jy), el2img(Feld[jx][jy]), 0);
8483 PlayLevelSoundAction(jx, jy, ACTION_DROPPING);
8485 CheckTriggeredElementChange(jx, jy, new_element, CE_OTHER_GETS_DROPPED);
8486 CheckElementChange(jx, jy, new_element, CE_DROPPED_BY_PLAYER);
8488 TestIfElementTouchesCustomElement(jx, jy);
8490 else /* player is dropping a dyna bomb */
8492 player->dynabombs_left--;
8495 EL_DYNABOMB_PLAYER_1_ACTIVE + (player->element_nr - EL_PLAYER_1);
8497 if (IN_SCR_FIELD(SCREENX(jx), SCREENY(jy)))
8498 DrawGraphicThruMask(SCREENX(jx), SCREENY(jy), el2img(Feld[jx][jy]), 0);
8500 PlayLevelSoundAction(jx, jy, ACTION_DROPPING);
8506 /* ------------------------------------------------------------------------- */
8507 /* game sound playing functions */
8508 /* ------------------------------------------------------------------------- */
8510 static int *loop_sound_frame = NULL;
8511 static int *loop_sound_volume = NULL;
8513 void InitPlayLevelSound()
8515 int num_sounds = getSoundListSize();
8517 if (loop_sound_frame != NULL)
8518 free(loop_sound_frame);
8520 if (loop_sound_volume != NULL)
8521 free(loop_sound_volume);
8523 loop_sound_frame = checked_calloc(num_sounds * sizeof(int));
8524 loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
8527 static void PlayLevelSound(int x, int y, int nr)
8529 int sx = SCREENX(x), sy = SCREENY(y);
8530 int volume, stereo_position;
8531 int max_distance = 8;
8532 int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
8534 if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
8535 (!setup.sound_loops && IS_LOOP_SOUND(nr)))
8538 if (!IN_LEV_FIELD(x, y) ||
8539 sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
8540 sy < -max_distance || sy >= SCR_FIELDY + max_distance)
8543 volume = SOUND_MAX_VOLUME;
8545 if (!IN_SCR_FIELD(sx, sy))
8547 int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
8548 int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
8550 volume -= volume * (dx > dy ? dx : dy) / max_distance;
8553 stereo_position = (SOUND_MAX_LEFT +
8554 (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
8555 (SCR_FIELDX + 2 * max_distance));
8557 if (IS_LOOP_SOUND(nr))
8559 /* This assures that quieter loop sounds do not overwrite louder ones,
8560 while restarting sound volume comparison with each new game frame. */
8562 if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
8565 loop_sound_volume[nr] = volume;
8566 loop_sound_frame[nr] = FrameCounter;
8569 PlaySoundExt(nr, volume, stereo_position, type);
8572 static void PlayLevelSoundNearest(int x, int y, int sound_action)
8574 PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
8575 x > LEVELX(BX2) ? LEVELX(BX2) : x,
8576 y < LEVELY(BY1) ? LEVELY(BY1) :
8577 y > LEVELY(BY2) ? LEVELY(BY2) : y,
8581 static void PlayLevelSoundAction(int x, int y, int action)
8583 PlayLevelSoundElementAction(x, y, Feld[x][y], action);
8586 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
8588 int sound_effect = element_info[element].sound[action];
8590 if (sound_effect != SND_UNDEFINED)
8591 PlayLevelSound(x, y, sound_effect);
8594 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
8596 int sound_effect = element_info[Feld[x][y]].sound[action];
8598 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
8599 PlayLevelSound(x, y, sound_effect);
8602 static void StopLevelSoundActionIfLoop(int x, int y, int action)
8604 int sound_effect = element_info[Feld[x][y]].sound[action];
8606 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
8607 StopSoundExt(sound_effect, SND_CTRL_STOP_SOUND);
8610 static void PlayLevelMusic()
8613 if (levelset.music[game_status][level_nr] != MUS_UNDEFINED)
8614 PlayMusic(levelset.music[game_status][level_nr]);
8616 PlayMusic(-(level_nr + 1));
8618 PlayMusic(level_nr);
8622 void RaiseScore(int value)
8624 local_player->score += value;
8625 DrawText(DX_SCORE, DY_SCORE, int2str(local_player->score, 5), FONT_TEXT_2);
8628 void RaiseScoreElement(int element)
8634 case EL_EMERALD_YELLOW:
8635 case EL_EMERALD_RED:
8636 case EL_EMERALD_PURPLE:
8637 case EL_SP_INFOTRON:
8638 RaiseScore(level.score[SC_EMERALD]);
8641 RaiseScore(level.score[SC_DIAMOND]);
8644 RaiseScore(level.score[SC_CRYSTAL]);
8647 RaiseScore(level.score[SC_PEARL]);
8650 case EL_BD_BUTTERFLY:
8651 case EL_SP_ELECTRON:
8652 RaiseScore(level.score[SC_BUG]);
8656 case EL_SP_SNIKSNAK:
8657 RaiseScore(level.score[SC_SPACESHIP]);
8660 case EL_DARK_YAMYAM:
8661 RaiseScore(level.score[SC_YAMYAM]);
8664 RaiseScore(level.score[SC_ROBOT]);
8667 RaiseScore(level.score[SC_PACMAN]);
8670 RaiseScore(level.score[SC_NUT]);
8673 case EL_SP_DISK_RED:
8674 case EL_DYNABOMB_INCREASE_NUMBER:
8675 case EL_DYNABOMB_INCREASE_SIZE:
8676 case EL_DYNABOMB_INCREASE_POWER:
8677 RaiseScore(level.score[SC_DYNAMITE]);
8679 case EL_SHIELD_NORMAL:
8680 case EL_SHIELD_DEADLY:
8681 RaiseScore(level.score[SC_SHIELD]);
8684 RaiseScore(level.score[SC_TIME_BONUS]);
8690 RaiseScore(level.score[SC_KEY]);
8693 RaiseScore(element_info[element].collect_score);
8698 void RequestQuitGame(boolean ask_if_really_quit)
8700 if (AllPlayersGone ||
8701 !ask_if_really_quit ||
8702 level_editor_test_game ||
8703 Request("Do you really want to quit the game ?",
8704 REQ_ASK | REQ_STAY_CLOSED))
8706 #if defined(PLATFORM_UNIX)
8707 if (options.network)
8708 SendToServer_StopPlaying();
8712 game_status = GAME_MODE_MAIN;
8718 OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
8723 /* ---------- new game button stuff ---------------------------------------- */
8725 /* graphic position values for game buttons */
8726 #define GAME_BUTTON_XSIZE 30
8727 #define GAME_BUTTON_YSIZE 30
8728 #define GAME_BUTTON_XPOS 5
8729 #define GAME_BUTTON_YPOS 215
8730 #define SOUND_BUTTON_XPOS 5
8731 #define SOUND_BUTTON_YPOS (GAME_BUTTON_YPOS + GAME_BUTTON_YSIZE)
8733 #define GAME_BUTTON_STOP_XPOS (GAME_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
8734 #define GAME_BUTTON_PAUSE_XPOS (GAME_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
8735 #define GAME_BUTTON_PLAY_XPOS (GAME_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
8736 #define SOUND_BUTTON_MUSIC_XPOS (SOUND_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
8737 #define SOUND_BUTTON_LOOPS_XPOS (SOUND_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
8738 #define SOUND_BUTTON_SIMPLE_XPOS (SOUND_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
8745 } gamebutton_info[NUM_GAME_BUTTONS] =
8748 GAME_BUTTON_STOP_XPOS, GAME_BUTTON_YPOS,
8753 GAME_BUTTON_PAUSE_XPOS, GAME_BUTTON_YPOS,
8758 GAME_BUTTON_PLAY_XPOS, GAME_BUTTON_YPOS,
8763 SOUND_BUTTON_MUSIC_XPOS, SOUND_BUTTON_YPOS,
8764 SOUND_CTRL_ID_MUSIC,
8765 "background music on/off"
8768 SOUND_BUTTON_LOOPS_XPOS, SOUND_BUTTON_YPOS,
8769 SOUND_CTRL_ID_LOOPS,
8770 "sound loops on/off"
8773 SOUND_BUTTON_SIMPLE_XPOS, SOUND_BUTTON_YPOS,
8774 SOUND_CTRL_ID_SIMPLE,
8775 "normal sounds on/off"
8779 void CreateGameButtons()
8783 for (i=0; i<NUM_GAME_BUTTONS; i++)
8785 Bitmap *gd_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
8786 struct GadgetInfo *gi;
8789 unsigned long event_mask;
8790 int gd_xoffset, gd_yoffset;
8791 int gd_x1, gd_x2, gd_y1, gd_y2;
8794 gd_xoffset = gamebutton_info[i].x;
8795 gd_yoffset = gamebutton_info[i].y;
8796 gd_x1 = DOOR_GFX_PAGEX4 + gd_xoffset;
8797 gd_x2 = DOOR_GFX_PAGEX3 + gd_xoffset;
8799 if (id == GAME_CTRL_ID_STOP ||
8800 id == GAME_CTRL_ID_PAUSE ||
8801 id == GAME_CTRL_ID_PLAY)
8803 button_type = GD_TYPE_NORMAL_BUTTON;
8805 event_mask = GD_EVENT_RELEASED;
8806 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
8807 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
8811 button_type = GD_TYPE_CHECK_BUTTON;
8813 ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
8814 (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
8815 (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
8816 event_mask = GD_EVENT_PRESSED;
8817 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset;
8818 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
8821 gi = CreateGadget(GDI_CUSTOM_ID, id,
8822 GDI_INFO_TEXT, gamebutton_info[i].infotext,
8823 GDI_X, DX + gd_xoffset,
8824 GDI_Y, DY + gd_yoffset,
8825 GDI_WIDTH, GAME_BUTTON_XSIZE,
8826 GDI_HEIGHT, GAME_BUTTON_YSIZE,
8827 GDI_TYPE, button_type,
8828 GDI_STATE, GD_BUTTON_UNPRESSED,
8829 GDI_CHECKED, checked,
8830 GDI_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y1,
8831 GDI_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y1,
8832 GDI_ALT_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y2,
8833 GDI_ALT_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y2,
8834 GDI_EVENT_MASK, event_mask,
8835 GDI_CALLBACK_ACTION, HandleGameButtons,
8839 Error(ERR_EXIT, "cannot create gadget");
8841 game_gadget[id] = gi;
8845 void FreeGameButtons()
8849 for (i=0; i<NUM_GAME_BUTTONS; i++)
8850 FreeGadget(game_gadget[i]);
8853 static void MapGameButtons()
8857 for (i=0; i<NUM_GAME_BUTTONS; i++)
8858 MapGadget(game_gadget[i]);
8861 void UnmapGameButtons()
8865 for (i=0; i<NUM_GAME_BUTTONS; i++)
8866 UnmapGadget(game_gadget[i]);
8869 static void HandleGameButtons(struct GadgetInfo *gi)
8871 int id = gi->custom_id;
8873 if (game_status != GAME_MODE_PLAYING)
8878 case GAME_CTRL_ID_STOP:
8879 RequestQuitGame(TRUE);
8882 case GAME_CTRL_ID_PAUSE:
8883 if (options.network)
8885 #if defined(PLATFORM_UNIX)
8887 SendToServer_ContinuePlaying();
8889 SendToServer_PausePlaying();
8893 TapeTogglePause(TAPE_TOGGLE_MANUAL);
8896 case GAME_CTRL_ID_PLAY:
8899 #if defined(PLATFORM_UNIX)
8900 if (options.network)
8901 SendToServer_ContinuePlaying();
8905 tape.pausing = FALSE;
8906 DrawVideoDisplay(VIDEO_STATE_PAUSE_OFF,0);
8911 case SOUND_CTRL_ID_MUSIC:
8912 if (setup.sound_music)
8914 setup.sound_music = FALSE;
8917 else if (audio.music_available)
8919 setup.sound = setup.sound_music = TRUE;
8921 SetAudioMode(setup.sound);
8927 case SOUND_CTRL_ID_LOOPS:
8928 if (setup.sound_loops)
8929 setup.sound_loops = FALSE;
8930 else if (audio.loops_available)
8932 setup.sound = setup.sound_loops = TRUE;
8933 SetAudioMode(setup.sound);
8937 case SOUND_CTRL_ID_SIMPLE:
8938 if (setup.sound_simple)
8939 setup.sound_simple = FALSE;
8940 else if (audio.sound_available)
8942 setup.sound = setup.sound_simple = TRUE;
8943 SetAudioMode(setup.sound);