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 PlaySoundLevel(int, int, int);
186 static void PlaySoundLevelNearest(int, int, int);
187 static void PlaySoundLevelAction(int, int, int);
188 static void PlaySoundLevelElementAction(int, int, int, int);
189 static void PlaySoundLevelActionIfLoop(int, int, int);
190 static void StopSoundLevelActionIfLoop(int, int, int);
192 static void MapGameButtons();
193 static void HandleGameButtons(struct GadgetInfo *);
195 static struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS];
198 /* ------------------------------------------------------------------------- */
199 /* definition of elements that automatically change to other elements after */
200 /* a specified time, eventually calling a function when changing */
201 /* ------------------------------------------------------------------------- */
203 /* forward declaration for changer functions */
204 static void InitBuggyBase(int x, int y);
205 static void WarnBuggyBase(int x, int y);
207 static void InitTrap(int x, int y);
208 static void ActivateTrap(int x, int y);
209 static void ChangeActiveTrap(int x, int y);
211 static void InitRobotWheel(int x, int y);
212 static void RunRobotWheel(int x, int y);
213 static void StopRobotWheel(int x, int y);
215 static void InitTimegateWheel(int x, int y);
216 static void RunTimegateWheel(int x, int y);
218 struct ChangingElementInfo
223 void (*pre_change_function)(int x, int y);
224 void (*change_function)(int x, int y);
225 void (*post_change_function)(int x, int y);
228 static struct ChangingElementInfo change_delay_list[] =
279 EL_SWITCHGATE_OPENING,
287 EL_SWITCHGATE_CLOSING,
288 EL_SWITCHGATE_CLOSED,
320 EL_ACID_SPLASH_RIGHT,
329 EL_SP_BUGGY_BASE_ACTIVATING,
336 EL_SP_BUGGY_BASE_ACTIVATING,
337 EL_SP_BUGGY_BASE_ACTIVE,
344 EL_SP_BUGGY_BASE_ACTIVE,
368 EL_ROBOT_WHEEL_ACTIVE,
376 EL_TIMEGATE_SWITCH_ACTIVE,
397 int push_delay_fixed, push_delay_random;
402 { EL_BALLOON, 0, 0 },
404 { EL_SOKOBAN_OBJECT, 2, 0 },
405 { EL_SOKOBAN_FIELD_FULL, 2, 0 },
406 { EL_SATELLITE, 2, 0 },
407 { EL_SP_DISK_YELLOW, 2, 0 },
409 { EL_UNDEFINED, 0, 0 },
417 move_stepsize_list[] =
419 { EL_AMOEBA_DROP, 2 },
420 { EL_AMOEBA_DROPPING, 2 },
421 { EL_QUICKSAND_FILLING, 1 },
422 { EL_QUICKSAND_EMPTYING, 1 },
423 { EL_MAGIC_WALL_FILLING, 2 },
424 { EL_BD_MAGIC_WALL_FILLING, 2 },
425 { EL_MAGIC_WALL_EMPTYING, 2 },
426 { EL_BD_MAGIC_WALL_EMPTYING, 2 },
436 collect_count_list[] =
439 { EL_BD_DIAMOND, 1 },
440 { EL_EMERALD_YELLOW, 1 },
441 { EL_EMERALD_RED, 1 },
442 { EL_EMERALD_PURPLE, 1 },
444 { EL_SP_INFOTRON, 1 },
451 static unsigned long trigger_events[MAX_NUM_ELEMENTS];
453 #define IS_AUTO_CHANGING(e) (element_info[e].change_events & \
454 CH_EVENT_BIT(CE_DELAY))
455 #define IS_JUST_CHANGING(x, y) (ChangeDelay[x][y] != 0)
456 #define IS_CHANGING(x, y) (IS_AUTO_CHANGING(Feld[x][y]) || \
457 IS_JUST_CHANGING(x, y))
459 #define CE_PAGE(e, ce) (element_info[e].event_page[ce])
462 void GetPlayerConfig()
464 if (!audio.sound_available)
465 setup.sound_simple = FALSE;
467 if (!audio.loops_available)
468 setup.sound_loops = FALSE;
470 if (!audio.music_available)
471 setup.sound_music = FALSE;
473 if (!video.fullscreen_available)
474 setup.fullscreen = FALSE;
476 setup.sound = (setup.sound_simple || setup.sound_loops || setup.sound_music);
478 SetAudioMode(setup.sound);
482 static int getBeltNrFromBeltElement(int element)
484 return (element < EL_CONVEYOR_BELT_2_LEFT ? 0 :
485 element < EL_CONVEYOR_BELT_3_LEFT ? 1 :
486 element < EL_CONVEYOR_BELT_4_LEFT ? 2 : 3);
489 static int getBeltNrFromBeltActiveElement(int element)
491 return (element < EL_CONVEYOR_BELT_2_LEFT_ACTIVE ? 0 :
492 element < EL_CONVEYOR_BELT_3_LEFT_ACTIVE ? 1 :
493 element < EL_CONVEYOR_BELT_4_LEFT_ACTIVE ? 2 : 3);
496 static int getBeltNrFromBeltSwitchElement(int element)
498 return (element < EL_CONVEYOR_BELT_2_SWITCH_LEFT ? 0 :
499 element < EL_CONVEYOR_BELT_3_SWITCH_LEFT ? 1 :
500 element < EL_CONVEYOR_BELT_4_SWITCH_LEFT ? 2 : 3);
503 static int getBeltDirNrFromBeltSwitchElement(int element)
505 static int belt_base_element[4] =
507 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
508 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
509 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
510 EL_CONVEYOR_BELT_4_SWITCH_LEFT
513 int belt_nr = getBeltNrFromBeltSwitchElement(element);
514 int belt_dir_nr = element - belt_base_element[belt_nr];
516 return (belt_dir_nr % 3);
519 static int getBeltDirFromBeltSwitchElement(int element)
521 static int belt_move_dir[3] =
528 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(element);
530 return belt_move_dir[belt_dir_nr];
533 static void InitPlayerField(int x, int y, int element, boolean init_game)
535 if (element == EL_SP_MURPHY)
539 if (stored_player[0].present)
541 Feld[x][y] = EL_SP_MURPHY_CLONE;
547 stored_player[0].use_murphy_graphic = TRUE;
550 Feld[x][y] = EL_PLAYER_1;
556 struct PlayerInfo *player = &stored_player[Feld[x][y] - EL_PLAYER_1];
557 int jx = player->jx, jy = player->jy;
559 player->present = TRUE;
561 if (!options.network || player->connected)
563 player->active = TRUE;
565 /* remove potentially duplicate players */
566 if (StorePlayer[jx][jy] == Feld[x][y])
567 StorePlayer[jx][jy] = 0;
569 StorePlayer[x][y] = Feld[x][y];
573 printf("Player %d activated.\n", player->element_nr);
574 printf("[Local player is %d and currently %s.]\n",
575 local_player->element_nr,
576 local_player->active ? "active" : "not active");
580 Feld[x][y] = EL_EMPTY;
581 player->jx = player->last_jx = x;
582 player->jy = player->last_jy = y;
586 static void InitField(int x, int y, boolean init_game)
588 int element = Feld[x][y];
597 InitPlayerField(x, y, element, init_game);
601 if (x < lev_fieldx-1 && Feld[x+1][y] == EL_ACID)
602 Feld[x][y] = EL_ACID_POOL_TOPLEFT;
603 else if (x > 0 && Feld[x-1][y] == EL_ACID)
604 Feld[x][y] = EL_ACID_POOL_TOPRIGHT;
605 else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPLEFT)
606 Feld[x][y] = EL_ACID_POOL_BOTTOMLEFT;
607 else if (y > 0 && Feld[x][y-1] == EL_ACID)
608 Feld[x][y] = EL_ACID_POOL_BOTTOM;
609 else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPRIGHT)
610 Feld[x][y] = EL_ACID_POOL_BOTTOMRIGHT;
618 case EL_SPACESHIP_RIGHT:
619 case EL_SPACESHIP_UP:
620 case EL_SPACESHIP_LEFT:
621 case EL_SPACESHIP_DOWN:
623 case EL_BD_BUTTERFLY_RIGHT:
624 case EL_BD_BUTTERFLY_UP:
625 case EL_BD_BUTTERFLY_LEFT:
626 case EL_BD_BUTTERFLY_DOWN:
627 case EL_BD_BUTTERFLY:
628 case EL_BD_FIREFLY_RIGHT:
629 case EL_BD_FIREFLY_UP:
630 case EL_BD_FIREFLY_LEFT:
631 case EL_BD_FIREFLY_DOWN:
633 case EL_PACMAN_RIGHT:
657 if (y == lev_fieldy - 1)
659 Feld[x][y] = EL_AMOEBA_GROWING;
660 Store[x][y] = EL_AMOEBA_WET;
664 case EL_DYNAMITE_ACTIVE:
669 local_player->lights_still_needed++;
672 case EL_SOKOBAN_FIELD_EMPTY:
673 local_player->sokobanfields_still_needed++;
677 local_player->friends_still_needed++;
682 GfxDir[x][y] = MovDir[x][y] = 1 << RND(4);
687 Feld[x][y] = EL_EMPTY;
692 case EL_EM_KEY_1_FILE:
693 Feld[x][y] = EL_EM_KEY_1;
695 case EL_EM_KEY_2_FILE:
696 Feld[x][y] = EL_EM_KEY_2;
698 case EL_EM_KEY_3_FILE:
699 Feld[x][y] = EL_EM_KEY_3;
701 case EL_EM_KEY_4_FILE:
702 Feld[x][y] = EL_EM_KEY_4;
706 case EL_CONVEYOR_BELT_1_SWITCH_LEFT:
707 case EL_CONVEYOR_BELT_1_SWITCH_MIDDLE:
708 case EL_CONVEYOR_BELT_1_SWITCH_RIGHT:
709 case EL_CONVEYOR_BELT_2_SWITCH_LEFT:
710 case EL_CONVEYOR_BELT_2_SWITCH_MIDDLE:
711 case EL_CONVEYOR_BELT_2_SWITCH_RIGHT:
712 case EL_CONVEYOR_BELT_3_SWITCH_LEFT:
713 case EL_CONVEYOR_BELT_3_SWITCH_MIDDLE:
714 case EL_CONVEYOR_BELT_3_SWITCH_RIGHT:
715 case EL_CONVEYOR_BELT_4_SWITCH_LEFT:
716 case EL_CONVEYOR_BELT_4_SWITCH_MIDDLE:
717 case EL_CONVEYOR_BELT_4_SWITCH_RIGHT:
720 int belt_nr = getBeltNrFromBeltSwitchElement(Feld[x][y]);
721 int belt_dir = getBeltDirFromBeltSwitchElement(Feld[x][y]);
722 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(Feld[x][y]);
724 if (game.belt_dir_nr[belt_nr] == 3) /* initial value */
726 game.belt_dir[belt_nr] = belt_dir;
727 game.belt_dir_nr[belt_nr] = belt_dir_nr;
729 else /* more than one switch -- set it like the first switch */
731 Feld[x][y] = Feld[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
736 case EL_SWITCHGATE_SWITCH_DOWN: /* always start with same switch pos */
738 Feld[x][y] = EL_SWITCHGATE_SWITCH_UP;
741 case EL_LIGHT_SWITCH_ACTIVE:
743 game.light_time_left = level.time_light * FRAMES_PER_SECOND;
747 if (IS_CUSTOM_ELEMENT(element) && CAN_MOVE(element))
753 void DrawGameDoorValues()
757 for (i=0; i<MAX_PLAYERS; i++)
759 if (stored_player[i].key[j])
760 DrawMiniGraphicExt(drawto, DX_KEYS + j * MINI_TILEX, DY_KEYS,
761 el2edimg(EL_KEY_1 + j));
763 DrawText(DX + XX_EMERALDS, DY + YY_EMERALDS,
764 int2str(local_player->gems_still_needed, 3), FONT_TEXT_2);
765 DrawText(DX + XX_DYNAMITE, DY + YY_DYNAMITE,
766 int2str(local_player->inventory_size, 3), FONT_TEXT_2);
767 DrawText(DX + XX_SCORE, DY + YY_SCORE,
768 int2str(local_player->score, 5), FONT_TEXT_2);
769 DrawText(DX + XX_TIME, DY + YY_TIME,
770 int2str(TimeLeft, 3), FONT_TEXT_2);
775 =============================================================================
777 -----------------------------------------------------------------------------
778 initialize game engine due to level / tape version number
779 =============================================================================
782 static void InitGameEngine()
786 /* set game engine from tape file when re-playing, else from level file */
787 game.engine_version = (tape.playing ? tape.engine_version :
790 /* dynamically adjust element properties according to game engine version */
791 InitElementPropertiesEngine(game.engine_version);
794 printf("level %d: level version == %06d\n", level_nr, level.game_version);
795 printf(" tape version == %06d [%s] [file: %06d]\n",
796 tape.engine_version, (tape.playing ? "PLAYING" : "RECORDING"),
798 printf(" => game.engine_version == %06d\n", game.engine_version);
801 /* ---------- initialize player's initial move delay --------------------- */
803 /* dynamically adjust player properties according to game engine version */
804 game.initial_move_delay =
805 (game.engine_version <= VERSION_IDENT(2,0,1,0) ? INITIAL_MOVE_DELAY_ON :
806 INITIAL_MOVE_DELAY_OFF);
808 /* dynamically adjust player properties according to level information */
809 game.initial_move_delay_value =
810 (level.double_speed ? MOVE_DELAY_HIGH_SPEED : MOVE_DELAY_NORMAL_SPEED);
812 /* ---------- initialize player's initial push delay --------------------- */
814 /* dynamically adjust player properties according to game engine version */
815 game.initial_push_delay_value =
816 (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
818 /* ---------- initialize changing elements ------------------------------- */
820 /* initialize changing elements information */
821 for (i=0; i < MAX_NUM_ELEMENTS; i++)
823 struct ElementInfo *ei = &element_info[i];
825 /* this pointer might have been changed in the level editor */
826 ei->change = &ei->change_page[0];
828 if (!IS_CUSTOM_ELEMENT(i))
830 ei->change->target_element = EL_EMPTY_SPACE;
831 ei->change->delay_fixed = 0;
832 ei->change->delay_random = 0;
833 ei->change->delay_frames = 1;
836 ei->change_events = CE_BITMASK_DEFAULT;
837 for (j=0; j < NUM_CHANGE_EVENTS; j++)
839 ei->event_page_nr[j] = 0;
840 ei->event_page[j] = &ei->change_page[0];
844 /* add changing elements from pre-defined list */
845 for (i=0; change_delay_list[i].element != EL_UNDEFINED; i++)
847 struct ChangingElementInfo *ch_delay = &change_delay_list[i];
848 struct ElementInfo *ei = &element_info[ch_delay->element];
850 ei->change->target_element = ch_delay->target_element;
851 ei->change->delay_fixed = ch_delay->change_delay;
853 ei->change->pre_change_function = ch_delay->pre_change_function;
854 ei->change->change_function = ch_delay->change_function;
855 ei->change->post_change_function = ch_delay->post_change_function;
857 ei->change_events |= CH_EVENT_BIT(CE_DELAY);
861 /* add change events from custom element configuration */
862 for (i=0; i < NUM_CUSTOM_ELEMENTS; i++)
864 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
866 for (j=0; j < ei->num_change_pages; j++)
868 if (!ei->change_page[j].can_change)
871 for (k=0; k < NUM_CHANGE_EVENTS; k++)
873 /* only add event page for the first page found with this event */
874 if (ei->change_page[j].events & CH_EVENT_BIT(k) &&
875 !(ei->change_events & CH_EVENT_BIT(k)))
877 ei->change_events |= CH_EVENT_BIT(k);
878 ei->event_page_nr[k] = j;
879 ei->event_page[k] = &ei->change_page[j];
887 /* add change events from custom element configuration */
888 for (i=0; i < NUM_CUSTOM_ELEMENTS; i++)
890 int element = EL_CUSTOM_START + i;
892 /* only add custom elements that change after fixed/random frame delay */
893 if (CAN_CHANGE(element) && HAS_CHANGE_EVENT(element, CE_DELAY))
894 element_info[element].change_events |= CH_EVENT_BIT(CE_DELAY);
898 /* ---------- initialize trigger events ---------------------------------- */
900 /* initialize trigger events information */
901 for (i=0; i<MAX_NUM_ELEMENTS; i++)
902 trigger_events[i] = EP_BITMASK_DEFAULT;
905 /* add trigger events from element change event properties */
906 for (i=0; i<MAX_NUM_ELEMENTS; i++)
908 struct ElementInfo *ei = &element_info[i];
910 for (j=0; j < ei->num_change_pages; j++)
912 if (!ei->change_page[j].can_change)
915 if (ei->change_page[j].events & CH_EVENT_BIT(CE_BY_OTHER_ACTION))
917 int trigger_element = ei->change_page[j].trigger_element;
919 trigger_events[trigger_element] |= ei->change_page[j].events;
924 /* add trigger events from element change event properties */
925 for (i=0; i<MAX_NUM_ELEMENTS; i++)
926 if (HAS_CHANGE_EVENT(i, CE_BY_OTHER_ACTION))
927 trigger_events[element_info[i].change->trigger_element] |=
928 element_info[i].change->events;
931 /* ---------- initialize push delay -------------------------------------- */
933 /* initialize push delay values to default */
934 for (i=0; i<MAX_NUM_ELEMENTS; i++)
936 if (!IS_CUSTOM_ELEMENT(i))
938 element_info[i].push_delay_fixed = game.default_push_delay_fixed;
939 element_info[i].push_delay_random = game.default_push_delay_random;
943 /* set push delay value for certain elements from pre-defined list */
944 for (i=0; push_delay_list[i].element != EL_UNDEFINED; i++)
946 int e = push_delay_list[i].element;
948 element_info[e].push_delay_fixed = push_delay_list[i].push_delay_fixed;
949 element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
952 /* ---------- initialize move stepsize ----------------------------------- */
954 /* initialize move stepsize values to default */
955 for (i=0; i<MAX_NUM_ELEMENTS; i++)
956 if (!IS_CUSTOM_ELEMENT(i))
957 element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
959 /* set move stepsize value for certain elements from pre-defined list */
960 for (i=0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
962 int e = move_stepsize_list[i].element;
964 element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
967 /* ---------- initialize gem count --------------------------------------- */
969 /* initialize gem count values for each element */
970 for (i=0; i<MAX_NUM_ELEMENTS; i++)
971 if (!IS_CUSTOM_ELEMENT(i))
972 element_info[i].collect_count = 0;
974 /* add gem count values for all elements from pre-defined list */
975 for (i=0; collect_count_list[i].element != EL_UNDEFINED; i++)
976 element_info[collect_count_list[i].element].collect_count =
977 collect_count_list[i].count;
982 =============================================================================
984 -----------------------------------------------------------------------------
985 initialize and start new game
986 =============================================================================
991 boolean emulate_bd = TRUE; /* unless non-BOULDERDASH elements found */
992 boolean emulate_sb = TRUE; /* unless non-SOKOBAN elements found */
993 boolean emulate_sp = TRUE; /* unless non-SUPAPLEX elements found */
1000 #if USE_NEW_AMOEBA_CODE
1001 printf("Using new amoeba code.\n");
1003 printf("Using old amoeba code.\n");
1008 /* don't play tapes over network */
1009 network_playing = (options.network && !tape.playing);
1011 for (i=0; i<MAX_PLAYERS; i++)
1013 struct PlayerInfo *player = &stored_player[i];
1015 player->index_nr = i;
1016 player->element_nr = EL_PLAYER_1 + i;
1018 player->present = FALSE;
1019 player->active = FALSE;
1022 player->effective_action = 0;
1023 player->programmed_action = 0;
1026 player->gems_still_needed = level.gems_needed;
1027 player->sokobanfields_still_needed = 0;
1028 player->lights_still_needed = 0;
1029 player->friends_still_needed = 0;
1032 player->key[j] = FALSE;
1034 player->dynabomb_count = 0;
1035 player->dynabomb_size = 1;
1036 player->dynabombs_left = 0;
1037 player->dynabomb_xl = FALSE;
1039 player->MovDir = MV_NO_MOVING;
1042 player->GfxDir = MV_NO_MOVING;
1043 player->GfxAction = ACTION_DEFAULT;
1045 player->StepFrame = 0;
1047 player->use_murphy_graphic = FALSE;
1049 player->actual_frame_counter = 0;
1051 player->last_move_dir = MV_NO_MOVING;
1053 player->is_waiting = FALSE;
1054 player->is_moving = FALSE;
1055 player->is_digging = FALSE;
1056 player->is_snapping = FALSE;
1057 player->is_collecting = FALSE;
1058 player->is_pushing = FALSE;
1059 player->is_switching = FALSE;
1061 player->switch_x = -1;
1062 player->switch_y = -1;
1064 player->show_envelope = 0;
1066 player->move_delay = game.initial_move_delay;
1067 player->move_delay_value = game.initial_move_delay_value;
1069 player->push_delay = 0;
1070 player->push_delay_value = game.initial_push_delay_value;
1072 player->last_jx = player->last_jy = 0;
1073 player->jx = player->jy = 0;
1075 player->shield_normal_time_left = 0;
1076 player->shield_deadly_time_left = 0;
1078 player->inventory_size = 0;
1080 DigField(player, 0, 0, 0, 0, DF_NO_PUSH);
1081 SnapField(player, 0, 0);
1083 player->LevelSolved = FALSE;
1084 player->GameOver = FALSE;
1087 network_player_action_received = FALSE;
1089 #if defined(PLATFORM_UNIX)
1090 /* initial null action */
1091 if (network_playing)
1092 SendToServer_MovePlayer(MV_NO_MOVING);
1100 TimeLeft = level.time;
1102 ScreenMovDir = MV_NO_MOVING;
1106 ScrollStepSize = 0; /* will be correctly initialized by ScrollScreen() */
1108 AllPlayersGone = FALSE;
1110 game.yamyam_content_nr = 0;
1111 game.magic_wall_active = FALSE;
1112 game.magic_wall_time_left = 0;
1113 game.light_time_left = 0;
1114 game.timegate_time_left = 0;
1115 game.switchgate_pos = 0;
1116 game.balloon_dir = MV_NO_MOVING;
1117 game.gravity = level.initial_gravity;
1118 game.explosions_delayed = TRUE;
1120 game.envelope_active = FALSE;
1124 game.belt_dir[i] = MV_NO_MOVING;
1125 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
1128 for (i=0; i<MAX_NUM_AMOEBA; i++)
1129 AmoebaCnt[i] = AmoebaCnt2[i] = 0;
1131 for (x=0; x<lev_fieldx; x++)
1133 for (y=0; y<lev_fieldy; y++)
1135 Feld[x][y] = level.field[x][y];
1136 MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
1137 ChangeDelay[x][y] = 0;
1138 ChangePage[x][y] = -1;
1139 Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
1141 WasJustMoving[x][y] = 0;
1142 WasJustFalling[x][y] = 0;
1144 Pushed[x][y] = FALSE;
1146 Changed[x][y] = CE_BITMASK_DEFAULT;
1147 ChangeEvent[x][y] = CE_BITMASK_DEFAULT;
1149 ExplodePhase[x][y] = 0;
1150 ExplodeField[x][y] = EX_NO_EXPLOSION;
1153 GfxRandom[x][y] = INIT_GFX_RANDOM();
1154 GfxElement[x][y] = EL_UNDEFINED;
1155 GfxAction[x][y] = ACTION_DEFAULT;
1156 GfxDir[x][y] = MV_NO_MOVING;
1160 for(y=0; y<lev_fieldy; y++)
1162 for(x=0; x<lev_fieldx; x++)
1164 if (emulate_bd && !IS_BD_ELEMENT(Feld[x][y]))
1166 if (emulate_sb && !IS_SB_ELEMENT(Feld[x][y]))
1168 if (emulate_sp && !IS_SP_ELEMENT(Feld[x][y]))
1171 InitField(x, y, TRUE);
1177 game.emulation = (emulate_bd ? EMU_BOULDERDASH :
1178 emulate_sb ? EMU_SOKOBAN :
1179 emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
1181 /* correct non-moving belts to start moving left */
1183 if (game.belt_dir[i] == MV_NO_MOVING)
1184 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
1186 /* check if any connected player was not found in playfield */
1187 for (i=0; i<MAX_PLAYERS; i++)
1189 struct PlayerInfo *player = &stored_player[i];
1191 if (player->connected && !player->present)
1193 for (j=0; j<MAX_PLAYERS; j++)
1195 struct PlayerInfo *some_player = &stored_player[j];
1196 int jx = some_player->jx, jy = some_player->jy;
1198 /* assign first free player found that is present in the playfield */
1199 if (some_player->present && !some_player->connected)
1201 player->present = TRUE;
1202 player->active = TRUE;
1203 some_player->present = FALSE;
1205 StorePlayer[jx][jy] = player->element_nr;
1206 player->jx = player->last_jx = jx;
1207 player->jy = player->last_jy = jy;
1217 /* when playing a tape, eliminate all players who do not participate */
1219 for (i=0; i<MAX_PLAYERS; i++)
1221 if (stored_player[i].active && !tape.player_participates[i])
1223 struct PlayerInfo *player = &stored_player[i];
1224 int jx = player->jx, jy = player->jy;
1226 player->active = FALSE;
1227 StorePlayer[jx][jy] = 0;
1228 Feld[jx][jy] = EL_EMPTY;
1232 else if (!options.network && !setup.team_mode) /* && !tape.playing */
1234 /* when in single player mode, eliminate all but the first active player */
1236 for (i=0; i<MAX_PLAYERS; i++)
1238 if (stored_player[i].active)
1240 for (j=i+1; j<MAX_PLAYERS; j++)
1242 if (stored_player[j].active)
1244 struct PlayerInfo *player = &stored_player[j];
1245 int jx = player->jx, jy = player->jy;
1247 player->active = FALSE;
1248 StorePlayer[jx][jy] = 0;
1249 Feld[jx][jy] = EL_EMPTY;
1256 /* when recording the game, store which players take part in the game */
1259 for (i=0; i<MAX_PLAYERS; i++)
1260 if (stored_player[i].active)
1261 tape.player_participates[i] = TRUE;
1266 for (i=0; i<MAX_PLAYERS; i++)
1268 struct PlayerInfo *player = &stored_player[i];
1270 printf("Player %d: present == %d, connected == %d, active == %d.\n",
1275 if (local_player == player)
1276 printf("Player %d is local player.\n", i+1);
1280 if (BorderElement == EL_EMPTY)
1283 SBX_Right = lev_fieldx - SCR_FIELDX;
1285 SBY_Lower = lev_fieldy - SCR_FIELDY;
1290 SBX_Right = lev_fieldx - SCR_FIELDX + 1;
1292 SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
1295 if (lev_fieldx + (SBX_Left == -1 ? 2 : 0) <= SCR_FIELDX)
1296 SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
1298 if (lev_fieldy + (SBY_Upper == -1 ? 2 : 0) <= SCR_FIELDY)
1299 SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
1301 /* if local player not found, look for custom element that might create
1302 the player (make some assumptions about the right custom element) */
1303 if (!local_player->present)
1305 int start_x = 0, start_y = 0;
1306 int found_rating = 0;
1307 int found_element = EL_UNDEFINED;
1309 for(y=0; y < lev_fieldy; y++) for(x=0; x < lev_fieldx; x++)
1311 int element = Feld[x][y];
1316 if (!IS_CUSTOM_ELEMENT(element))
1319 if (CAN_CHANGE(element))
1321 for (i=0; i < element_info[element].num_change_pages; i++)
1323 content = element_info[element].change_page[i].target_element;
1324 is_player = ELEM_IS_PLAYER(content);
1326 if (is_player && (found_rating < 3 || element < found_element))
1332 found_element = element;
1337 for(yy=0; yy < 3; yy++) for(xx=0; xx < 3; xx++)
1339 content = element_info[element].content[xx][yy];
1340 is_player = ELEM_IS_PLAYER(content);
1342 if (is_player && (found_rating < 2 || element < found_element))
1344 start_x = x + xx - 1;
1345 start_y = y + yy - 1;
1348 found_element = element;
1351 if (!CAN_CHANGE(element))
1354 for (i=0; i < element_info[element].num_change_pages; i++)
1356 content = element_info[element].change_page[i].content[xx][yy];
1357 is_player = ELEM_IS_PLAYER(content);
1359 if (is_player && (found_rating < 1 || element < found_element))
1361 start_x = x + xx - 1;
1362 start_y = y + yy - 1;
1365 found_element = element;
1371 scroll_x = (start_x < SBX_Left + MIDPOSX ? SBX_Left :
1372 start_x > SBX_Right + MIDPOSX ? SBX_Right :
1375 scroll_y = (start_y < SBY_Upper + MIDPOSY ? SBY_Upper :
1376 start_y > SBY_Lower + MIDPOSY ? SBY_Lower :
1382 scroll_x = (local_player->jx < SBX_Left + MIDPOSX ? SBX_Left :
1383 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
1384 local_player->jx - MIDPOSX);
1386 scroll_y = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
1387 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
1388 local_player->jy - MIDPOSY);
1390 scroll_x = SBX_Left;
1391 scroll_y = SBY_Upper;
1392 if (local_player->jx >= SBX_Left + MIDPOSX)
1393 scroll_x = (local_player->jx <= SBX_Right + MIDPOSX ?
1394 local_player->jx - MIDPOSX :
1396 if (local_player->jy >= SBY_Upper + MIDPOSY)
1397 scroll_y = (local_player->jy <= SBY_Lower + MIDPOSY ?
1398 local_player->jy - MIDPOSY :
1403 CloseDoor(DOOR_CLOSE_1);
1408 /* after drawing the level, correct some elements */
1409 if (game.timegate_time_left == 0)
1410 CloseAllOpenTimegates();
1412 if (setup.soft_scrolling)
1413 BlitBitmap(fieldbuffer, backbuffer, FX, FY, SXSIZE, SYSIZE, SX, SY);
1415 redraw_mask |= REDRAW_FROM_BACKBUFFER;
1418 /* copy default game door content to main double buffer */
1419 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
1420 DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
1423 DrawText(DX + XX_LEVEL, DY + YY_LEVEL, int2str(level_nr, 2), FONT_TEXT_2);
1426 DrawTextExt(drawto, DX + XX_EMERALDS, DY + YY_EMERALDS,
1427 int2str(level_nr, 3), FONT_LEVEL_NUMBER, BLIT_OPAQUE);
1428 BlitBitmap(drawto, drawto,
1429 DX + XX_EMERALDS, DY + YY_EMERALDS + 1,
1430 getFontWidth(FONT_LEVEL_NUMBER) * 3,
1431 getFontHeight(FONT_LEVEL_NUMBER) - 1,
1432 DX + XX_LEVEL - 1, DY + YY_LEVEL + 1);
1435 DrawGameDoorValues();
1439 game_gadget[SOUND_CTRL_ID_MUSIC]->checked = setup.sound_music;
1440 game_gadget[SOUND_CTRL_ID_LOOPS]->checked = setup.sound_loops;
1441 game_gadget[SOUND_CTRL_ID_SIMPLE]->checked = setup.sound_simple;
1445 /* copy actual game door content to door double buffer for OpenDoor() */
1446 BlitBitmap(drawto, bitmap_db_door,
1447 DX, DY, DXSIZE, DYSIZE, DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1);
1449 OpenDoor(DOOR_OPEN_ALL);
1451 PlaySoundStereo(SND_GAME_STARTING, SOUND_MIDDLE);
1452 if (setup.sound_music)
1453 PlayMusic(level_nr);
1455 KeyboardAutoRepeatOffUnlessAutoplay();
1460 printf("Player %d %sactive.\n",
1461 i + 1, (stored_player[i].active ? "" : "not "));
1465 void InitMovDir(int x, int y)
1467 int i, element = Feld[x][y];
1468 static int xy[4][2] =
1475 static int direction[3][4] =
1477 { MV_RIGHT, MV_UP, MV_LEFT, MV_DOWN },
1478 { MV_LEFT, MV_DOWN, MV_RIGHT, MV_UP },
1479 { MV_LEFT, MV_RIGHT, MV_UP, MV_DOWN }
1488 Feld[x][y] = EL_BUG;
1489 MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
1492 case EL_SPACESHIP_RIGHT:
1493 case EL_SPACESHIP_UP:
1494 case EL_SPACESHIP_LEFT:
1495 case EL_SPACESHIP_DOWN:
1496 Feld[x][y] = EL_SPACESHIP;
1497 MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
1500 case EL_BD_BUTTERFLY_RIGHT:
1501 case EL_BD_BUTTERFLY_UP:
1502 case EL_BD_BUTTERFLY_LEFT:
1503 case EL_BD_BUTTERFLY_DOWN:
1504 Feld[x][y] = EL_BD_BUTTERFLY;
1505 MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
1508 case EL_BD_FIREFLY_RIGHT:
1509 case EL_BD_FIREFLY_UP:
1510 case EL_BD_FIREFLY_LEFT:
1511 case EL_BD_FIREFLY_DOWN:
1512 Feld[x][y] = EL_BD_FIREFLY;
1513 MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
1516 case EL_PACMAN_RIGHT:
1518 case EL_PACMAN_LEFT:
1519 case EL_PACMAN_DOWN:
1520 Feld[x][y] = EL_PACMAN;
1521 MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
1524 case EL_SP_SNIKSNAK:
1525 MovDir[x][y] = MV_UP;
1528 case EL_SP_ELECTRON:
1529 MovDir[x][y] = MV_LEFT;
1536 Feld[x][y] = EL_MOLE;
1537 MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
1541 if (IS_CUSTOM_ELEMENT(element))
1543 if (element_info[element].move_direction_initial != MV_NO_MOVING)
1544 MovDir[x][y] = element_info[element].move_direction_initial;
1545 else if (element_info[element].move_pattern == MV_ALL_DIRECTIONS ||
1546 element_info[element].move_pattern == MV_TURNING_LEFT ||
1547 element_info[element].move_pattern == MV_TURNING_RIGHT)
1548 MovDir[x][y] = 1 << RND(4);
1549 else if (element_info[element].move_pattern == MV_HORIZONTAL)
1550 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
1551 else if (element_info[element].move_pattern == MV_VERTICAL)
1552 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
1553 else if (element_info[element].move_pattern & MV_ANY_DIRECTION)
1554 MovDir[x][y] = element_info[element].move_pattern;
1555 else if (element_info[element].move_pattern == MV_ALONG_LEFT_SIDE ||
1556 element_info[element].move_pattern == MV_ALONG_RIGHT_SIDE)
1560 int x1 = x + xy[i][0];
1561 int y1 = y + xy[i][1];
1563 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
1565 if (element_info[element].move_pattern == MV_ALONG_RIGHT_SIDE)
1566 MovDir[x][y] = direction[0][i];
1568 MovDir[x][y] = direction[1][i];
1577 MovDir[x][y] = 1 << RND(4);
1579 if (element != EL_BUG &&
1580 element != EL_SPACESHIP &&
1581 element != EL_BD_BUTTERFLY &&
1582 element != EL_BD_FIREFLY)
1587 int x1 = x + xy[i][0];
1588 int y1 = y + xy[i][1];
1590 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
1592 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
1594 MovDir[x][y] = direction[0][i];
1597 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
1598 element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
1600 MovDir[x][y] = direction[1][i];
1609 GfxDir[x][y] = MovDir[x][y];
1612 void InitAmoebaNr(int x, int y)
1615 int group_nr = AmoebeNachbarNr(x, y);
1619 for (i=1; i<MAX_NUM_AMOEBA; i++)
1621 if (AmoebaCnt[i] == 0)
1629 AmoebaNr[x][y] = group_nr;
1630 AmoebaCnt[group_nr]++;
1631 AmoebaCnt2[group_nr]++;
1637 boolean raise_level = FALSE;
1639 if (local_player->MovPos)
1643 if (tape.auto_play) /* tape might already be stopped here */
1644 tape.auto_play_level_solved = TRUE;
1646 if (tape.playing && tape.auto_play)
1647 tape.auto_play_level_solved = TRUE;
1650 local_player->LevelSolved = FALSE;
1652 PlaySoundStereo(SND_GAME_WINNING, SOUND_MIDDLE);
1656 if (!tape.playing && setup.sound_loops)
1657 PlaySoundExt(SND_GAME_LEVELTIME_BONUS, SOUND_MAX_VOLUME, SOUND_MIDDLE,
1658 SND_CTRL_PLAY_LOOP);
1660 while (TimeLeft > 0)
1662 if (!tape.playing && !setup.sound_loops)
1663 PlaySoundStereo(SND_GAME_LEVELTIME_BONUS, SOUND_MIDDLE);
1664 if (TimeLeft > 0 && !(TimeLeft % 10))
1665 RaiseScore(level.score[SC_TIME_BONUS]);
1666 if (TimeLeft > 100 && !(TimeLeft % 10))
1670 DrawText(DX_TIME, DY_TIME, int2str(TimeLeft, 3), FONT_TEXT_2);
1677 if (!tape.playing && setup.sound_loops)
1678 StopSound(SND_GAME_LEVELTIME_BONUS);
1680 else if (level.time == 0) /* level without time limit */
1682 if (!tape.playing && setup.sound_loops)
1683 PlaySoundExt(SND_GAME_LEVELTIME_BONUS, SOUND_MAX_VOLUME, SOUND_MIDDLE,
1684 SND_CTRL_PLAY_LOOP);
1686 while (TimePlayed < 999)
1688 if (!tape.playing && !setup.sound_loops)
1689 PlaySoundStereo(SND_GAME_LEVELTIME_BONUS, SOUND_MIDDLE);
1690 if (TimePlayed < 999 && !(TimePlayed % 10))
1691 RaiseScore(level.score[SC_TIME_BONUS]);
1692 if (TimePlayed < 900 && !(TimePlayed % 10))
1696 DrawText(DX_TIME, DY_TIME, int2str(TimePlayed, 3), FONT_TEXT_2);
1703 if (!tape.playing && setup.sound_loops)
1704 StopSound(SND_GAME_LEVELTIME_BONUS);
1707 /* close exit door after last player */
1708 if ((Feld[ExitX][ExitY] == EL_EXIT_OPEN ||
1709 Feld[ExitX][ExitY] == EL_SP_EXIT_OPEN) && AllPlayersGone)
1711 int element = Feld[ExitX][ExitY];
1713 Feld[ExitX][ExitY] = (element == EL_EXIT_OPEN ? EL_EXIT_CLOSING :
1714 EL_SP_EXIT_CLOSING);
1716 PlaySoundLevelElementAction(ExitX, ExitY, element, ACTION_CLOSING);
1719 /* Hero disappears */
1720 DrawLevelField(ExitX, ExitY);
1726 CloseDoor(DOOR_CLOSE_1);
1731 SaveTape(tape.level_nr); /* Ask to save tape */
1734 if (level_nr == leveldir_current->handicap_level)
1736 leveldir_current->handicap_level++;
1737 SaveLevelSetup_SeriesInfo();
1740 if (level_editor_test_game)
1741 local_player->score = -1; /* no highscore when playing from editor */
1742 else if (level_nr < leveldir_current->last_level)
1743 raise_level = TRUE; /* advance to next level */
1745 if ((hi_pos = NewHiScore()) >= 0)
1747 game_status = GAME_MODE_SCORES;
1748 DrawHallOfFame(hi_pos);
1757 game_status = GAME_MODE_MAIN;
1774 LoadScore(level_nr);
1776 if (strcmp(setup.player_name, EMPTY_PLAYER_NAME) == 0 ||
1777 local_player->score < highscore[MAX_SCORE_ENTRIES - 1].Score)
1780 for (k=0; k<MAX_SCORE_ENTRIES; k++)
1782 if (local_player->score > highscore[k].Score)
1784 /* player has made it to the hall of fame */
1786 if (k < MAX_SCORE_ENTRIES - 1)
1788 int m = MAX_SCORE_ENTRIES - 1;
1791 for (l=k; l<MAX_SCORE_ENTRIES; l++)
1792 if (!strcmp(setup.player_name, highscore[l].Name))
1794 if (m == k) /* player's new highscore overwrites his old one */
1800 strcpy(highscore[l].Name, highscore[l - 1].Name);
1801 highscore[l].Score = highscore[l - 1].Score;
1808 strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
1809 highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
1810 highscore[k].Score = local_player->score;
1816 else if (!strncmp(setup.player_name, highscore[k].Name,
1817 MAX_PLAYER_NAME_LEN))
1818 break; /* player already there with a higher score */
1824 SaveScore(level_nr);
1829 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
1831 if (player->GfxAction != action || player->GfxDir != dir)
1834 printf("Player frame reset! (%d => %d, %d => %d)\n",
1835 player->GfxAction, action, player->GfxDir, dir);
1838 player->GfxAction = action;
1839 player->GfxDir = dir;
1841 player->StepFrame = 0;
1845 static void ResetRandomAnimationValue(int x, int y)
1847 GfxRandom[x][y] = INIT_GFX_RANDOM();
1850 static void ResetGfxAnimation(int x, int y)
1853 GfxAction[x][y] = ACTION_DEFAULT;
1854 GfxDir[x][y] = MovDir[x][y];
1857 void InitMovingField(int x, int y, int direction)
1859 int element = Feld[x][y];
1860 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
1861 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
1865 if (!WasJustMoving[x][y] || direction != MovDir[x][y])
1866 ResetGfxAnimation(x, y);
1868 MovDir[newx][newy] = MovDir[x][y] = direction;
1869 GfxDir[x][y] = direction;
1871 if (Feld[newx][newy] == EL_EMPTY)
1872 Feld[newx][newy] = EL_BLOCKED;
1874 if (direction == MV_DOWN && CAN_FALL(element))
1875 GfxAction[x][y] = ACTION_FALLING;
1877 GfxAction[x][y] = ACTION_MOVING;
1879 GfxFrame[newx][newy] = GfxFrame[x][y];
1880 GfxRandom[newx][newy] = GfxRandom[x][y];
1881 GfxAction[newx][newy] = GfxAction[x][y];
1882 GfxDir[newx][newy] = GfxDir[x][y];
1885 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
1887 int direction = MovDir[x][y];
1888 int newx = x + (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
1889 int newy = y + (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
1895 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
1897 int oldx = x, oldy = y;
1898 int direction = MovDir[x][y];
1900 if (direction == MV_LEFT)
1902 else if (direction == MV_RIGHT)
1904 else if (direction == MV_UP)
1906 else if (direction == MV_DOWN)
1909 *comes_from_x = oldx;
1910 *comes_from_y = oldy;
1913 int MovingOrBlocked2Element(int x, int y)
1915 int element = Feld[x][y];
1917 if (element == EL_BLOCKED)
1921 Blocked2Moving(x, y, &oldx, &oldy);
1922 return Feld[oldx][oldy];
1928 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
1930 /* like MovingOrBlocked2Element(), but if element is moving
1931 and (x,y) is the field the moving element is just leaving,
1932 return EL_BLOCKED instead of the element value */
1933 int element = Feld[x][y];
1935 if (IS_MOVING(x, y))
1937 if (element == EL_BLOCKED)
1941 Blocked2Moving(x, y, &oldx, &oldy);
1942 return Feld[oldx][oldy];
1951 static void RemoveField(int x, int y)
1953 Feld[x][y] = EL_EMPTY;
1960 ChangeDelay[x][y] = 0;
1961 ChangePage[x][y] = -1;
1962 Pushed[x][y] = FALSE;
1964 GfxElement[x][y] = EL_UNDEFINED;
1965 GfxAction[x][y] = ACTION_DEFAULT;
1966 GfxDir[x][y] = MV_NO_MOVING;
1969 void RemoveMovingField(int x, int y)
1971 int oldx = x, oldy = y, newx = x, newy = y;
1972 int element = Feld[x][y];
1973 int next_element = EL_UNDEFINED;
1975 if (element != EL_BLOCKED && !IS_MOVING(x, y))
1978 if (IS_MOVING(x, y))
1980 Moving2Blocked(x, y, &newx, &newy);
1981 if (Feld[newx][newy] != EL_BLOCKED)
1984 else if (element == EL_BLOCKED)
1986 Blocked2Moving(x, y, &oldx, &oldy);
1987 if (!IS_MOVING(oldx, oldy))
1991 if (element == EL_BLOCKED &&
1992 (Feld[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
1993 Feld[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
1994 Feld[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
1995 Feld[oldx][oldy] == EL_AMOEBA_DROPPING))
1996 next_element = get_next_element(Feld[oldx][oldy]);
1998 RemoveField(oldx, oldy);
1999 RemoveField(newx, newy);
2001 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
2003 if (next_element != EL_UNDEFINED)
2004 Feld[oldx][oldy] = next_element;
2006 DrawLevelField(oldx, oldy);
2007 DrawLevelField(newx, newy);
2010 void DrawDynamite(int x, int y)
2012 int sx = SCREENX(x), sy = SCREENY(y);
2013 int graphic = el2img(Feld[x][y]);
2016 if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
2019 if (IS_WALKABLE_INSIDE(Back[x][y]))
2023 DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
2024 else if (Store[x][y])
2025 DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
2027 frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
2030 if (Back[x][y] || Store[x][y])
2031 DrawGraphicThruMask(sx, sy, graphic, frame);
2033 DrawGraphic(sx, sy, graphic, frame);
2035 if (game.emulation == EMU_SUPAPLEX)
2036 DrawGraphic(sx, sy, IMG_SP_DISK_RED, frame);
2037 else if (Store[x][y])
2038 DrawGraphicThruMask(sx, sy, graphic, frame);
2040 DrawGraphic(sx, sy, graphic, frame);
2044 void CheckDynamite(int x, int y)
2046 if (MovDelay[x][y] != 0) /* dynamite is still waiting to explode */
2050 if (MovDelay[x][y] != 0)
2053 PlaySoundLevelActionIfLoop(x, y, ACTION_ACTIVE);
2060 StopSoundLevelActionIfLoop(x, y, ACTION_ACTIVE);
2062 if (Feld[x][y] == EL_DYNAMITE_ACTIVE ||
2063 Feld[x][y] == EL_SP_DISK_RED_ACTIVE)
2064 StopSound(SND_DYNAMITE_ACTIVE);
2066 StopSound(SND_DYNABOMB_ACTIVE);
2072 void RelocatePlayer(int x, int y, int element)
2074 struct PlayerInfo *player = &stored_player[element - EL_PLAYER_1];
2077 RemoveField(x, y); /* temporarily remove newly placed player */
2078 DrawLevelField(x, y);
2081 if (player->present)
2083 while (player->MovPos)
2085 ScrollPlayer(player, SCROLL_GO_ON);
2086 ScrollScreen(NULL, SCROLL_GO_ON);
2092 Delay(GAME_FRAME_DELAY);
2095 DrawPlayer(player); /* needed here only to cleanup last field */
2096 DrawLevelField(player->jx, player->jy); /* remove player graphic */
2098 player->is_moving = FALSE;
2101 Feld[x][y] = element;
2102 InitPlayerField(x, y, element, TRUE);
2104 if (player == local_player)
2106 int scroll_xx = -999, scroll_yy = -999;
2108 while (scroll_xx != scroll_x || scroll_yy != scroll_y)
2111 int fx = FX, fy = FY;
2113 scroll_xx = (local_player->jx < SBX_Left + MIDPOSX ? SBX_Left :
2114 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
2115 local_player->jx - MIDPOSX);
2117 scroll_yy = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
2118 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
2119 local_player->jy - MIDPOSY);
2121 dx = (scroll_xx < scroll_x ? +1 : scroll_xx > scroll_x ? -1 : 0);
2122 dy = (scroll_yy < scroll_y ? +1 : scroll_yy > scroll_y ? -1 : 0);
2127 fx += dx * TILEX / 2;
2128 fy += dy * TILEY / 2;
2130 ScrollLevel(dx, dy);
2133 /* scroll in two steps of half tile size to make things smoother */
2134 BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
2136 Delay(GAME_FRAME_DELAY);
2138 /* scroll second step to align at full tile size */
2140 Delay(GAME_FRAME_DELAY);
2145 void Explode(int ex, int ey, int phase, int mode)
2149 int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
2150 int last_phase = num_phase * delay;
2151 int half_phase = (num_phase / 2) * delay;
2152 int first_phase_after_start = EX_PHASE_START + 1;
2154 if (game.explosions_delayed)
2156 ExplodeField[ex][ey] = mode;
2160 if (phase == EX_PHASE_START) /* initialize 'Store[][]' field */
2162 int center_element = Feld[ex][ey];
2165 /* --- This is only really needed (and now handled) in "Impact()". --- */
2166 /* do not explode moving elements that left the explode field in time */
2167 if (game.engine_version >= VERSION_IDENT(2,2,0,7) &&
2168 center_element == EL_EMPTY && (mode == EX_NORMAL || mode == EX_CENTER))
2172 if (mode == EX_NORMAL || mode == EX_CENTER)
2173 PlaySoundLevelAction(ex, ey, ACTION_EXPLODING);
2175 /* remove things displayed in background while burning dynamite */
2176 if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
2179 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
2181 /* put moving element to center field (and let it explode there) */
2182 center_element = MovingOrBlocked2Element(ex, ey);
2183 RemoveMovingField(ex, ey);
2184 Feld[ex][ey] = center_element;
2187 for (y = ey - 1; y <= ey + 1; y++) for(x = ex - 1; x <= ex + 1; x++)
2189 int xx = x - ex + 1;
2190 int yy = y - ey + 1;
2193 if (!IN_LEV_FIELD(x, y) ||
2194 ((mode != EX_NORMAL || center_element == EL_AMOEBA_TO_DIAMOND) &&
2195 (x != ex || y != ey)))
2198 element = Feld[x][y];
2200 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
2202 element = MovingOrBlocked2Element(x, y);
2204 if (!IS_EXPLOSION_PROOF(element))
2205 RemoveMovingField(x, y);
2211 if (IS_EXPLOSION_PROOF(element))
2214 /* indestructible elements can only explode in center (but not flames) */
2215 if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey)) ||
2216 element == EL_FLAMES)
2221 if ((IS_INDESTRUCTIBLE(element) &&
2222 (game.engine_version < VERSION_IDENT(2,2,0,0) ||
2223 (!IS_WALKABLE_OVER(element) && !IS_WALKABLE_UNDER(element)))) ||
2224 element == EL_FLAMES)
2228 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
2230 if (IS_ACTIVE_BOMB(element))
2232 /* re-activate things under the bomb like gate or penguin */
2233 Feld[x][y] = (Store[x][y] ? Store[x][y] : EL_EMPTY);
2240 /* save walkable background elements while explosion on same tile */
2242 if (IS_INDESTRUCTIBLE(element))
2243 Back[x][y] = element;
2245 if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element))
2246 Back[x][y] = element;
2249 /* ignite explodable elements reached by other explosion */
2250 if (element == EL_EXPLOSION)
2251 element = Store2[x][y];
2254 if (AmoebaNr[x][y] &&
2255 (element == EL_AMOEBA_FULL ||
2256 element == EL_BD_AMOEBA ||
2257 element == EL_AMOEBA_GROWING))
2259 AmoebaCnt[AmoebaNr[x][y]]--;
2260 AmoebaCnt2[AmoebaNr[x][y]]--;
2266 if (IS_PLAYER(ex, ey) && !PLAYER_PROTECTED(ex, ey))
2268 switch(StorePlayer[ex][ey])
2271 Store[x][y] = EL_EMERALD_RED;
2274 Store[x][y] = EL_EMERALD;
2277 Store[x][y] = EL_EMERALD_PURPLE;
2281 Store[x][y] = EL_EMERALD_YELLOW;
2285 if (game.emulation == EMU_SUPAPLEX)
2286 Store[x][y] = EL_EMPTY;
2288 else if (center_element == EL_MOLE)
2289 Store[x][y] = EL_EMERALD_RED;
2290 else if (center_element == EL_PENGUIN)
2291 Store[x][y] = EL_EMERALD_PURPLE;
2292 else if (center_element == EL_BUG)
2293 Store[x][y] = ((x == ex && y == ey) ? EL_DIAMOND : EL_EMERALD);
2294 else if (center_element == EL_BD_BUTTERFLY)
2295 Store[x][y] = EL_BD_DIAMOND;
2296 else if (center_element == EL_SP_ELECTRON)
2297 Store[x][y] = EL_SP_INFOTRON;
2298 else if (center_element == EL_AMOEBA_TO_DIAMOND)
2299 Store[x][y] = level.amoeba_content;
2300 else if (center_element == EL_YAMYAM)
2301 Store[x][y] = level.yamyam_content[game.yamyam_content_nr][xx][yy];
2302 else if (IS_CUSTOM_ELEMENT(center_element) &&
2303 element_info[center_element].content[xx][yy] != EL_EMPTY)
2304 Store[x][y] = element_info[center_element].content[xx][yy];
2305 else if (element == EL_WALL_EMERALD)
2306 Store[x][y] = EL_EMERALD;
2307 else if (element == EL_WALL_DIAMOND)
2308 Store[x][y] = EL_DIAMOND;
2309 else if (element == EL_WALL_BD_DIAMOND)
2310 Store[x][y] = EL_BD_DIAMOND;
2311 else if (element == EL_WALL_EMERALD_YELLOW)
2312 Store[x][y] = EL_EMERALD_YELLOW;
2313 else if (element == EL_WALL_EMERALD_RED)
2314 Store[x][y] = EL_EMERALD_RED;
2315 else if (element == EL_WALL_EMERALD_PURPLE)
2316 Store[x][y] = EL_EMERALD_PURPLE;
2317 else if (element == EL_WALL_PEARL)
2318 Store[x][y] = EL_PEARL;
2319 else if (element == EL_WALL_CRYSTAL)
2320 Store[x][y] = EL_CRYSTAL;
2321 else if (IS_CUSTOM_ELEMENT(element) && !CAN_EXPLODE(element))
2322 Store[x][y] = element_info[element].content[1][1];
2324 Store[x][y] = EL_EMPTY;
2326 if (x != ex || y != ey ||
2327 center_element == EL_AMOEBA_TO_DIAMOND || mode == EX_BORDER)
2328 Store2[x][y] = element;
2331 if (AmoebaNr[x][y] &&
2332 (element == EL_AMOEBA_FULL ||
2333 element == EL_BD_AMOEBA ||
2334 element == EL_AMOEBA_GROWING))
2336 AmoebaCnt[AmoebaNr[x][y]]--;
2337 AmoebaCnt2[AmoebaNr[x][y]]--;
2343 MovDir[x][y] = MovPos[x][y] = 0;
2344 GfxDir[x][y] = MovDir[x][y];
2349 Feld[x][y] = EL_EXPLOSION;
2351 GfxElement[x][y] = center_element;
2353 GfxElement[x][y] = EL_UNDEFINED;
2356 ExplodePhase[x][y] = 1;
2360 if (center_element == EL_YAMYAM)
2361 game.yamyam_content_nr =
2362 (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
2373 ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
2377 /* activate this even in non-DEBUG version until cause for crash in
2378 getGraphicAnimationFrame() (see below) is found and eliminated */
2382 if (GfxElement[x][y] == EL_UNDEFINED)
2385 printf("Explode(): x = %d, y = %d: GfxElement == EL_UNDEFINED\n", x, y);
2386 printf("Explode(): This should never happen!\n");
2389 GfxElement[x][y] = EL_EMPTY;
2393 if (phase == first_phase_after_start)
2395 int element = Store2[x][y];
2397 if (element == EL_BLACK_ORB)
2399 Feld[x][y] = Store2[x][y];
2404 else if (phase == half_phase)
2406 int element = Store2[x][y];
2408 if (IS_PLAYER(x, y))
2409 KillHeroUnlessProtected(x, y);
2410 else if (CAN_EXPLODE_BY_FIRE(element))
2412 Feld[x][y] = Store2[x][y];
2416 else if (element == EL_AMOEBA_TO_DIAMOND)
2417 AmoebeUmwandeln(x, y);
2420 if (phase == last_phase)
2424 element = Feld[x][y] = Store[x][y];
2425 Store[x][y] = Store2[x][y] = 0;
2426 GfxElement[x][y] = EL_UNDEFINED;
2428 if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
2429 element = Feld[x][y] = Back[x][y];
2432 MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
2433 GfxDir[x][y] = MV_NO_MOVING;
2434 ChangeDelay[x][y] = 0;
2435 ChangePage[x][y] = -1;
2437 InitField(x, y, FALSE);
2438 if (CAN_MOVE(element))
2440 DrawLevelField(x, y);
2442 TestIfElementTouchesCustomElement(x, y);
2444 if (GFX_CRUMBLED(element))
2445 DrawLevelFieldCrumbledSandNeighbours(x, y);
2447 if (IS_PLAYER(x, y) && !PLAYERINFO(x,y)->present)
2448 StorePlayer[x][y] = 0;
2450 if (ELEM_IS_PLAYER(element))
2451 RelocatePlayer(x, y, element);
2453 else if (phase >= delay && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
2456 int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
2458 int stored = Store[x][y];
2459 int graphic = (game.emulation != EMU_SUPAPLEX ? IMG_EXPLOSION :
2460 stored == EL_SP_INFOTRON ? IMG_SP_EXPLOSION_INFOTRON :
2463 int frame = getGraphicAnimationFrame(graphic, phase - delay);
2466 DrawLevelFieldCrumbledSand(x, y);
2468 if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
2470 DrawLevelElement(x, y, Back[x][y]);
2471 DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
2473 else if (IS_WALKABLE_UNDER(Back[x][y]))
2475 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
2476 DrawLevelElementThruMask(x, y, Back[x][y]);
2478 else if (!IS_WALKABLE_INSIDE(Back[x][y]))
2479 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
2483 void DynaExplode(int ex, int ey)
2486 int dynabomb_size = 1;
2487 boolean dynabomb_xl = FALSE;
2488 struct PlayerInfo *player;
2489 static int xy[4][2] =
2497 if (IS_ACTIVE_BOMB(Feld[ex][ey]))
2499 player = &stored_player[Feld[ex][ey] - EL_DYNABOMB_PLAYER_1_ACTIVE];
2500 dynabomb_size = player->dynabomb_size;
2501 dynabomb_xl = player->dynabomb_xl;
2502 player->dynabombs_left++;
2505 Explode(ex, ey, EX_PHASE_START, EX_CENTER);
2509 for (j=1; j<=dynabomb_size; j++)
2511 int x = ex + j * xy[i % 4][0];
2512 int y = ey + j * xy[i % 4][1];
2515 if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
2518 element = Feld[x][y];
2520 /* do not restart explosions of fields with active bombs */
2521 if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
2524 Explode(x, y, EX_PHASE_START, EX_BORDER);
2526 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
2527 if (element != EL_EMPTY &&
2528 element != EL_SAND &&
2529 element != EL_EXPLOSION &&
2536 void Bang(int x, int y)
2539 int element = MovingOrBlocked2Element(x, y);
2541 int element = Feld[x][y];
2545 if (IS_PLAYER(x, y) && !PLAYER_PROTECTED(x, y))
2547 if (IS_PLAYER(x, y))
2550 struct PlayerInfo *player = PLAYERINFO(x, y);
2552 element = Feld[x][y] = (player->use_murphy_graphic ? EL_SP_MURPHY :
2553 player->element_nr);
2558 PlaySoundLevelAction(x, y, ACTION_EXPLODING);
2560 if (game.emulation == EMU_SUPAPLEX)
2561 PlaySoundLevel(x, y, SND_SP_ELEMENT_EXPLODING);
2563 PlaySoundLevel(x, y, SND_ELEMENT_EXPLODING);
2568 if (IS_PLAYER(x, y)) /* remove objects that might cause smaller explosion */
2576 case EL_BD_BUTTERFLY:
2579 case EL_DARK_YAMYAM:
2583 RaiseScoreElement(element);
2584 Explode(x, y, EX_PHASE_START, EX_NORMAL);
2586 case EL_DYNABOMB_PLAYER_1_ACTIVE:
2587 case EL_DYNABOMB_PLAYER_2_ACTIVE:
2588 case EL_DYNABOMB_PLAYER_3_ACTIVE:
2589 case EL_DYNABOMB_PLAYER_4_ACTIVE:
2590 case EL_DYNABOMB_INCREASE_NUMBER:
2591 case EL_DYNABOMB_INCREASE_SIZE:
2592 case EL_DYNABOMB_INCREASE_POWER:
2597 case EL_LAMP_ACTIVE:
2598 if (IS_PLAYER(x, y))
2599 Explode(x, y, EX_PHASE_START, EX_NORMAL);
2601 Explode(x, y, EX_PHASE_START, EX_CENTER);
2604 if (CAN_EXPLODE_1X1(element))
2605 Explode(x, y, EX_PHASE_START, EX_CENTER);
2607 Explode(x, y, EX_PHASE_START, EX_NORMAL);
2611 CheckTriggeredElementChange(x, y, element, CE_OTHER_IS_EXPLODING);
2614 void SplashAcid(int x, int y)
2616 int element = Feld[x][y];
2618 if (element != EL_ACID_SPLASH_LEFT &&
2619 element != EL_ACID_SPLASH_RIGHT)
2621 PlaySoundLevel(x, y, SND_ACID_SPLASHING);
2623 if (IN_LEV_FIELD(x-1, y) && IS_FREE(x-1, y) &&
2624 (!IN_LEV_FIELD(x-1, y-1) ||
2625 !CAN_FALL(MovingOrBlocked2Element(x-1, y-1))))
2626 Feld[x-1][y] = EL_ACID_SPLASH_LEFT;
2628 if (IN_LEV_FIELD(x+1, y) && IS_FREE(x+1, y) &&
2629 (!IN_LEV_FIELD(x+1, y-1) ||
2630 !CAN_FALL(MovingOrBlocked2Element(x+1, y-1))))
2631 Feld[x+1][y] = EL_ACID_SPLASH_RIGHT;
2635 static void InitBeltMovement()
2637 static int belt_base_element[4] =
2639 EL_CONVEYOR_BELT_1_LEFT,
2640 EL_CONVEYOR_BELT_2_LEFT,
2641 EL_CONVEYOR_BELT_3_LEFT,
2642 EL_CONVEYOR_BELT_4_LEFT
2644 static int belt_base_active_element[4] =
2646 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
2647 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
2648 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
2649 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
2654 /* set frame order for belt animation graphic according to belt direction */
2661 int element = belt_base_active_element[belt_nr] + j;
2662 int graphic = el2img(element);
2664 if (game.belt_dir[i] == MV_LEFT)
2665 graphic_info[graphic].anim_mode &= ~ANIM_REVERSE;
2667 graphic_info[graphic].anim_mode |= ANIM_REVERSE;
2671 for(y=0; y<lev_fieldy; y++)
2673 for(x=0; x<lev_fieldx; x++)
2675 int element = Feld[x][y];
2679 if (IS_BELT(element) && game.belt_dir[i] != MV_NO_MOVING)
2681 int e_belt_nr = getBeltNrFromBeltElement(element);
2684 if (e_belt_nr == belt_nr)
2686 int belt_part = Feld[x][y] - belt_base_element[belt_nr];
2688 Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
2696 static void ToggleBeltSwitch(int x, int y)
2698 static int belt_base_element[4] =
2700 EL_CONVEYOR_BELT_1_LEFT,
2701 EL_CONVEYOR_BELT_2_LEFT,
2702 EL_CONVEYOR_BELT_3_LEFT,
2703 EL_CONVEYOR_BELT_4_LEFT
2705 static int belt_base_active_element[4] =
2707 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
2708 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
2709 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
2710 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
2712 static int belt_base_switch_element[4] =
2714 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
2715 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
2716 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
2717 EL_CONVEYOR_BELT_4_SWITCH_LEFT
2719 static int belt_move_dir[4] =
2727 int element = Feld[x][y];
2728 int belt_nr = getBeltNrFromBeltSwitchElement(element);
2729 int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
2730 int belt_dir = belt_move_dir[belt_dir_nr];
2733 if (!IS_BELT_SWITCH(element))
2736 game.belt_dir_nr[belt_nr] = belt_dir_nr;
2737 game.belt_dir[belt_nr] = belt_dir;
2739 if (belt_dir_nr == 3)
2742 /* set frame order for belt animation graphic according to belt direction */
2745 int element = belt_base_active_element[belt_nr] + i;
2746 int graphic = el2img(element);
2748 if (belt_dir == MV_LEFT)
2749 graphic_info[graphic].anim_mode &= ~ANIM_REVERSE;
2751 graphic_info[graphic].anim_mode |= ANIM_REVERSE;
2754 for (yy=0; yy<lev_fieldy; yy++)
2756 for (xx=0; xx<lev_fieldx; xx++)
2758 int element = Feld[xx][yy];
2760 if (IS_BELT_SWITCH(element))
2762 int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
2764 if (e_belt_nr == belt_nr)
2766 Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
2767 DrawLevelField(xx, yy);
2770 else if (IS_BELT(element) && belt_dir != MV_NO_MOVING)
2772 int e_belt_nr = getBeltNrFromBeltElement(element);
2774 if (e_belt_nr == belt_nr)
2776 int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
2778 Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
2779 DrawLevelField(xx, yy);
2782 else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NO_MOVING)
2784 int e_belt_nr = getBeltNrFromBeltActiveElement(element);
2786 if (e_belt_nr == belt_nr)
2788 int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
2790 Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
2791 DrawLevelField(xx, yy);
2798 static void ToggleSwitchgateSwitch(int x, int y)
2802 game.switchgate_pos = !game.switchgate_pos;
2804 for (yy=0; yy<lev_fieldy; yy++)
2806 for (xx=0; xx<lev_fieldx; xx++)
2808 int element = Feld[xx][yy];
2810 if (element == EL_SWITCHGATE_SWITCH_UP ||
2811 element == EL_SWITCHGATE_SWITCH_DOWN)
2813 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
2814 DrawLevelField(xx, yy);
2816 else if (element == EL_SWITCHGATE_OPEN ||
2817 element == EL_SWITCHGATE_OPENING)
2819 Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
2821 PlaySoundLevelAction(xx, yy, ACTION_CLOSING);
2823 PlaySoundLevel(xx, yy, SND_SWITCHGATE_CLOSING);
2826 else if (element == EL_SWITCHGATE_CLOSED ||
2827 element == EL_SWITCHGATE_CLOSING)
2829 Feld[xx][yy] = EL_SWITCHGATE_OPENING;
2831 PlaySoundLevelAction(xx, yy, ACTION_OPENING);
2833 PlaySoundLevel(xx, yy, SND_SWITCHGATE_OPENING);
2840 static int getInvisibleActiveFromInvisibleElement(int element)
2842 return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
2843 element == EL_INVISIBLE_WALL ? EL_INVISIBLE_WALL_ACTIVE :
2844 element == EL_INVISIBLE_SAND ? EL_INVISIBLE_SAND_ACTIVE :
2848 static int getInvisibleFromInvisibleActiveElement(int element)
2850 return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
2851 element == EL_INVISIBLE_WALL_ACTIVE ? EL_INVISIBLE_WALL :
2852 element == EL_INVISIBLE_SAND_ACTIVE ? EL_INVISIBLE_SAND :
2856 static void RedrawAllLightSwitchesAndInvisibleElements()
2860 for (y=0; y<lev_fieldy; y++)
2862 for (x=0; x<lev_fieldx; x++)
2864 int element = Feld[x][y];
2866 if (element == EL_LIGHT_SWITCH &&
2867 game.light_time_left > 0)
2869 Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
2870 DrawLevelField(x, y);
2872 else if (element == EL_LIGHT_SWITCH_ACTIVE &&
2873 game.light_time_left == 0)
2875 Feld[x][y] = EL_LIGHT_SWITCH;
2876 DrawLevelField(x, y);
2878 else if (element == EL_INVISIBLE_STEELWALL ||
2879 element == EL_INVISIBLE_WALL ||
2880 element == EL_INVISIBLE_SAND)
2882 if (game.light_time_left > 0)
2883 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
2885 DrawLevelField(x, y);
2887 else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
2888 element == EL_INVISIBLE_WALL_ACTIVE ||
2889 element == EL_INVISIBLE_SAND_ACTIVE)
2891 if (game.light_time_left == 0)
2892 Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
2894 DrawLevelField(x, y);
2900 static void ToggleLightSwitch(int x, int y)
2902 int element = Feld[x][y];
2904 game.light_time_left =
2905 (element == EL_LIGHT_SWITCH ?
2906 level.time_light * FRAMES_PER_SECOND : 0);
2908 RedrawAllLightSwitchesAndInvisibleElements();
2911 static void ActivateTimegateSwitch(int x, int y)
2915 game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
2917 for (yy=0; yy<lev_fieldy; yy++)
2919 for (xx=0; xx<lev_fieldx; xx++)
2921 int element = Feld[xx][yy];
2923 if (element == EL_TIMEGATE_CLOSED ||
2924 element == EL_TIMEGATE_CLOSING)
2926 Feld[xx][yy] = EL_TIMEGATE_OPENING;
2927 PlaySoundLevel(xx, yy, SND_TIMEGATE_OPENING);
2931 else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
2933 Feld[xx][yy] = EL_TIMEGATE_SWITCH;
2934 DrawLevelField(xx, yy);
2941 Feld[x][y] = EL_TIMEGATE_SWITCH_ACTIVE;
2944 inline static int getElementMoveStepsize(int x, int y)
2946 int element = Feld[x][y];
2947 int direction = MovDir[x][y];
2948 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
2949 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
2950 int horiz_move = (dx != 0);
2951 int sign = (horiz_move ? dx : dy);
2952 int step = sign * element_info[element].move_stepsize;
2954 /* special values for move stepsize for spring and things on conveyor belt */
2957 if (CAN_FALL(element) &&
2958 y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
2959 step = sign * MOVE_STEPSIZE_NORMAL / 2;
2960 else if (element == EL_SPRING)
2961 step = sign * MOVE_STEPSIZE_NORMAL * 2;
2967 void Impact(int x, int y)
2969 boolean lastline = (y == lev_fieldy-1);
2970 boolean object_hit = FALSE;
2971 boolean impact = (lastline || object_hit);
2972 int element = Feld[x][y];
2973 int smashed = EL_UNDEFINED;
2975 if (!lastline) /* check if element below was hit */
2977 if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
2980 object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
2981 MovDir[x][y + 1] != MV_DOWN ||
2982 MovPos[x][y + 1] <= TILEY / 2));
2984 /* do not smash moving elements that left the smashed field in time */
2985 if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
2986 ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
2990 smashed = MovingOrBlocked2Element(x, y + 1);
2992 impact = (lastline || object_hit);
2995 if (!lastline && smashed == EL_ACID) /* element falls into acid */
3001 /* only reset graphic animation if graphic really changes after impact */
3003 el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
3005 ResetGfxAnimation(x, y);
3006 DrawLevelField(x, y);
3009 if (impact && CAN_EXPLODE_IMPACT(element))
3014 else if (impact && element == EL_PEARL)
3016 Feld[x][y] = EL_PEARL_BREAKING;
3017 PlaySoundLevel(x, y, SND_PEARL_BREAKING);
3020 else if (impact && CheckElementChange(x, y, element, CE_IMPACT))
3022 PlaySoundLevelElementAction(x, y, element, ACTION_IMPACT);
3027 if (impact && element == EL_AMOEBA_DROP)
3029 if (object_hit && IS_PLAYER(x, y + 1))
3030 KillHeroUnlessProtected(x, y + 1);
3031 else if (object_hit && smashed == EL_PENGUIN)
3035 Feld[x][y] = EL_AMOEBA_GROWING;
3036 Store[x][y] = EL_AMOEBA_WET;
3038 ResetRandomAnimationValue(x, y);
3043 if (object_hit) /* check which object was hit */
3045 if (CAN_PASS_MAGIC_WALL(element) &&
3046 (smashed == EL_MAGIC_WALL ||
3047 smashed == EL_BD_MAGIC_WALL))
3050 int activated_magic_wall =
3051 (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
3052 EL_BD_MAGIC_WALL_ACTIVE);
3054 /* activate magic wall / mill */
3055 for (yy=0; yy<lev_fieldy; yy++)
3056 for (xx=0; xx<lev_fieldx; xx++)
3057 if (Feld[xx][yy] == smashed)
3058 Feld[xx][yy] = activated_magic_wall;
3060 game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
3061 game.magic_wall_active = TRUE;
3063 PlaySoundLevel(x, y, (smashed == EL_MAGIC_WALL ?
3064 SND_MAGIC_WALL_ACTIVATING :
3065 SND_BD_MAGIC_WALL_ACTIVATING));
3068 if (IS_PLAYER(x, y + 1))
3070 if (CAN_SMASH_PLAYER(element))
3072 KillHeroUnlessProtected(x, y + 1);
3076 else if (smashed == EL_PENGUIN)
3078 if (CAN_SMASH_PLAYER(element))
3084 else if (element == EL_BD_DIAMOND)
3086 if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
3092 else if ((element == EL_SP_INFOTRON ||
3093 element == EL_SP_ZONK) &&
3094 (smashed == EL_SP_SNIKSNAK ||
3095 smashed == EL_SP_ELECTRON ||
3096 smashed == EL_SP_DISK_ORANGE))
3102 else if (CAN_SMASH_ENEMIES(element) && IS_CLASSIC_ENEMY(smashed))
3108 else if (CAN_SMASH_EVERYTHING(element))
3110 if (IS_CLASSIC_ENEMY(smashed) ||
3111 CAN_EXPLODE_SMASHED(smashed))
3116 else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
3118 if (smashed == EL_LAMP ||
3119 smashed == EL_LAMP_ACTIVE)
3124 else if (smashed == EL_NUT)
3126 Feld[x][y + 1] = EL_NUT_BREAKING;
3127 PlaySoundLevel(x, y, SND_NUT_BREAKING);
3128 RaiseScoreElement(EL_NUT);
3131 else if (smashed == EL_PEARL)
3133 Feld[x][y + 1] = EL_PEARL_BREAKING;
3134 PlaySoundLevel(x, y, SND_PEARL_BREAKING);
3137 else if (smashed == EL_DIAMOND)
3139 Feld[x][y + 1] = EL_DIAMOND_BREAKING;
3140 PlaySoundLevel(x, y, SND_DIAMOND_BREAKING);
3143 else if (IS_BELT_SWITCH(smashed))
3145 ToggleBeltSwitch(x, y + 1);
3147 else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
3148 smashed == EL_SWITCHGATE_SWITCH_DOWN)
3150 ToggleSwitchgateSwitch(x, y + 1);
3152 else if (smashed == EL_LIGHT_SWITCH ||
3153 smashed == EL_LIGHT_SWITCH_ACTIVE)
3155 ToggleLightSwitch(x, y + 1);
3159 CheckElementChange(x, y + 1, smashed, CE_SMASHED);
3161 CheckTriggeredElementSideChange(x, y + 1, smashed, CH_SIDE_TOP,
3162 CE_OTHER_IS_SWITCHING);
3163 CheckElementSideChange(x, y + 1, smashed, CH_SIDE_TOP,
3169 CheckElementChange(x, y + 1, smashed, CE_SMASHED);
3174 /* play sound of magic wall / mill */
3176 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
3177 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE))
3179 if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
3180 PlaySoundLevel(x, y, SND_MAGIC_WALL_FILLING);
3181 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
3182 PlaySoundLevel(x, y, SND_BD_MAGIC_WALL_FILLING);
3187 /* play sound of object that hits the ground */
3188 if (lastline || object_hit)
3189 PlaySoundLevelElementAction(x, y, element, ACTION_IMPACT);
3192 inline static void TurnRoundExt(int x, int y)
3204 { 0, 0 }, { 0, 0 }, { 0, 0 },
3209 int left, right, back;
3213 { MV_DOWN, MV_UP, MV_RIGHT },
3214 { MV_UP, MV_DOWN, MV_LEFT },
3216 { MV_LEFT, MV_RIGHT, MV_DOWN },
3220 { MV_RIGHT, MV_LEFT, MV_UP }
3223 int element = Feld[x][y];
3224 int old_move_dir = MovDir[x][y];
3225 int left_dir = turn[old_move_dir].left;
3226 int right_dir = turn[old_move_dir].right;
3227 int back_dir = turn[old_move_dir].back;
3229 int left_dx = move_xy[left_dir].x, left_dy = move_xy[left_dir].y;
3230 int right_dx = move_xy[right_dir].x, right_dy = move_xy[right_dir].y;
3231 int move_dx = move_xy[old_move_dir].x, move_dy = move_xy[old_move_dir].y;
3232 int back_dx = move_xy[back_dir].x, back_dy = move_xy[back_dir].y;
3234 int left_x = x + left_dx, left_y = y + left_dy;
3235 int right_x = x + right_dx, right_y = y + right_dy;
3236 int move_x = x + move_dx, move_y = y + move_dy;
3240 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
3242 TestIfBadThingTouchesOtherBadThing(x, y);
3244 if (ENEMY_CAN_ENTER_FIELD(right_x, right_y))
3245 MovDir[x][y] = right_dir;
3246 else if (!ENEMY_CAN_ENTER_FIELD(move_x, move_y))
3247 MovDir[x][y] = left_dir;
3249 if (element == EL_BUG && MovDir[x][y] != old_move_dir)
3251 else if (element == EL_BD_BUTTERFLY) /* && MovDir[x][y] == left_dir) */
3254 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
3255 element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
3257 TestIfBadThingTouchesOtherBadThing(x, y);
3259 if (ENEMY_CAN_ENTER_FIELD(left_x, left_y))
3260 MovDir[x][y] = left_dir;
3261 else if (!ENEMY_CAN_ENTER_FIELD(move_x, move_y))
3262 MovDir[x][y] = right_dir;
3264 if ((element == EL_SPACESHIP ||
3265 element == EL_SP_SNIKSNAK ||
3266 element == EL_SP_ELECTRON)
3267 && MovDir[x][y] != old_move_dir)
3269 else if (element == EL_BD_FIREFLY) /* && MovDir[x][y] == right_dir) */
3272 else if (element == EL_YAMYAM)
3274 boolean can_turn_left = YAMYAM_CAN_ENTER_FIELD(left_x, left_y);
3275 boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(right_x, right_y);
3277 if (can_turn_left && can_turn_right)
3278 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
3279 else if (can_turn_left)
3280 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
3281 else if (can_turn_right)
3282 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
3284 MovDir[x][y] = back_dir;
3286 MovDelay[x][y] = 16 + 16 * RND(3);
3288 else if (element == EL_DARK_YAMYAM)
3290 boolean can_turn_left = DARK_YAMYAM_CAN_ENTER_FIELD(left_x, left_y);
3291 boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(right_x, right_y);
3293 if (can_turn_left && can_turn_right)
3294 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
3295 else if (can_turn_left)
3296 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
3297 else if (can_turn_right)
3298 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
3300 MovDir[x][y] = back_dir;
3302 MovDelay[x][y] = 16 + 16 * RND(3);
3304 else if (element == EL_PACMAN)
3306 boolean can_turn_left = PACMAN_CAN_ENTER_FIELD(left_x, left_y);
3307 boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(right_x, right_y);
3309 if (can_turn_left && can_turn_right)
3310 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
3311 else if (can_turn_left)
3312 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
3313 else if (can_turn_right)
3314 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
3316 MovDir[x][y] = back_dir;
3318 MovDelay[x][y] = 6 + RND(40);
3320 else if (element == EL_PIG)
3322 boolean can_turn_left = PIG_CAN_ENTER_FIELD(left_x, left_y);
3323 boolean can_turn_right = PIG_CAN_ENTER_FIELD(right_x, right_y);
3324 boolean can_move_on = PIG_CAN_ENTER_FIELD(move_x, move_y);
3325 boolean should_turn_left, should_turn_right, should_move_on;
3327 int rnd = RND(rnd_value);
3329 should_turn_left = (can_turn_left &&
3331 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
3332 y + back_dy + left_dy)));
3333 should_turn_right = (can_turn_right &&
3335 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
3336 y + back_dy + right_dy)));
3337 should_move_on = (can_move_on &&
3340 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
3341 y + move_dy + left_dy) ||
3342 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
3343 y + move_dy + right_dy)));
3345 if (should_turn_left || should_turn_right || should_move_on)
3347 if (should_turn_left && should_turn_right && should_move_on)
3348 MovDir[x][y] = (rnd < rnd_value / 3 ? left_dir :
3349 rnd < 2 * rnd_value / 3 ? right_dir :
3351 else if (should_turn_left && should_turn_right)
3352 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
3353 else if (should_turn_left && should_move_on)
3354 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
3355 else if (should_turn_right && should_move_on)
3356 MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
3357 else if (should_turn_left)
3358 MovDir[x][y] = left_dir;
3359 else if (should_turn_right)
3360 MovDir[x][y] = right_dir;
3361 else if (should_move_on)
3362 MovDir[x][y] = old_move_dir;
3364 else if (can_move_on && rnd > rnd_value / 8)
3365 MovDir[x][y] = old_move_dir;
3366 else if (can_turn_left && can_turn_right)
3367 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
3368 else if (can_turn_left && rnd > rnd_value / 8)
3369 MovDir[x][y] = left_dir;
3370 else if (can_turn_right && rnd > rnd_value/8)
3371 MovDir[x][y] = right_dir;
3373 MovDir[x][y] = back_dir;
3375 xx = x + move_xy[MovDir[x][y]].x;
3376 yy = y + move_xy[MovDir[x][y]].y;
3378 if (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy]))
3379 MovDir[x][y] = old_move_dir;
3383 else if (element == EL_DRAGON)
3385 boolean can_turn_left = IN_LEV_FIELD_AND_IS_FREE(left_x, left_y);
3386 boolean can_turn_right = IN_LEV_FIELD_AND_IS_FREE(right_x, right_y);
3387 boolean can_move_on = IN_LEV_FIELD_AND_IS_FREE(move_x, move_y);
3389 int rnd = RND(rnd_value);
3391 if (can_move_on && rnd > rnd_value / 8)
3392 MovDir[x][y] = old_move_dir;
3393 else if (can_turn_left && can_turn_right)
3394 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
3395 else if (can_turn_left && rnd > rnd_value / 8)
3396 MovDir[x][y] = left_dir;
3397 else if (can_turn_right && rnd > rnd_value / 8)
3398 MovDir[x][y] = right_dir;
3400 MovDir[x][y] = back_dir;
3402 xx = x + move_xy[MovDir[x][y]].x;
3403 yy = y + move_xy[MovDir[x][y]].y;
3405 if (!IS_FREE(xx, yy))
3406 MovDir[x][y] = old_move_dir;
3410 else if (element == EL_MOLE)
3412 boolean can_move_on =
3413 (MOLE_CAN_ENTER_FIELD(move_x, move_y,
3414 IS_AMOEBOID(Feld[move_x][move_y]) ||
3415 Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
3418 boolean can_turn_left =
3419 (MOLE_CAN_ENTER_FIELD(left_x, left_y,
3420 IS_AMOEBOID(Feld[left_x][left_y])));
3422 boolean can_turn_right =
3423 (MOLE_CAN_ENTER_FIELD(right_x, right_y,
3424 IS_AMOEBOID(Feld[right_x][right_y])));
3426 if (can_turn_left && can_turn_right)
3427 MovDir[x][y] = (RND(2) ? left_dir : right_dir);
3428 else if (can_turn_left)
3429 MovDir[x][y] = left_dir;
3431 MovDir[x][y] = right_dir;
3434 if (MovDir[x][y] != old_move_dir)
3437 else if (element == EL_BALLOON)
3439 MovDir[x][y] = game.balloon_dir;
3442 else if (element == EL_SPRING)
3444 if (MovDir[x][y] & MV_HORIZONTAL &&
3445 (!IN_LEV_FIELD_AND_IS_FREE(move_x, move_y) ||
3446 IN_LEV_FIELD_AND_IS_FREE(x, y + 1)))
3447 MovDir[x][y] = MV_NO_MOVING;
3451 else if (element == EL_ROBOT ||
3452 element == EL_SATELLITE ||
3453 element == EL_PENGUIN)
3455 int attr_x = -1, attr_y = -1;
3466 for (i=0; i<MAX_PLAYERS; i++)
3468 struct PlayerInfo *player = &stored_player[i];
3469 int jx = player->jx, jy = player->jy;
3471 if (!player->active)
3475 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
3483 if (element == EL_ROBOT && ZX >= 0 && ZY >= 0)
3489 if (element == EL_PENGUIN)
3492 static int xy[4][2] =
3502 int ex = x + xy[i % 4][0];
3503 int ey = y + xy[i % 4][1];
3505 if (IN_LEV_FIELD(ex, ey) && Feld[ex][ey] == EL_EXIT_OPEN)
3514 MovDir[x][y] = MV_NO_MOVING;
3516 MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
3517 else if (attr_x > x)
3518 MovDir[x][y] |= (AllPlayersGone ? MV_LEFT : MV_RIGHT);
3520 MovDir[x][y] |= (AllPlayersGone ? MV_DOWN : MV_UP);
3521 else if (attr_y > y)
3522 MovDir[x][y] |= (AllPlayersGone ? MV_UP : MV_DOWN);
3524 if (element == EL_ROBOT)
3528 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
3529 MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
3530 Moving2Blocked(x, y, &newx, &newy);
3532 if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
3533 MovDelay[x][y] = 8 + 8 * !RND(3);
3535 MovDelay[x][y] = 16;
3537 else if (element == EL_PENGUIN)
3543 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
3545 boolean first_horiz = RND(2);
3546 int new_move_dir = MovDir[x][y];
3549 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
3550 Moving2Blocked(x, y, &newx, &newy);
3552 if (PENGUIN_CAN_ENTER_FIELD(newx, newy))
3556 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
3557 Moving2Blocked(x, y, &newx, &newy);
3559 if (PENGUIN_CAN_ENTER_FIELD(newx, newy))
3562 MovDir[x][y] = old_move_dir;
3566 else /* (element == EL_SATELLITE) */
3572 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
3574 boolean first_horiz = RND(2);
3575 int new_move_dir = MovDir[x][y];
3578 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
3579 Moving2Blocked(x, y, &newx, &newy);
3581 if (ELEMENT_CAN_ENTER_FIELD_OR_ACID_2(newx, newy))
3585 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
3586 Moving2Blocked(x, y, &newx, &newy);
3588 if (ELEMENT_CAN_ENTER_FIELD_OR_ACID_2(newx, newy))
3591 MovDir[x][y] = old_move_dir;
3596 else if (element_info[element].move_pattern == MV_ALL_DIRECTIONS ||
3597 element_info[element].move_pattern == MV_TURNING_LEFT ||
3598 element_info[element].move_pattern == MV_TURNING_RIGHT)
3600 boolean can_turn_left = ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
3601 boolean can_turn_right = ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
3603 if (element_info[element].move_pattern == MV_TURNING_LEFT)
3604 MovDir[x][y] = left_dir;
3605 else if (element_info[element].move_pattern == MV_TURNING_RIGHT)
3606 MovDir[x][y] = right_dir;
3607 else if (can_turn_left && can_turn_right)
3608 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
3609 else if (can_turn_left)
3610 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
3611 else if (can_turn_right)
3612 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
3614 MovDir[x][y] = back_dir;
3616 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
3618 else if (element_info[element].move_pattern == MV_HORIZONTAL ||
3619 element_info[element].move_pattern == MV_VERTICAL)
3621 if (element_info[element].move_pattern & old_move_dir)
3622 MovDir[x][y] = back_dir;
3623 else if (element_info[element].move_pattern == MV_HORIZONTAL)
3624 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
3625 else if (element_info[element].move_pattern == MV_VERTICAL)
3626 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
3628 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
3630 else if (element_info[element].move_pattern & MV_ANY_DIRECTION)
3632 MovDir[x][y] = element_info[element].move_pattern;
3633 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
3635 else if (element_info[element].move_pattern == MV_ALONG_LEFT_SIDE)
3637 if (ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
3638 MovDir[x][y] = left_dir;
3639 else if (!ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
3640 MovDir[x][y] = right_dir;
3642 if (MovDir[x][y] != old_move_dir)
3643 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
3645 else if (element_info[element].move_pattern == MV_ALONG_RIGHT_SIDE)
3647 if (ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
3648 MovDir[x][y] = right_dir;
3649 else if (!ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
3650 MovDir[x][y] = left_dir;
3652 if (MovDir[x][y] != old_move_dir)
3653 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
3655 else if (element_info[element].move_pattern == MV_TOWARDS_PLAYER ||
3656 element_info[element].move_pattern == MV_AWAY_FROM_PLAYER)
3658 int attr_x = -1, attr_y = -1;
3661 (element_info[element].move_pattern == MV_AWAY_FROM_PLAYER);
3672 for (i=0; i<MAX_PLAYERS; i++)
3674 struct PlayerInfo *player = &stored_player[i];
3675 int jx = player->jx, jy = player->jy;
3677 if (!player->active)
3681 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
3689 MovDir[x][y] = MV_NO_MOVING;
3691 MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
3692 else if (attr_x > x)
3693 MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
3695 MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
3696 else if (attr_y > y)
3697 MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
3699 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
3701 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
3703 boolean first_horiz = RND(2);
3704 int new_move_dir = MovDir[x][y];
3707 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
3708 Moving2Blocked(x, y, &newx, &newy);
3710 if (ELEMENT_CAN_ENTER_FIELD_OR_ACID(element, newx, newy))
3714 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
3715 Moving2Blocked(x, y, &newx, &newy);
3717 if (ELEMENT_CAN_ENTER_FIELD_OR_ACID(element, newx, newy))
3720 MovDir[x][y] = old_move_dir;
3723 else if (element_info[element].move_pattern == MV_WHEN_PUSHED)
3725 if (!IN_LEV_FIELD_AND_IS_FREE(move_x, move_y))
3726 MovDir[x][y] = MV_NO_MOVING;
3732 static void TurnRound(int x, int y)
3734 int direction = MovDir[x][y];
3737 GfxDir[x][y] = MovDir[x][y];
3743 GfxDir[x][y] = MovDir[x][y];
3746 if (direction != MovDir[x][y])
3751 GfxAction[x][y] = ACTION_XXX_LEFT + MV_DIR_BIT(direction);
3754 GfxAction[x][y] = ACTION_WAITING;
3758 static boolean JustBeingPushed(int x, int y)
3762 for (i=0; i<MAX_PLAYERS; i++)
3764 struct PlayerInfo *player = &stored_player[i];
3766 if (player->active && player->is_pushing && player->MovPos)
3768 int next_jx = player->jx + (player->jx - player->last_jx);
3769 int next_jy = player->jy + (player->jy - player->last_jy);
3771 if (x == next_jx && y == next_jy)
3779 void StartMoving(int x, int y)
3781 boolean use_spring_bug = (game.engine_version < VERSION_IDENT(2,2,0,0));
3782 boolean started_moving = FALSE; /* some elements can fall _and_ move */
3783 int element = Feld[x][y];
3789 /* !!! this should be handled more generic (not only for mole) !!! */
3790 if (MovDelay[x][y] == 0)
3791 GfxAction[x][y] = ACTION_DEFAULT;
3793 /* !!! this should be handled more generic (not only for mole) !!! */
3794 if (element != EL_MOLE && GfxAction[x][y] != ACTION_DIGGING)
3795 GfxAction[x][y] = ACTION_DEFAULT;
3798 if (CAN_FALL(element) && y < lev_fieldy - 1)
3800 if ((x > 0 && IS_PLAYER(x - 1, y)) ||
3801 (x < lev_fieldx-1 && IS_PLAYER(x + 1, y)))
3802 if (JustBeingPushed(x, y))
3805 if (element == EL_QUICKSAND_FULL)
3807 if (IS_FREE(x, y + 1))
3809 InitMovingField(x, y, MV_DOWN);
3810 started_moving = TRUE;
3812 Feld[x][y] = EL_QUICKSAND_EMPTYING;
3813 Store[x][y] = EL_ROCK;
3815 PlaySoundLevelAction(x, y, ACTION_EMPTYING);
3817 PlaySoundLevel(x, y, SND_QUICKSAND_EMPTYING);
3820 else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
3822 if (!MovDelay[x][y])
3823 MovDelay[x][y] = TILEY + 1;
3832 Feld[x][y] = EL_QUICKSAND_EMPTY;
3833 Feld[x][y + 1] = EL_QUICKSAND_FULL;
3834 Store[x][y + 1] = Store[x][y];
3837 PlaySoundLevelAction(x, y, ACTION_FILLING);
3839 PlaySoundLevel(x, y, SND_QUICKSAND_FILLING);
3843 else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
3844 Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
3846 InitMovingField(x, y, MV_DOWN);
3847 started_moving = TRUE;
3849 Feld[x][y] = EL_QUICKSAND_FILLING;
3850 Store[x][y] = element;
3852 PlaySoundLevelAction(x, y, ACTION_FILLING);
3854 PlaySoundLevel(x, y, SND_QUICKSAND_FILLING);
3857 else if (element == EL_MAGIC_WALL_FULL)
3859 if (IS_FREE(x, y + 1))
3861 InitMovingField(x, y, MV_DOWN);
3862 started_moving = TRUE;
3864 Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
3865 Store[x][y] = EL_CHANGED(Store[x][y]);
3867 else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
3869 if (!MovDelay[x][y])
3870 MovDelay[x][y] = TILEY/4 + 1;
3879 Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
3880 Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
3881 Store[x][y + 1] = EL_CHANGED(Store[x][y]);
3885 else if (element == EL_BD_MAGIC_WALL_FULL)
3887 if (IS_FREE(x, y + 1))
3889 InitMovingField(x, y, MV_DOWN);
3890 started_moving = TRUE;
3892 Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
3893 Store[x][y] = EL_CHANGED2(Store[x][y]);
3895 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
3897 if (!MovDelay[x][y])
3898 MovDelay[x][y] = TILEY/4 + 1;
3907 Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
3908 Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
3909 Store[x][y + 1] = EL_CHANGED2(Store[x][y]);
3913 else if (CAN_PASS_MAGIC_WALL(element) &&
3914 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
3915 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE))
3917 InitMovingField(x, y, MV_DOWN);
3918 started_moving = TRUE;
3921 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
3922 EL_BD_MAGIC_WALL_FILLING);
3923 Store[x][y] = element;
3926 else if (CAN_SMASH(element) && Feld[x][y + 1] == EL_ACID)
3928 else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
3933 InitMovingField(x, y, MV_DOWN);
3934 started_moving = TRUE;
3936 Store[x][y] = EL_ACID;
3938 /* !!! TEST !!! better use "_FALLING" etc. !!! */
3939 GfxAction[x][y + 1] = ACTION_ACTIVE;
3943 else if ((game.engine_version < VERSION_IDENT(2,2,0,7) &&
3944 CAN_SMASH(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
3945 (Feld[x][y + 1] == EL_BLOCKED)) ||
3946 (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
3947 CAN_SMASH(element) && WasJustFalling[x][y] &&
3948 (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))))
3952 else if (game.engine_version < VERSION_IDENT(2,2,0,7) &&
3953 CAN_SMASH(element) && Feld[x][y + 1] == EL_BLOCKED &&
3954 WasJustMoving[x][y] && !Pushed[x][y + 1])
3956 else if (CAN_SMASH(element) && Feld[x][y + 1] == EL_BLOCKED &&
3957 WasJustMoving[x][y])
3962 /* this is needed for a special case not covered by calling "Impact()"
3963 from "ContinueMoving()": if an element moves to a tile directly below
3964 another element which was just falling on that tile (which was empty
3965 in the previous frame), the falling element above would just stop
3966 instead of smashing the element below (in previous version, the above
3967 element was just checked for "moving" instead of "falling", resulting
3968 in incorrect smashes caused by horizontal movement of the above
3969 element; also, the case of the player being the element to smash was
3970 simply not covered here... :-/ ) */
3974 else if (IS_FREE(x, y + 1) && element == EL_SPRING && use_spring_bug)
3976 if (MovDir[x][y] == MV_NO_MOVING)
3978 InitMovingField(x, y, MV_DOWN);
3979 started_moving = TRUE;
3982 else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
3984 if (WasJustFalling[x][y]) /* prevent animation from being restarted */
3985 MovDir[x][y] = MV_DOWN;
3987 InitMovingField(x, y, MV_DOWN);
3988 started_moving = TRUE;
3990 else if (element == EL_AMOEBA_DROP)
3992 Feld[x][y] = EL_AMOEBA_GROWING;
3993 Store[x][y] = EL_AMOEBA_WET;
3995 /* Store[x][y + 1] must be zero, because:
3996 (EL_QUICKSAND_FULL -> EL_ROCK): Store[x][y + 1] == EL_QUICKSAND_EMPTY
3999 #if OLD_GAME_BEHAVIOUR
4000 else if (IS_SLIPPERY(Feld[x][y + 1]) && !Store[x][y + 1])
4002 else if (IS_SLIPPERY(Feld[x][y + 1]) && !Store[x][y + 1] &&
4003 !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
4004 element != EL_DX_SUPABOMB)
4007 else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
4008 (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
4009 !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
4010 element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
4013 boolean can_fall_left = (x > 0 && IS_FREE(x - 1, y) &&
4014 (IS_FREE(x - 1, y + 1) ||
4015 Feld[x - 1][y + 1] == EL_ACID));
4016 boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
4017 (IS_FREE(x + 1, y + 1) ||
4018 Feld[x + 1][y + 1] == EL_ACID));
4019 boolean can_fall_any = (can_fall_left || can_fall_right);
4020 boolean can_fall_both = (can_fall_left && can_fall_right);
4022 if (can_fall_any && IS_CUSTOM_ELEMENT(Feld[x][y + 1]))
4024 int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
4026 if (slippery_type == SLIPPERY_ONLY_LEFT)
4027 can_fall_right = FALSE;
4028 else if (slippery_type == SLIPPERY_ONLY_RIGHT)
4029 can_fall_left = FALSE;
4030 else if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
4031 can_fall_right = FALSE;
4032 else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
4033 can_fall_left = FALSE;
4035 can_fall_any = (can_fall_left || can_fall_right);
4036 can_fall_both = (can_fall_left && can_fall_right);
4041 if (can_fall_both &&
4042 (game.emulation != EMU_BOULDERDASH &&
4043 element != EL_BD_ROCK && element != EL_BD_DIAMOND))
4044 can_fall_left = !(can_fall_right = RND(2));
4046 InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
4047 started_moving = TRUE;
4050 else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
4052 boolean left_is_free = (x > 0 && IS_FREE(x - 1, y));
4053 boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
4054 int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
4055 int belt_dir = game.belt_dir[belt_nr];
4057 if ((belt_dir == MV_LEFT && left_is_free) ||
4058 (belt_dir == MV_RIGHT && right_is_free))
4060 InitMovingField(x, y, belt_dir);
4061 started_moving = TRUE;
4063 GfxAction[x][y] = ACTION_DEFAULT;
4068 /* not "else if" because of elements that can fall and move (EL_SPRING) */
4069 if (CAN_MOVE(element) && !started_moving)
4074 if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
4077 if ((element == EL_SATELLITE ||
4078 element == EL_BALLOON ||
4079 element == EL_SPRING)
4080 && JustBeingPushed(x, y))
4086 if (element == EL_SPRING && MovDir[x][y] == MV_DOWN)
4087 Feld[x][y + 1] = EL_EMPTY; /* was set to EL_BLOCKED above */
4089 if (element == EL_SPRING && MovDir[x][y] != MV_NO_MOVING)
4091 Moving2Blocked(x, y, &newx, &newy);
4092 if (Feld[newx][newy] == EL_BLOCKED)
4093 Feld[newx][newy] = EL_EMPTY; /* was set to EL_BLOCKED above */
4098 if (!MovDelay[x][y]) /* start new movement phase */
4100 /* all objects that can change their move direction after each step
4101 (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall */
4103 if (element != EL_YAMYAM &&
4104 element != EL_DARK_YAMYAM &&
4105 element != EL_PACMAN &&
4106 !(element_info[element].move_pattern & MV_ANY_DIRECTION) &&
4107 element_info[element].move_pattern != MV_TURNING_LEFT &&
4108 element_info[element].move_pattern != MV_TURNING_RIGHT)
4112 if (MovDelay[x][y] && (element == EL_BUG ||
4113 element == EL_SPACESHIP ||
4114 element == EL_SP_SNIKSNAK ||
4115 element == EL_SP_ELECTRON ||
4116 element == EL_MOLE))
4117 DrawLevelField(x, y);
4121 if (MovDelay[x][y]) /* wait some time before next movement */
4126 if (element == EL_YAMYAM)
4129 el_act_dir2img(EL_YAMYAM, ACTION_WAITING, MV_LEFT));
4130 DrawLevelElementAnimation(x, y, element);
4134 if (MovDelay[x][y]) /* element still has to wait some time */
4137 /* !!! PLACE THIS SOMEWHERE AFTER "TurnRound()" !!! */
4138 ResetGfxAnimation(x, y);
4142 if (GfxAction[x][y] != ACTION_WAITING)
4143 printf("::: %d: %d != ACTION_WAITING\n", element, GfxAction[x][y]);
4145 GfxAction[x][y] = ACTION_WAITING;
4149 if (element == EL_ROBOT ||
4151 element == EL_PACMAN ||
4153 element == EL_YAMYAM ||
4154 element == EL_DARK_YAMYAM)
4157 DrawLevelElementAnimation(x, y, element);
4159 DrawLevelElementAnimationIfNeeded(x, y, element);
4161 PlaySoundLevelAction(x, y, ACTION_WAITING);
4163 else if (element == EL_SP_ELECTRON)
4164 DrawLevelElementAnimationIfNeeded(x, y, element);
4165 else if (element == EL_DRAGON)
4168 int dir = MovDir[x][y];
4169 int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
4170 int dy = (dir == MV_UP ? -1 : dir == MV_DOWN ? +1 : 0);
4171 int graphic = (dir == MV_LEFT ? IMG_FLAMES_1_LEFT :
4172 dir == MV_RIGHT ? IMG_FLAMES_1_RIGHT :
4173 dir == MV_UP ? IMG_FLAMES_1_UP :
4174 dir == MV_DOWN ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
4175 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
4178 printf("::: %d, %d\n", GfxAction[x][y], GfxFrame[x][y]);
4181 GfxAction[x][y] = ACTION_ATTACKING;
4183 if (IS_PLAYER(x, y))
4184 DrawPlayerField(x, y);
4186 DrawLevelField(x, y);
4188 PlaySoundLevelActionIfLoop(x, y, ACTION_ATTACKING);
4190 for (i=1; i <= 3; i++)
4192 int xx = x + i * dx;
4193 int yy = y + i * dy;
4194 int sx = SCREENX(xx);
4195 int sy = SCREENY(yy);
4196 int flame_graphic = graphic + (i - 1);
4198 if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
4203 int flamed = MovingOrBlocked2Element(xx, yy);
4205 if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_FIRE(flamed))
4208 RemoveMovingField(xx, yy);
4210 Feld[xx][yy] = EL_FLAMES;
4211 if (IN_SCR_FIELD(sx, sy))
4213 DrawLevelFieldCrumbledSand(xx, yy);
4214 DrawGraphic(sx, sy, flame_graphic, frame);
4219 if (Feld[xx][yy] == EL_FLAMES)
4220 Feld[xx][yy] = EL_EMPTY;
4221 DrawLevelField(xx, yy);
4226 if (MovDelay[x][y]) /* element still has to wait some time */
4228 PlaySoundLevelAction(x, y, ACTION_WAITING);
4234 /* special case of "moving" animation of waiting elements (FIX THIS !!!);
4235 for all other elements GfxAction will be set by InitMovingField() */
4236 if (element == EL_BD_BUTTERFLY || element == EL_BD_FIREFLY)
4237 GfxAction[x][y] = ACTION_MOVING;
4241 /* now make next step */
4243 Moving2Blocked(x, y, &newx, &newy); /* get next screen position */
4245 if (DONT_COLLIDE_WITH(element) &&
4246 IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
4247 !PLAYER_PROTECTED(newx, newy))
4250 TestIfBadThingRunsIntoHero(x, y, MovDir[x][y]);
4253 /* player killed by element which is deadly when colliding with */
4255 KillHero(PLAYERINFO(newx, newy));
4260 else if ((element == EL_PENGUIN ||
4261 element == EL_ROBOT ||
4262 element == EL_SATELLITE ||
4263 element == EL_BALLOON ||
4264 IS_CUSTOM_ELEMENT(element)) &&
4265 IN_LEV_FIELD(newx, newy) &&
4266 MovDir[x][y] == MV_DOWN && Feld[newx][newy] == EL_ACID)
4269 Store[x][y] = EL_ACID;
4271 else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
4273 if (Feld[newx][newy] == EL_EXIT_OPEN)
4275 Feld[x][y] = EL_EMPTY;
4276 DrawLevelField(x, y);
4278 PlaySoundLevel(newx, newy, SND_PENGUIN_PASSING);
4279 if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
4280 DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
4282 local_player->friends_still_needed--;
4283 if (!local_player->friends_still_needed &&
4284 !local_player->GameOver && AllPlayersGone)
4285 local_player->LevelSolved = local_player->GameOver = TRUE;
4289 else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
4291 if (DigField(local_player, newx, newy, 0, 0, DF_DIG) == MF_MOVING)
4292 DrawLevelField(newx, newy);
4294 GfxDir[x][y] = MovDir[x][y] = MV_NO_MOVING;
4296 else if (!IS_FREE(newx, newy))
4298 GfxAction[x][y] = ACTION_WAITING;
4300 if (IS_PLAYER(x, y))
4301 DrawPlayerField(x, y);
4303 DrawLevelField(x, y);
4307 else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
4309 if (IS_FOOD_PIG(Feld[newx][newy]))
4311 if (IS_MOVING(newx, newy))
4312 RemoveMovingField(newx, newy);
4315 Feld[newx][newy] = EL_EMPTY;
4316 DrawLevelField(newx, newy);
4319 PlaySoundLevel(x, y, SND_PIG_DIGGING);
4321 else if (!IS_FREE(newx, newy))
4323 if (IS_PLAYER(x, y))
4324 DrawPlayerField(x, y);
4326 DrawLevelField(x, y);
4330 else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
4332 if (!IS_FREE(newx, newy))
4334 if (IS_PLAYER(x, y))
4335 DrawPlayerField(x, y);
4337 DrawLevelField(x, y);
4343 boolean wanna_flame = !RND(10);
4344 int dx = newx - x, dy = newy - y;
4345 int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
4346 int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
4347 int element1 = (IN_LEV_FIELD(newx1, newy1) ?
4348 MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
4349 int element2 = (IN_LEV_FIELD(newx2, newy2) ?
4350 MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
4353 IS_CLASSIC_ENEMY(element1) ||
4354 IS_CLASSIC_ENEMY(element2)) &&
4355 element1 != EL_DRAGON && element2 != EL_DRAGON &&
4356 element1 != EL_FLAMES && element2 != EL_FLAMES)
4359 ResetGfxAnimation(x, y);
4360 GfxAction[x][y] = ACTION_ATTACKING;
4363 if (IS_PLAYER(x, y))
4364 DrawPlayerField(x, y);
4366 DrawLevelField(x, y);
4368 PlaySoundLevel(x, y, SND_DRAGON_ATTACKING);
4370 MovDelay[x][y] = 50;
4372 Feld[newx][newy] = EL_FLAMES;
4373 if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
4374 Feld[newx1][newy1] = EL_FLAMES;
4375 if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
4376 Feld[newx2][newy2] = EL_FLAMES;
4382 else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
4383 Feld[newx][newy] == EL_DIAMOND)
4385 if (IS_MOVING(newx, newy))
4386 RemoveMovingField(newx, newy);
4389 Feld[newx][newy] = EL_EMPTY;
4390 DrawLevelField(newx, newy);
4393 PlaySoundLevel(x, y, SND_YAMYAM_DIGGING);
4395 else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
4396 IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
4398 if (AmoebaNr[newx][newy])
4400 AmoebaCnt2[AmoebaNr[newx][newy]]--;
4401 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
4402 Feld[newx][newy] == EL_BD_AMOEBA)
4403 AmoebaCnt[AmoebaNr[newx][newy]]--;
4406 if (IS_MOVING(newx, newy))
4407 RemoveMovingField(newx, newy);
4410 Feld[newx][newy] = EL_EMPTY;
4411 DrawLevelField(newx, newy);
4414 PlaySoundLevel(x, y, SND_DARK_YAMYAM_DIGGING);
4416 else if ((element == EL_PACMAN || element == EL_MOLE)
4417 && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
4419 if (AmoebaNr[newx][newy])
4421 AmoebaCnt2[AmoebaNr[newx][newy]]--;
4422 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
4423 Feld[newx][newy] == EL_BD_AMOEBA)
4424 AmoebaCnt[AmoebaNr[newx][newy]]--;
4427 if (element == EL_MOLE)
4429 Feld[newx][newy] = EL_AMOEBA_SHRINKING;
4430 PlaySoundLevel(x, y, SND_MOLE_DIGGING);
4432 ResetGfxAnimation(x, y);
4433 GfxAction[x][y] = ACTION_DIGGING;
4434 DrawLevelField(x, y);
4436 MovDelay[newx][newy] = 0; /* start amoeba shrinking delay */
4437 return; /* wait for shrinking amoeba */
4439 else /* element == EL_PACMAN */
4441 Feld[newx][newy] = EL_EMPTY;
4442 DrawLevelField(newx, newy);
4443 PlaySoundLevel(x, y, SND_PACMAN_DIGGING);
4446 else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
4447 (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
4448 (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
4450 /* wait for shrinking amoeba to completely disappear */
4453 else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
4455 /* object was running against a wall */
4460 if (GFX_ELEMENT(element) != EL_SAND) /* !!! FIX THIS (crumble) !!! */
4461 DrawLevelElementAnimation(x, y, element);
4463 if (element == EL_BUG ||
4464 element == EL_SPACESHIP ||
4465 element == EL_SP_SNIKSNAK)
4466 DrawLevelField(x, y);
4467 else if (element == EL_MOLE)
4468 DrawLevelField(x, y);
4469 else if (element == EL_BD_BUTTERFLY ||
4470 element == EL_BD_FIREFLY)
4471 DrawLevelElementAnimationIfNeeded(x, y, element);
4472 else if (element == EL_SATELLITE)
4473 DrawLevelElementAnimationIfNeeded(x, y, element);
4474 else if (element == EL_SP_ELECTRON)
4475 DrawLevelElementAnimationIfNeeded(x, y, element);
4478 if (DONT_TOUCH(element))
4479 TestIfBadThingTouchesHero(x, y);
4482 PlaySoundLevelAction(x, y, ACTION_WAITING);
4488 InitMovingField(x, y, MovDir[x][y]);
4490 PlaySoundLevelAction(x, y, ACTION_MOVING);
4494 ContinueMoving(x, y);
4497 void ContinueMoving(int x, int y)
4499 int element = Feld[x][y];
4500 int direction = MovDir[x][y];
4501 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
4502 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
4503 int newx = x + dx, newy = y + dy;
4504 int nextx = newx + dx, nexty = newy + dy;
4505 boolean pushed = Pushed[x][y];
4507 MovPos[x][y] += getElementMoveStepsize(x, y);
4509 if (pushed) /* special case: moving object pushed by player */
4510 MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
4512 if (ABS(MovPos[x][y]) < TILEX)
4514 DrawLevelField(x, y);
4516 return; /* element is still moving */
4519 /* element reached destination field */
4521 Feld[x][y] = EL_EMPTY;
4522 Feld[newx][newy] = element;
4523 MovPos[x][y] = 0; /* force "not moving" for "crumbled sand" */
4525 if (element == EL_MOLE)
4527 Feld[x][y] = EL_SAND;
4529 DrawLevelFieldCrumbledSandNeighbours(x, y);
4531 else if (element == EL_QUICKSAND_FILLING)
4533 element = Feld[newx][newy] = get_next_element(element);
4534 Store[newx][newy] = Store[x][y];
4536 else if (element == EL_QUICKSAND_EMPTYING)
4538 Feld[x][y] = get_next_element(element);
4539 element = Feld[newx][newy] = Store[x][y];
4541 else if (element == EL_MAGIC_WALL_FILLING)
4543 element = Feld[newx][newy] = get_next_element(element);
4544 if (!game.magic_wall_active)
4545 element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
4546 Store[newx][newy] = Store[x][y];
4548 else if (element == EL_MAGIC_WALL_EMPTYING)
4550 Feld[x][y] = get_next_element(element);
4551 if (!game.magic_wall_active)
4552 Feld[x][y] = EL_MAGIC_WALL_DEAD;
4553 element = Feld[newx][newy] = Store[x][y];
4555 else if (element == EL_BD_MAGIC_WALL_FILLING)
4557 element = Feld[newx][newy] = get_next_element(element);
4558 if (!game.magic_wall_active)
4559 element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
4560 Store[newx][newy] = Store[x][y];
4562 else if (element == EL_BD_MAGIC_WALL_EMPTYING)
4564 Feld[x][y] = get_next_element(element);
4565 if (!game.magic_wall_active)
4566 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
4567 element = Feld[newx][newy] = Store[x][y];
4569 else if (element == EL_AMOEBA_DROPPING)
4571 Feld[x][y] = get_next_element(element);
4572 element = Feld[newx][newy] = Store[x][y];
4574 else if (element == EL_SOKOBAN_OBJECT)
4577 Feld[x][y] = Back[x][y];
4579 if (Back[newx][newy])
4580 Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
4582 Back[x][y] = Back[newx][newy] = 0;
4584 else if (Store[x][y] == EL_ACID)
4586 element = Feld[newx][newy] = EL_ACID;
4590 MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
4591 MovDelay[newx][newy] = 0;
4593 /* copy element change control values to new field */
4594 ChangeDelay[newx][newy] = ChangeDelay[x][y];
4595 ChangePage[newx][newy] = ChangePage[x][y];
4596 Changed[newx][newy] = Changed[x][y];
4597 ChangeEvent[newx][newy] = ChangeEvent[x][y];
4599 ChangeDelay[x][y] = 0;
4600 ChangePage[x][y] = -1;
4601 Changed[x][y] = CE_BITMASK_DEFAULT;
4602 ChangeEvent[x][y] = CE_BITMASK_DEFAULT;
4604 /* copy animation control values to new field */
4605 GfxFrame[newx][newy] = GfxFrame[x][y];
4606 GfxRandom[newx][newy] = GfxRandom[x][y]; /* keep same random value */
4607 GfxAction[newx][newy] = GfxAction[x][y]; /* keep action one frame */
4608 GfxDir[newx][newy] = GfxDir[x][y]; /* keep element direction */
4610 Pushed[x][y] = Pushed[newx][newy] = FALSE;
4612 ResetGfxAnimation(x, y); /* reset animation values for old field */
4615 /* 2.1.1 (does not work correctly for spring) */
4616 if (!CAN_MOVE(element))
4617 MovDir[newx][newy] = 0;
4621 /* (does not work for falling objects that slide horizontally) */
4622 if (CAN_FALL(element) && MovDir[newx][newy] == MV_DOWN)
4623 MovDir[newx][newy] = 0;
4626 if (!CAN_MOVE(element) ||
4627 (element == EL_SPRING && MovDir[newx][newy] == MV_DOWN))
4628 MovDir[newx][newy] = 0;
4631 if (!CAN_MOVE(element) ||
4632 (CAN_FALL(element) && direction == MV_DOWN))
4633 GfxDir[x][y] = MovDir[newx][newy] = 0;
4638 DrawLevelField(x, y);
4639 DrawLevelField(newx, newy);
4641 Stop[newx][newy] = TRUE; /* ignore this element until the next frame */
4643 /* prevent pushed element from moving on in pushed direction */
4644 if (pushed && CAN_MOVE(element) &&
4645 element_info[element].move_pattern & MV_ANY_DIRECTION &&
4646 !(element_info[element].move_pattern & direction))
4647 TurnRound(newx, newy);
4649 if (!pushed) /* special case: moving object pushed by player */
4651 WasJustMoving[newx][newy] = 3;
4653 if (CAN_FALL(element) && direction == MV_DOWN)
4654 WasJustFalling[newx][newy] = 3;
4657 if (DONT_TOUCH(element)) /* object may be nasty to player or others */
4659 TestIfBadThingTouchesHero(newx, newy);
4660 TestIfBadThingTouchesFriend(newx, newy);
4661 TestIfBadThingTouchesOtherBadThing(newx, newy);
4663 else if (element == EL_PENGUIN)
4664 TestIfFriendTouchesBadThing(newx, newy);
4666 if (CAN_FALL(element) && direction == MV_DOWN &&
4667 (newy == lev_fieldy - 1 || !IS_FREE(x, newy + 1)))
4671 TestIfElementTouchesCustomElement(x, y); /* for empty space */
4675 if (ChangePage[newx][newy] != -1) /* delayed change */
4676 ChangeElement(newx, newy, ChangePage[newx][newy]);
4679 if (!IN_LEV_FIELD(nextx, nexty) || !IS_FREE(nextx, nexty))
4680 CheckElementSideChange(newx, newy, Feld[newx][newy], direction,
4683 TestIfPlayerTouchesCustomElement(newx, newy);
4684 TestIfElementTouchesCustomElement(newx, newy);
4687 int AmoebeNachbarNr(int ax, int ay)
4690 int element = Feld[ax][ay];
4692 static int xy[4][2] =
4702 int x = ax + xy[i][0];
4703 int y = ay + xy[i][1];
4705 if (!IN_LEV_FIELD(x, y))
4708 if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
4709 group_nr = AmoebaNr[x][y];
4715 void AmoebenVereinigen(int ax, int ay)
4717 int i, x, y, xx, yy;
4718 int new_group_nr = AmoebaNr[ax][ay];
4719 static int xy[4][2] =
4727 if (new_group_nr == 0)
4735 if (!IN_LEV_FIELD(x, y))
4738 if ((Feld[x][y] == EL_AMOEBA_FULL ||
4739 Feld[x][y] == EL_BD_AMOEBA ||
4740 Feld[x][y] == EL_AMOEBA_DEAD) &&
4741 AmoebaNr[x][y] != new_group_nr)
4743 int old_group_nr = AmoebaNr[x][y];
4745 if (old_group_nr == 0)
4748 AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
4749 AmoebaCnt[old_group_nr] = 0;
4750 AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
4751 AmoebaCnt2[old_group_nr] = 0;
4753 for (yy=0; yy<lev_fieldy; yy++)
4755 for (xx=0; xx<lev_fieldx; xx++)
4757 if (AmoebaNr[xx][yy] == old_group_nr)
4758 AmoebaNr[xx][yy] = new_group_nr;
4765 void AmoebeUmwandeln(int ax, int ay)
4769 if (Feld[ax][ay] == EL_AMOEBA_DEAD)
4771 int group_nr = AmoebaNr[ax][ay];
4776 printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
4777 printf("AmoebeUmwandeln(): This should never happen!\n");
4782 for (y=0; y<lev_fieldy; y++)
4784 for (x=0; x<lev_fieldx; x++)
4786 if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
4789 Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
4793 PlaySoundLevel(ax, ay, (IS_GEM(level.amoeba_content) ?
4794 SND_AMOEBA_TURNING_TO_GEM :
4795 SND_AMOEBA_TURNING_TO_ROCK));
4800 static int xy[4][2] =
4813 if (!IN_LEV_FIELD(x, y))
4816 if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
4818 PlaySoundLevel(x, y, (IS_GEM(level.amoeba_content) ?
4819 SND_AMOEBA_TURNING_TO_GEM :
4820 SND_AMOEBA_TURNING_TO_ROCK));
4827 void AmoebeUmwandelnBD(int ax, int ay, int new_element)
4830 int group_nr = AmoebaNr[ax][ay];
4831 boolean done = FALSE;
4836 printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
4837 printf("AmoebeUmwandelnBD(): This should never happen!\n");
4842 for (y=0; y<lev_fieldy; y++)
4844 for (x=0; x<lev_fieldx; x++)
4846 if (AmoebaNr[x][y] == group_nr &&
4847 (Feld[x][y] == EL_AMOEBA_DEAD ||
4848 Feld[x][y] == EL_BD_AMOEBA ||
4849 Feld[x][y] == EL_AMOEBA_GROWING))
4852 Feld[x][y] = new_element;
4853 InitField(x, y, FALSE);
4854 DrawLevelField(x, y);
4861 PlaySoundLevel(ax, ay, (new_element == EL_BD_ROCK ?
4862 SND_BD_AMOEBA_TURNING_TO_ROCK :
4863 SND_BD_AMOEBA_TURNING_TO_GEM));
4866 void AmoebeWaechst(int x, int y)
4868 static unsigned long sound_delay = 0;
4869 static unsigned long sound_delay_value = 0;
4871 if (!MovDelay[x][y]) /* start new growing cycle */
4875 if (DelayReached(&sound_delay, sound_delay_value))
4878 PlaySoundLevelElementAction(x, y, Store[x][y], ACTION_GROWING);
4880 if (Store[x][y] == EL_BD_AMOEBA)
4881 PlaySoundLevel(x, y, SND_BD_AMOEBA_GROWING);
4883 PlaySoundLevel(x, y, SND_AMOEBA_GROWING);
4885 sound_delay_value = 30;
4889 if (MovDelay[x][y]) /* wait some time before growing bigger */
4892 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
4894 int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
4895 6 - MovDelay[x][y]);
4897 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
4900 if (!MovDelay[x][y])
4902 Feld[x][y] = Store[x][y];
4904 DrawLevelField(x, y);
4909 void AmoebaDisappearing(int x, int y)
4911 static unsigned long sound_delay = 0;
4912 static unsigned long sound_delay_value = 0;
4914 if (!MovDelay[x][y]) /* start new shrinking cycle */
4918 if (DelayReached(&sound_delay, sound_delay_value))
4919 sound_delay_value = 30;
4922 if (MovDelay[x][y]) /* wait some time before shrinking */
4925 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
4927 int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
4928 6 - MovDelay[x][y]);
4930 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
4933 if (!MovDelay[x][y])
4935 Feld[x][y] = EL_EMPTY;
4936 DrawLevelField(x, y);
4938 /* don't let mole enter this field in this cycle;
4939 (give priority to objects falling to this field from above) */
4945 void AmoebeAbleger(int ax, int ay)
4948 int element = Feld[ax][ay];
4949 int graphic = el2img(element);
4950 int newax = ax, neway = ay;
4951 static int xy[4][2] =
4959 if (!level.amoeba_speed)
4961 Feld[ax][ay] = EL_AMOEBA_DEAD;
4962 DrawLevelField(ax, ay);
4966 if (IS_ANIMATED(graphic))
4967 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
4969 if (!MovDelay[ax][ay]) /* start making new amoeba field */
4970 MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
4972 if (MovDelay[ax][ay]) /* wait some time before making new amoeba */
4975 if (MovDelay[ax][ay])
4979 if (element == EL_AMOEBA_WET) /* object is an acid / amoeba drop */
4982 int x = ax + xy[start][0];
4983 int y = ay + xy[start][1];
4985 if (!IN_LEV_FIELD(x, y))
4988 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
4989 if (IS_FREE(x, y) ||
4990 Feld[x][y] == EL_SAND || Feld[x][y] == EL_QUICKSAND_EMPTY)
4996 if (newax == ax && neway == ay)
4999 else /* normal or "filled" (BD style) amoeba */
5002 boolean waiting_for_player = FALSE;
5006 int j = (start + i) % 4;
5007 int x = ax + xy[j][0];
5008 int y = ay + xy[j][1];
5010 if (!IN_LEV_FIELD(x, y))
5013 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
5014 if (IS_FREE(x, y) ||
5015 Feld[x][y] == EL_SAND || Feld[x][y] == EL_QUICKSAND_EMPTY)
5021 else if (IS_PLAYER(x, y))
5022 waiting_for_player = TRUE;
5025 if (newax == ax && neway == ay) /* amoeba cannot grow */
5027 if (i == 4 && (!waiting_for_player || game.emulation == EMU_BOULDERDASH))
5029 Feld[ax][ay] = EL_AMOEBA_DEAD;
5030 DrawLevelField(ax, ay);
5031 AmoebaCnt[AmoebaNr[ax][ay]]--;
5033 if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0) /* amoeba is completely dead */
5035 if (element == EL_AMOEBA_FULL)
5036 AmoebeUmwandeln(ax, ay);
5037 else if (element == EL_BD_AMOEBA)
5038 AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
5043 else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
5045 /* amoeba gets larger by growing in some direction */
5047 int new_group_nr = AmoebaNr[ax][ay];
5050 if (new_group_nr == 0)
5052 printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
5053 printf("AmoebeAbleger(): This should never happen!\n");
5058 AmoebaNr[newax][neway] = new_group_nr;
5059 AmoebaCnt[new_group_nr]++;
5060 AmoebaCnt2[new_group_nr]++;
5062 /* if amoeba touches other amoeba(s) after growing, unify them */
5063 AmoebenVereinigen(newax, neway);
5065 if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
5067 AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
5073 if (element != EL_AMOEBA_WET || neway < ay || !IS_FREE(newax, neway) ||
5074 (neway == lev_fieldy - 1 && newax != ax))
5076 Feld[newax][neway] = EL_AMOEBA_GROWING; /* creation of new amoeba */
5077 Store[newax][neway] = element;
5079 else if (neway == ay)
5081 Feld[newax][neway] = EL_AMOEBA_DROP; /* drop left/right of amoeba */
5083 PlaySoundLevelAction(newax, neway, ACTION_GROWING);
5085 PlaySoundLevel(newax, neway, SND_AMOEBA_GROWING);
5090 InitMovingField(ax, ay, MV_DOWN); /* drop dripping from amoeba */
5091 Feld[ax][ay] = EL_AMOEBA_DROPPING;
5092 Store[ax][ay] = EL_AMOEBA_DROP;
5093 ContinueMoving(ax, ay);
5097 DrawLevelField(newax, neway);
5100 void Life(int ax, int ay)
5103 static int life[4] = { 2, 3, 3, 3 }; /* parameters for "game of life" */
5105 int element = Feld[ax][ay];
5106 int graphic = el2img(element);
5107 boolean changed = FALSE;
5109 if (IS_ANIMATED(graphic))
5110 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
5115 if (!MovDelay[ax][ay]) /* start new "game of life" cycle */
5116 MovDelay[ax][ay] = life_time;
5118 if (MovDelay[ax][ay]) /* wait some time before next cycle */
5121 if (MovDelay[ax][ay])
5125 for (y1=-1; y1<2; y1++) for(x1=-1; x1<2; x1++)
5127 int xx = ax+x1, yy = ay+y1;
5130 if (!IN_LEV_FIELD(xx, yy))
5133 for (y2=-1; y2<2; y2++) for (x2=-1; x2<2; x2++)
5135 int x = xx+x2, y = yy+y2;
5137 if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
5140 if (((Feld[x][y] == element ||
5141 (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y))) &&
5143 (IS_FREE(x, y) && Stop[x][y]))
5147 if (xx == ax && yy == ay) /* field in the middle */
5149 if (nachbarn < life[0] || nachbarn > life[1])
5151 Feld[xx][yy] = EL_EMPTY;
5153 DrawLevelField(xx, yy);
5154 Stop[xx][yy] = TRUE;
5158 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
5159 else if (IS_FREE(xx, yy) || Feld[xx][yy] == EL_SAND)
5160 { /* free border field */
5161 if (nachbarn >= life[2] && nachbarn <= life[3])
5163 Feld[xx][yy] = element;
5164 MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
5166 DrawLevelField(xx, yy);
5167 Stop[xx][yy] = TRUE;
5174 PlaySoundLevel(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
5175 SND_GAME_OF_LIFE_GROWING);
5178 static void InitRobotWheel(int x, int y)
5180 ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
5183 static void RunRobotWheel(int x, int y)
5185 PlaySoundLevel(x, y, SND_ROBOT_WHEEL_ACTIVE);
5188 static void StopRobotWheel(int x, int y)
5190 if (ZX == x && ZY == y)
5194 static void InitTimegateWheel(int x, int y)
5196 ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
5199 static void RunTimegateWheel(int x, int y)
5201 PlaySoundLevel(x, y, SND_TIMEGATE_SWITCH_ACTIVE);
5204 void CheckExit(int x, int y)
5206 if (local_player->gems_still_needed > 0 ||
5207 local_player->sokobanfields_still_needed > 0 ||
5208 local_player->lights_still_needed > 0)
5210 int element = Feld[x][y];
5211 int graphic = el2img(element);
5213 if (IS_ANIMATED(graphic))
5214 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
5219 if (AllPlayersGone) /* do not re-open exit door closed after last player */
5222 Feld[x][y] = EL_EXIT_OPENING;
5224 PlaySoundLevelNearest(x, y, SND_CLASS_EXIT_OPENING);
5227 void CheckExitSP(int x, int y)
5229 if (local_player->gems_still_needed > 0)
5231 int element = Feld[x][y];
5232 int graphic = el2img(element);
5234 if (IS_ANIMATED(graphic))
5235 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
5240 if (AllPlayersGone) /* do not re-open exit door closed after last player */
5243 Feld[x][y] = EL_SP_EXIT_OPENING;
5245 PlaySoundLevelNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
5248 static void CloseAllOpenTimegates()
5252 for (y=0; y<lev_fieldy; y++)
5254 for (x=0; x<lev_fieldx; x++)
5256 int element = Feld[x][y];
5258 if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
5260 Feld[x][y] = EL_TIMEGATE_CLOSING;
5262 PlaySoundLevelAction(x, y, ACTION_CLOSING);
5264 PlaySoundLevel(x, y, SND_TIMEGATE_CLOSING);
5271 void EdelsteinFunkeln(int x, int y)
5273 if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
5276 if (Feld[x][y] == EL_BD_DIAMOND)
5279 if (MovDelay[x][y] == 0) /* next animation frame */
5280 MovDelay[x][y] = 11 * !SimpleRND(500);
5282 if (MovDelay[x][y] != 0) /* wait some time before next frame */
5286 if (setup.direct_draw && MovDelay[x][y])
5287 SetDrawtoField(DRAW_BUFFERED);
5289 DrawLevelElementAnimation(x, y, Feld[x][y]);
5291 if (MovDelay[x][y] != 0)
5293 int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
5294 10 - MovDelay[x][y]);
5296 DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
5298 if (setup.direct_draw)
5302 dest_x = FX + SCREENX(x) * TILEX;
5303 dest_y = FY + SCREENY(y) * TILEY;
5305 BlitBitmap(drawto_field, window,
5306 dest_x, dest_y, TILEX, TILEY, dest_x, dest_y);
5307 SetDrawtoField(DRAW_DIRECT);
5313 void MauerWaechst(int x, int y)
5317 if (!MovDelay[x][y]) /* next animation frame */
5318 MovDelay[x][y] = 3 * delay;
5320 if (MovDelay[x][y]) /* wait some time before next frame */
5324 if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
5326 int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
5327 int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
5329 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5332 if (!MovDelay[x][y])
5334 if (MovDir[x][y] == MV_LEFT)
5336 if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
5337 DrawLevelField(x - 1, y);
5339 else if (MovDir[x][y] == MV_RIGHT)
5341 if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
5342 DrawLevelField(x + 1, y);
5344 else if (MovDir[x][y] == MV_UP)
5346 if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
5347 DrawLevelField(x, y - 1);
5351 if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
5352 DrawLevelField(x, y + 1);
5355 Feld[x][y] = Store[x][y];
5357 GfxDir[x][y] = MovDir[x][y] = MV_NO_MOVING;
5358 DrawLevelField(x, y);
5363 void MauerAbleger(int ax, int ay)
5365 int element = Feld[ax][ay];
5366 int graphic = el2img(element);
5367 boolean oben_frei = FALSE, unten_frei = FALSE;
5368 boolean links_frei = FALSE, rechts_frei = FALSE;
5369 boolean oben_massiv = FALSE, unten_massiv = FALSE;
5370 boolean links_massiv = FALSE, rechts_massiv = FALSE;
5371 boolean new_wall = FALSE;
5373 if (IS_ANIMATED(graphic))
5374 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
5376 if (!MovDelay[ax][ay]) /* start building new wall */
5377 MovDelay[ax][ay] = 6;
5379 if (MovDelay[ax][ay]) /* wait some time before building new wall */
5382 if (MovDelay[ax][ay])
5386 if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
5388 if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
5390 if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
5392 if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
5395 if (element == EL_EXPANDABLE_WALL_VERTICAL ||
5396 element == EL_EXPANDABLE_WALL_ANY)
5400 Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
5401 Store[ax][ay-1] = element;
5402 GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
5403 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
5404 DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
5405 IMG_EXPANDABLE_WALL_GROWING_UP, 0);
5410 Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
5411 Store[ax][ay+1] = element;
5412 GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
5413 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
5414 DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
5415 IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
5420 if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
5421 element == EL_EXPANDABLE_WALL_ANY ||
5422 element == EL_EXPANDABLE_WALL)
5426 Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
5427 Store[ax-1][ay] = element;
5428 GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
5429 if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
5430 DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
5431 IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
5437 Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
5438 Store[ax+1][ay] = element;
5439 GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
5440 if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
5441 DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
5442 IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
5447 if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
5448 DrawLevelField(ax, ay);
5450 if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
5452 if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
5453 unten_massiv = TRUE;
5454 if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
5455 links_massiv = TRUE;
5456 if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
5457 rechts_massiv = TRUE;
5459 if (((oben_massiv && unten_massiv) ||
5460 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
5461 element == EL_EXPANDABLE_WALL) &&
5462 ((links_massiv && rechts_massiv) ||
5463 element == EL_EXPANDABLE_WALL_VERTICAL))
5464 Feld[ax][ay] = EL_WALL;
5468 PlaySoundLevelAction(ax, ay, ACTION_GROWING);
5470 PlaySoundLevel(ax, ay, SND_EXPANDABLE_WALL_GROWING);
5474 void CheckForDragon(int x, int y)
5477 boolean dragon_found = FALSE;
5478 static int xy[4][2] =
5490 int xx = x + j*xy[i][0], yy = y + j*xy[i][1];
5492 if (IN_LEV_FIELD(xx, yy) &&
5493 (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
5495 if (Feld[xx][yy] == EL_DRAGON)
5496 dragon_found = TRUE;
5509 int xx = x + j*xy[i][0], yy = y + j*xy[i][1];
5511 if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
5513 Feld[xx][yy] = EL_EMPTY;
5514 DrawLevelField(xx, yy);
5523 static void InitBuggyBase(int x, int y)
5525 int element = Feld[x][y];
5526 int activating_delay = FRAMES_PER_SECOND / 4;
5529 (element == EL_SP_BUGGY_BASE ?
5530 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
5531 element == EL_SP_BUGGY_BASE_ACTIVATING ?
5533 element == EL_SP_BUGGY_BASE_ACTIVE ?
5534 1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
5537 static void WarnBuggyBase(int x, int y)
5540 static int xy[4][2] =
5550 int xx = x + xy[i][0], yy = y + xy[i][1];
5552 if (IS_PLAYER(xx, yy))
5554 PlaySoundLevel(x, y, SND_SP_BUGGY_BASE_ACTIVE);
5561 static void InitTrap(int x, int y)
5563 ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
5566 static void ActivateTrap(int x, int y)
5568 PlaySoundLevel(x, y, SND_TRAP_ACTIVATING);
5571 static void ChangeActiveTrap(int x, int y)
5573 int graphic = IMG_TRAP_ACTIVE;
5575 /* if new animation frame was drawn, correct crumbled sand border */
5576 if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
5577 DrawLevelFieldCrumbledSand(x, y);
5580 static void ChangeElementNowExt(int x, int y, int target_element)
5582 /* check if element under player changes from accessible to unaccessible
5583 (needed for special case of dropping element which then changes) */
5584 if (IS_PLAYER(x, y) && !PLAYER_PROTECTED(x, y) &&
5585 IS_ACCESSIBLE(Feld[x][y]) && !IS_ACCESSIBLE(target_element))
5592 Feld[x][y] = target_element;
5594 Changed[x][y] |= ChangeEvent[x][y]; /* ignore same changes in this frame */
5596 ResetGfxAnimation(x, y);
5597 ResetRandomAnimationValue(x, y);
5599 InitField(x, y, FALSE);
5600 if (CAN_MOVE(Feld[x][y]))
5603 DrawLevelField(x, y);
5605 if (GFX_CRUMBLED(Feld[x][y]))
5606 DrawLevelFieldCrumbledSandNeighbours(x, y);
5608 TestIfBadThingTouchesHero(x, y);
5609 TestIfPlayerTouchesCustomElement(x, y);
5610 TestIfElementTouchesCustomElement(x, y);
5612 if (ELEM_IS_PLAYER(target_element))
5613 RelocatePlayer(x, y, target_element);
5616 static boolean ChangeElementNow(int x, int y, int element, int page)
5618 struct ElementChangeInfo *change = &element_info[element].change_page[page];
5620 /* always use default change event to prevent running into a loop */
5621 if (ChangeEvent[x][y] == CE_BITMASK_DEFAULT)
5622 ChangeEvent[x][y] = CH_EVENT_BIT(CE_DELAY);
5624 /* do not change already changed elements with same change event */
5626 if (Changed[x][y] & ChangeEvent[x][y])
5633 Changed[x][y] |= ChangeEvent[x][y]; /* ignore same changes in this frame */
5635 CheckTriggeredElementChange(x, y, Feld[x][y], CE_OTHER_IS_CHANGING);
5637 if (change->explode)
5644 if (change->use_content)
5646 boolean complete_change = TRUE;
5647 boolean can_change[3][3];
5650 for (yy = 0; yy < 3; yy++) for(xx = 0; xx < 3 ; xx++)
5652 boolean half_destructible;
5653 int ex = x + xx - 1;
5654 int ey = y + yy - 1;
5657 can_change[xx][yy] = TRUE;
5659 if (ex == x && ey == y) /* do not check changing element itself */
5662 if (change->content[xx][yy] == EL_EMPTY_SPACE)
5664 can_change[xx][yy] = FALSE; /* do not change empty borders */
5669 if (!IN_LEV_FIELD(ex, ey))
5671 can_change[xx][yy] = FALSE;
5672 complete_change = FALSE;
5679 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
5680 e = MovingOrBlocked2Element(ex, ey);
5682 half_destructible = (IS_FREE(ex, ey) || IS_DIGGABLE(e));
5684 if ((change->power <= CP_NON_DESTRUCTIVE && !IS_FREE(ex, ey)) ||
5685 (change->power <= CP_HALF_DESTRUCTIVE && !half_destructible) ||
5686 (change->power <= CP_FULL_DESTRUCTIVE && IS_INDESTRUCTIBLE(e)))
5688 can_change[xx][yy] = FALSE;
5689 complete_change = FALSE;
5693 if (!change->only_complete || complete_change)
5695 boolean something_has_changed = FALSE;
5697 if (change->only_complete && change->use_random_change &&
5698 RND(100) < change->random)
5701 for (yy = 0; yy < 3; yy++) for(xx = 0; xx < 3 ; xx++)
5703 int ex = x + xx - 1;
5704 int ey = y + yy - 1;
5706 if (can_change[xx][yy] && (!change->use_random_change ||
5707 RND(100) < change->random))
5709 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
5710 RemoveMovingField(ex, ey);
5712 ChangeEvent[ex][ey] = ChangeEvent[x][y];
5714 ChangeElementNowExt(ex, ey, change->content[xx][yy]);
5716 something_has_changed = TRUE;
5718 /* for symmetry reasons, freeze newly created border elements */
5719 if (ex != x || ey != y)
5720 Stop[ex][ey] = TRUE; /* no more moving in this frame */
5724 if (something_has_changed)
5725 PlaySoundLevelElementAction(x, y, element, ACTION_CHANGING);
5730 ChangeElementNowExt(x, y, change->target_element);
5732 PlaySoundLevelElementAction(x, y, element, ACTION_CHANGING);
5738 static void ChangeElement(int x, int y, int page)
5740 int element = MovingOrBlocked2Element(x, y);
5741 struct ElementInfo *ei = &element_info[element];
5742 struct ElementChangeInfo *change = &ei->change_page[page];
5746 if (!CAN_CHANGE(element))
5749 printf("ChangeElement(): %d,%d: element = %d ('%s')\n",
5750 x, y, element, element_info[element].token_name);
5751 printf("ChangeElement(): This should never happen!\n");
5757 if (ChangeDelay[x][y] == 0) /* initialize element change */
5759 ChangeDelay[x][y] = ( change->delay_fixed * change->delay_frames +
5760 RND(change->delay_random * change->delay_frames)) + 1;
5762 ResetGfxAnimation(x, y);
5763 ResetRandomAnimationValue(x, y);
5765 if (change->pre_change_function)
5766 change->pre_change_function(x, y);
5769 ChangeDelay[x][y]--;
5771 if (ChangeDelay[x][y] != 0) /* continue element change */
5773 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
5775 if (IS_ANIMATED(graphic))
5776 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
5778 if (change->change_function)
5779 change->change_function(x, y);
5781 else /* finish element change */
5783 if (ChangePage[x][y] != -1) /* remember page from delayed change */
5785 page = ChangePage[x][y];
5786 ChangePage[x][y] = -1;
5789 if (IS_MOVING(x, y)) /* never change a running system ;-) */
5791 ChangeDelay[x][y] = 1; /* try change after next move step */
5792 ChangePage[x][y] = page; /* remember page to use for change */
5797 if (ChangeElementNow(x, y, element, page))
5799 if (change->post_change_function)
5800 change->post_change_function(x, y);
5805 static boolean CheckTriggeredElementSideChange(int lx, int ly,
5806 int trigger_element,
5812 if (!(trigger_events[trigger_element] & CH_EVENT_BIT(trigger_event)))
5815 for (i=0; i < NUM_CUSTOM_ELEMENTS; i++)
5817 int element = EL_CUSTOM_START + i;
5819 boolean change_element = FALSE;
5822 if (!CAN_CHANGE(element) || !HAS_ANY_CHANGE_EVENT(element, trigger_event))
5825 for (j=0; j < element_info[element].num_change_pages; j++)
5827 struct ElementChangeInfo *change = &element_info[element].change_page[j];
5829 if (change->can_change &&
5831 change->events & CH_EVENT_BIT(trigger_event) &&
5833 change->sides & trigger_side &&
5834 change->trigger_element == trigger_element)
5837 if (!(change->events & CH_EVENT_BIT(trigger_event)))
5838 printf("::: !!! %d triggers %d: using wrong page %d [event %d]\n",
5839 trigger_element-EL_CUSTOM_START+1, i+1, j, trigger_event);
5842 change_element = TRUE;
5849 if (!change_element)
5852 for (y=0; y<lev_fieldy; y++) for (x=0; x<lev_fieldx; x++)
5855 if (x == lx && y == ly) /* do not change trigger element itself */
5859 if (Feld[x][y] == element)
5861 ChangeDelay[x][y] = 1;
5862 ChangeEvent[x][y] = CH_EVENT_BIT(trigger_event);
5863 ChangeElement(x, y, page);
5871 static boolean CheckTriggeredElementChange(int lx, int ly, int trigger_element,
5874 return CheckTriggeredElementSideChange(lx, ly, trigger_element, CH_SIDE_ANY,
5878 static boolean CheckElementSideChange(int x, int y, int element, int side,
5879 int trigger_event, int page)
5881 if (!CAN_CHANGE(element) || !HAS_ANY_CHANGE_EVENT(element, trigger_event))
5884 if (Feld[x][y] == EL_BLOCKED)
5886 Blocked2Moving(x, y, &x, &y);
5887 element = Feld[x][y];
5891 page = element_info[element].event_page_nr[trigger_event];
5893 if (!(element_info[element].change_page[page].sides & side))
5896 ChangeDelay[x][y] = 1;
5897 ChangeEvent[x][y] = CH_EVENT_BIT(trigger_event);
5898 ChangeElement(x, y, page);
5903 static boolean CheckElementChange(int x, int y, int element, int trigger_event)
5905 return CheckElementSideChange(x, y, element, CH_SIDE_ANY, trigger_event, -1);
5908 static void PlayerActions(struct PlayerInfo *player, byte player_action)
5910 static byte stored_player_action[MAX_PLAYERS];
5911 static int num_stored_actions = 0;
5912 boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
5913 int left = player_action & JOY_LEFT;
5914 int right = player_action & JOY_RIGHT;
5915 int up = player_action & JOY_UP;
5916 int down = player_action & JOY_DOWN;
5917 int button1 = player_action & JOY_BUTTON_1;
5918 int button2 = player_action & JOY_BUTTON_2;
5919 int dx = (left ? -1 : right ? 1 : 0);
5920 int dy = (up ? -1 : down ? 1 : 0);
5922 stored_player_action[player->index_nr] = 0;
5923 num_stored_actions++;
5925 if (!player->active || tape.pausing)
5931 snapped = SnapField(player, dx, dy);
5935 dropped = DropElement(player);
5937 moved = MovePlayer(player, dx, dy);
5940 if (tape.single_step && tape.recording && !tape.pausing)
5942 if (button1 || (dropped && !moved))
5944 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
5945 SnapField(player, 0, 0); /* stop snapping */
5949 stored_player_action[player->index_nr] = player_action;
5953 /* no actions for this player (no input at player's configured device) */
5955 DigField(player, 0, 0, 0, 0, DF_NO_PUSH);
5956 SnapField(player, 0, 0);
5957 CheckGravityMovement(player);
5959 if (player->MovPos == 0)
5960 InitPlayerGfxAnimation(player, ACTION_DEFAULT, player->MovDir);
5962 if (player->MovPos == 0) /* needed for tape.playing */
5963 player->is_moving = FALSE;
5966 if (tape.recording && num_stored_actions >= MAX_PLAYERS)
5968 TapeRecordAction(stored_player_action);
5969 num_stored_actions = 0;
5975 static unsigned long action_delay = 0;
5976 unsigned long action_delay_value;
5977 int magic_wall_x = 0, magic_wall_y = 0;
5978 int i, x, y, element, graphic;
5979 byte *recorded_player_action;
5980 byte summarized_player_action = 0;
5982 if (game_status != GAME_MODE_PLAYING)
5985 action_delay_value =
5986 (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
5988 if (tape.playing && tape.index_search && !tape.pausing)
5989 action_delay_value = 0;
5991 /* ---------- main game synchronization point ---------- */
5993 WaitUntilDelayReached(&action_delay, action_delay_value);
5995 if (network_playing && !network_player_action_received)
5999 printf("DEBUG: try to get network player actions in time\n");
6003 #if defined(PLATFORM_UNIX)
6004 /* last chance to get network player actions without main loop delay */
6008 if (game_status != GAME_MODE_PLAYING)
6011 if (!network_player_action_received)
6015 printf("DEBUG: failed to get network player actions in time\n");
6025 recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
6027 for (i=0; i<MAX_PLAYERS; i++)
6029 summarized_player_action |= stored_player[i].action;
6031 if (!network_playing)
6032 stored_player[i].effective_action = stored_player[i].action;
6035 #if defined(PLATFORM_UNIX)
6036 if (network_playing)
6037 SendToServer_MovePlayer(summarized_player_action);
6040 if (!options.network && !setup.team_mode)
6041 local_player->effective_action = summarized_player_action;
6043 for (i=0; i<MAX_PLAYERS; i++)
6045 int actual_player_action = stored_player[i].effective_action;
6047 if (stored_player[i].programmed_action)
6048 actual_player_action = stored_player[i].programmed_action;
6050 if (recorded_player_action)
6051 actual_player_action = recorded_player_action[i];
6053 PlayerActions(&stored_player[i], actual_player_action);
6054 ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
6057 network_player_action_received = FALSE;
6059 ScrollScreen(NULL, SCROLL_GO_ON);
6065 for (i=0; i<MAX_PLAYERS; i++)
6066 stored_player[i].Frame++;
6070 if (game.engine_version < VERSION_IDENT(2,2,0,7))
6072 for (i=0; i<MAX_PLAYERS; i++)
6074 struct PlayerInfo *player = &stored_player[i];
6078 if (player->active && player->is_pushing && player->is_moving &&
6081 ContinueMoving(x, y);
6083 /* continue moving after pushing (this is actually a bug) */
6084 if (!IS_MOVING(x, y))
6093 for (y=0; y<lev_fieldy; y++) for (x=0; x<lev_fieldx; x++)
6095 Changed[x][y] = CE_BITMASK_DEFAULT;
6096 ChangeEvent[x][y] = CE_BITMASK_DEFAULT;
6099 if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
6101 printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
6102 printf("GameActions(): This should never happen!\n");
6104 ChangePage[x][y] = -1;
6109 if (WasJustMoving[x][y] > 0)
6110 WasJustMoving[x][y]--;
6111 if (WasJustFalling[x][y] > 0)
6112 WasJustFalling[x][y]--;
6117 /* reset finished pushing action (not done in ContinueMoving() to allow
6118 continous pushing animation for elements with zero push delay) */
6119 if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
6121 ResetGfxAnimation(x, y);
6122 DrawLevelField(x, y);
6127 if (IS_BLOCKED(x, y))
6131 Blocked2Moving(x, y, &oldx, &oldy);
6132 if (!IS_MOVING(oldx, oldy))
6134 printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
6135 printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
6136 printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
6137 printf("GameActions(): This should never happen!\n");
6143 for (y=0; y<lev_fieldy; y++) for (x=0; x<lev_fieldx; x++)
6145 element = Feld[x][y];
6147 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
6149 graphic = el2img(element);
6155 printf("::: %d,%d: %d [%d]\n", x, y, element, FrameCounter);
6157 element = graphic = 0;
6161 if (graphic_info[graphic].anim_global_sync)
6162 GfxFrame[x][y] = FrameCounter;
6164 if (ANIM_MODE(graphic) == ANIM_RANDOM &&
6165 IS_NEXT_FRAME(GfxFrame[x][y], graphic))
6166 ResetRandomAnimationValue(x, y);
6168 SetRandomAnimationValue(x, y);
6171 PlaySoundLevelActionIfLoop(x, y, GfxAction[x][y]);
6174 if (IS_INACTIVE(element))
6176 if (IS_ANIMATED(graphic))
6177 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
6183 /* this may take place after moving, so 'element' may have changed */
6185 if (IS_CHANGING(x, y))
6187 if (IS_CHANGING(x, y) &&
6188 (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
6192 ChangeElement(x, y, ChangePage[x][y] != -1 ? ChangePage[x][y] :
6193 element_info[element].event_page_nr[CE_DELAY]);
6195 ChangeElement(x, y, element_info[element].event_page_nr[CE_DELAY]);
6198 element = Feld[x][y];
6199 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
6203 if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
6208 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
6210 if (element == EL_MOLE)
6211 printf("::: %d, %d, %d [%d]\n",
6212 IS_ANIMATED(graphic), IS_MOVING(x, y), Stop[x][y],
6216 if (element == EL_YAMYAM)
6217 printf("::: %d, %d, %d\n",
6218 IS_ANIMATED(graphic), IS_MOVING(x, y), Stop[x][y]);
6222 if (IS_ANIMATED(graphic) &&
6226 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
6229 if (element == EL_MOLE)
6230 printf("::: %d, %d\n", graphic, GfxFrame[x][y]);
6234 if (IS_GEM(element) || element == EL_SP_INFOTRON)
6235 EdelsteinFunkeln(x, y);
6237 else if ((element == EL_ACID ||
6238 element == EL_EXIT_OPEN ||
6239 element == EL_SP_EXIT_OPEN ||
6240 element == EL_SP_TERMINAL ||
6241 element == EL_SP_TERMINAL_ACTIVE ||
6242 element == EL_EXTRA_TIME ||
6243 element == EL_SHIELD_NORMAL ||
6244 element == EL_SHIELD_DEADLY) &&
6245 IS_ANIMATED(graphic))
6246 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
6247 else if (IS_MOVING(x, y))
6248 ContinueMoving(x, y);
6249 else if (IS_ACTIVE_BOMB(element))
6250 CheckDynamite(x, y);
6252 else if (element == EL_EXPLOSION && !game.explosions_delayed)
6253 Explode(x, y, ExplodePhase[x][y], EX_NORMAL);
6255 else if (element == EL_AMOEBA_GROWING)
6256 AmoebeWaechst(x, y);
6257 else if (element == EL_AMOEBA_SHRINKING)
6258 AmoebaDisappearing(x, y);
6260 #if !USE_NEW_AMOEBA_CODE
6261 else if (IS_AMOEBALIVE(element))
6262 AmoebeAbleger(x, y);
6265 else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
6267 else if (element == EL_EXIT_CLOSED)
6269 else if (element == EL_SP_EXIT_CLOSED)
6271 else if (element == EL_EXPANDABLE_WALL_GROWING)
6273 else if (element == EL_EXPANDABLE_WALL ||
6274 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
6275 element == EL_EXPANDABLE_WALL_VERTICAL ||
6276 element == EL_EXPANDABLE_WALL_ANY)
6278 else if (element == EL_FLAMES)
6279 CheckForDragon(x, y);
6281 else if (IS_AUTO_CHANGING(element))
6282 ChangeElement(x, y);
6284 else if (element == EL_EXPLOSION)
6285 ; /* drawing of correct explosion animation is handled separately */
6286 else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
6287 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
6290 /* this may take place after moving, so 'element' may have changed */
6291 if (IS_AUTO_CHANGING(Feld[x][y]))
6292 ChangeElement(x, y);
6295 if (IS_BELT_ACTIVE(element))
6296 PlaySoundLevelAction(x, y, ACTION_ACTIVE);
6298 if (game.magic_wall_active)
6300 int jx = local_player->jx, jy = local_player->jy;
6302 /* play the element sound at the position nearest to the player */
6303 if ((element == EL_MAGIC_WALL_FULL ||
6304 element == EL_MAGIC_WALL_ACTIVE ||
6305 element == EL_MAGIC_WALL_EMPTYING ||
6306 element == EL_BD_MAGIC_WALL_FULL ||
6307 element == EL_BD_MAGIC_WALL_ACTIVE ||
6308 element == EL_BD_MAGIC_WALL_EMPTYING) &&
6309 ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
6317 #if USE_NEW_AMOEBA_CODE
6318 /* new experimental amoeba growth stuff */
6320 if (!(FrameCounter % 8))
6323 static unsigned long random = 1684108901;
6325 for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
6328 x = (random >> 10) % lev_fieldx;
6329 y = (random >> 20) % lev_fieldy;
6331 x = RND(lev_fieldx);
6332 y = RND(lev_fieldy);
6334 element = Feld[x][y];
6336 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
6337 if (!IS_PLAYER(x,y) &&
6338 (element == EL_EMPTY ||
6339 element == EL_SAND ||
6340 element == EL_QUICKSAND_EMPTY ||
6341 element == EL_ACID_SPLASH_LEFT ||
6342 element == EL_ACID_SPLASH_RIGHT))
6344 if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
6345 (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
6346 (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
6347 (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
6348 Feld[x][y] = EL_AMOEBA_DROP;
6351 random = random * 129 + 1;
6357 if (game.explosions_delayed)
6360 game.explosions_delayed = FALSE;
6362 for (y=0; y<lev_fieldy; y++) for (x=0; x<lev_fieldx; x++)
6364 element = Feld[x][y];
6366 if (ExplodeField[x][y])
6367 Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
6368 else if (element == EL_EXPLOSION)
6369 Explode(x, y, ExplodePhase[x][y], EX_NORMAL);
6371 ExplodeField[x][y] = EX_NO_EXPLOSION;
6374 game.explosions_delayed = TRUE;
6377 if (game.magic_wall_active)
6379 if (!(game.magic_wall_time_left % 4))
6381 int element = Feld[magic_wall_x][magic_wall_y];
6383 if (element == EL_BD_MAGIC_WALL_FULL ||
6384 element == EL_BD_MAGIC_WALL_ACTIVE ||
6385 element == EL_BD_MAGIC_WALL_EMPTYING)
6386 PlaySoundLevel(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
6388 PlaySoundLevel(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
6391 if (game.magic_wall_time_left > 0)
6393 game.magic_wall_time_left--;
6394 if (!game.magic_wall_time_left)
6396 for (y=0; y<lev_fieldy; y++) for (x=0; x<lev_fieldx; x++)
6398 element = Feld[x][y];
6400 if (element == EL_MAGIC_WALL_ACTIVE ||
6401 element == EL_MAGIC_WALL_FULL)
6403 Feld[x][y] = EL_MAGIC_WALL_DEAD;
6404 DrawLevelField(x, y);
6406 else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
6407 element == EL_BD_MAGIC_WALL_FULL)
6409 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
6410 DrawLevelField(x, y);
6414 game.magic_wall_active = FALSE;
6419 if (game.light_time_left > 0)
6421 game.light_time_left--;
6423 if (game.light_time_left == 0)
6424 RedrawAllLightSwitchesAndInvisibleElements();
6427 if (game.timegate_time_left > 0)
6429 game.timegate_time_left--;
6431 if (game.timegate_time_left == 0)
6432 CloseAllOpenTimegates();
6435 for (i=0; i<MAX_PLAYERS; i++)
6437 struct PlayerInfo *player = &stored_player[i];
6439 if (SHIELD_ON(player))
6441 if (player->shield_deadly_time_left)
6442 PlaySoundLevel(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
6443 else if (player->shield_normal_time_left)
6444 PlaySoundLevel(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
6448 if (TimeFrames >= FRAMES_PER_SECOND)
6453 for (i=0; i<MAX_PLAYERS; i++)
6455 struct PlayerInfo *player = &stored_player[i];
6457 if (SHIELD_ON(player))
6459 player->shield_normal_time_left--;
6461 if (player->shield_deadly_time_left > 0)
6462 player->shield_deadly_time_left--;
6466 if (tape.recording || tape.playing)
6467 DrawVideoDisplay(VIDEO_STATE_TIME_ON, TimePlayed);
6473 if (TimeLeft <= 10 && setup.time_limit)
6474 PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, SOUND_MIDDLE);
6476 DrawText(DX_TIME, DY_TIME, int2str(TimeLeft, 3), FONT_TEXT_2);
6478 if (!TimeLeft && setup.time_limit)
6479 for (i=0; i<MAX_PLAYERS; i++)
6480 KillHero(&stored_player[i]);
6482 else if (level.time == 0 && !AllPlayersGone) /* level without time limit */
6483 DrawText(DX_TIME, DY_TIME, int2str(TimePlayed, 3), FONT_TEXT_2);
6488 if (options.debug) /* calculate frames per second */
6490 static unsigned long fps_counter = 0;
6491 static int fps_frames = 0;
6492 unsigned long fps_delay_ms = Counter() - fps_counter;
6496 if (fps_delay_ms >= 500) /* calculate fps every 0.5 seconds */
6498 global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
6501 fps_counter = Counter();
6504 redraw_mask |= REDRAW_FPS;
6508 if (stored_player[0].jx != stored_player[0].last_jx ||
6509 stored_player[0].jy != stored_player[0].last_jy)
6510 printf("::: %d, %d, %d, %d, %d\n",
6511 stored_player[0].MovDir,
6512 stored_player[0].MovPos,
6513 stored_player[0].GfxPos,
6514 stored_player[0].Frame,
6515 stored_player[0].StepFrame);
6522 for (i=0; i<MAX_PLAYERS; i++)
6525 MOVE_DELAY_NORMAL_SPEED / stored_player[i].move_delay_value;
6527 stored_player[i].Frame += move_frames;
6529 if (stored_player[i].MovPos != 0)
6530 stored_player[i].StepFrame += move_frames;
6535 if (local_player->show_envelope != 0 && local_player->MovPos == 0)
6537 ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
6539 local_player->show_envelope = 0;
6544 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
6546 int min_x = x, min_y = y, max_x = x, max_y = y;
6549 for (i=0; i<MAX_PLAYERS; i++)
6551 int jx = stored_player[i].jx, jy = stored_player[i].jy;
6553 if (!stored_player[i].active || &stored_player[i] == player)
6556 min_x = MIN(min_x, jx);
6557 min_y = MIN(min_y, jy);
6558 max_x = MAX(max_x, jx);
6559 max_y = MAX(max_y, jy);
6562 return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
6565 static boolean AllPlayersInVisibleScreen()
6569 for (i=0; i<MAX_PLAYERS; i++)
6571 int jx = stored_player[i].jx, jy = stored_player[i].jy;
6573 if (!stored_player[i].active)
6576 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
6583 void ScrollLevel(int dx, int dy)
6585 int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
6588 BlitBitmap(drawto_field, drawto_field,
6589 FX + TILEX * (dx == -1) - softscroll_offset,
6590 FY + TILEY * (dy == -1) - softscroll_offset,
6591 SXSIZE - TILEX * (dx!=0) + 2 * softscroll_offset,
6592 SYSIZE - TILEY * (dy!=0) + 2 * softscroll_offset,
6593 FX + TILEX * (dx == 1) - softscroll_offset,
6594 FY + TILEY * (dy == 1) - softscroll_offset);
6598 x = (dx == 1 ? BX1 : BX2);
6599 for (y=BY1; y <= BY2; y++)
6600 DrawScreenField(x, y);
6605 y = (dy == 1 ? BY1 : BY2);
6606 for (x=BX1; x <= BX2; x++)
6607 DrawScreenField(x, y);
6610 redraw_mask |= REDRAW_FIELD;
6613 static void CheckGravityMovement(struct PlayerInfo *player)
6615 if (game.gravity && !player->programmed_action)
6617 int move_dir_vertical = player->action & (MV_UP | MV_DOWN);
6618 int move_dir_horizontal = player->action & (MV_LEFT | MV_RIGHT);
6620 (player->last_move_dir & (MV_LEFT | MV_RIGHT) ?
6621 (move_dir_vertical ? move_dir_vertical : move_dir_horizontal) :
6622 (move_dir_horizontal ? move_dir_horizontal : move_dir_vertical));
6623 int jx = player->jx, jy = player->jy;
6624 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
6625 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
6626 int new_jx = jx + dx, new_jy = jy + dy;
6627 boolean field_under_player_is_free =
6628 (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
6629 boolean player_is_moving_to_valid_field =
6630 (IN_LEV_FIELD(new_jx, new_jy) &&
6631 (Feld[new_jx][new_jy] == EL_SP_BASE ||
6632 Feld[new_jx][new_jy] == EL_SAND));
6633 /* !!! extend EL_SAND to anything diggable !!! */
6635 if (field_under_player_is_free &&
6636 !player_is_moving_to_valid_field &&
6637 !IS_WALKABLE_INSIDE(Feld[jx][jy]))
6638 player->programmed_action = MV_DOWN;
6644 -----------------------------------------------------------------------------
6645 dx, dy: direction (non-diagonal) to try to move the player to
6646 real_dx, real_dy: direction as read from input device (can be diagonal)
6649 boolean MovePlayerOneStep(struct PlayerInfo *player,
6650 int dx, int dy, int real_dx, int real_dy)
6653 static int change_sides[4][2] =
6655 /* enter side leave side */
6656 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* moving left */
6657 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* moving right */
6658 { CH_SIDE_BOTTOM, CH_SIDE_TOP }, /* moving up */
6659 { CH_SIDE_TOP, CH_SIDE_BOTTOM } /* moving down */
6661 int move_direction = (dx == -1 ? MV_LEFT :
6662 dx == +1 ? MV_RIGHT :
6664 dy == +1 ? MV_DOWN : MV_NO_MOVING);
6665 int enter_side = change_sides[MV_DIR_BIT(move_direction)][0];
6666 int leave_side = change_sides[MV_DIR_BIT(move_direction)][1];
6668 int jx = player->jx, jy = player->jy;
6669 int new_jx = jx + dx, new_jy = jy + dy;
6673 if (!player->active || (!dx && !dy))
6674 return MF_NO_ACTION;
6676 player->MovDir = (dx < 0 ? MV_LEFT :
6679 dy > 0 ? MV_DOWN : MV_NO_MOVING);
6681 if (!IN_LEV_FIELD(new_jx, new_jy))
6682 return MF_NO_ACTION;
6684 if (!options.network && !AllPlayersInSight(player, new_jx, new_jy))
6685 return MF_NO_ACTION;
6688 element = MovingOrBlocked2Element(new_jx, new_jy);
6690 element = MovingOrBlocked2ElementIfNotLeaving(new_jx, new_jy);
6693 if (DONT_RUN_INTO(element))
6695 if (element == EL_ACID && dx == 0 && dy == 1)
6698 Feld[jx][jy] = EL_PLAYER_1;
6699 InitMovingField(jx, jy, MV_DOWN);
6700 Store[jx][jy] = EL_ACID;
6701 ContinueMoving(jx, jy);
6705 TestIfHeroRunsIntoBadThing(jx, jy, player->MovDir);
6710 can_move = DigField(player, new_jx, new_jy, real_dx, real_dy, DF_DIG);
6711 if (can_move != MF_MOVING)
6714 /* check if DigField() has caused relocation of the player */
6715 if (player->jx != jx || player->jy != jy)
6716 return MF_NO_ACTION;
6718 StorePlayer[jx][jy] = 0;
6719 player->last_jx = jx;
6720 player->last_jy = jy;
6721 player->jx = new_jx;
6722 player->jy = new_jy;
6723 StorePlayer[new_jx][new_jy] = player->element_nr;
6726 (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
6728 ScrollPlayer(player, SCROLL_INIT);
6731 if (IS_CUSTOM_ELEMENT(Feld[jx][jy]))
6733 CheckTriggeredElementSideChange(jx, jy, Feld[jx][jy], leave_side,
6734 CE_OTHER_GETS_LEFT);
6735 CheckElementSideChange(jx, jy, Feld[jx][jy], leave_side,
6736 CE_LEFT_BY_PLAYER, -1);
6739 if (IS_CUSTOM_ELEMENT(Feld[new_jx][new_jy]))
6741 CheckTriggeredElementSideChange(new_jx, new_jy, Feld[new_jx][new_jy],
6742 enter_side, CE_OTHER_GETS_ENTERED);
6743 CheckElementSideChange(new_jx, new_jy, Feld[new_jx][new_jy], enter_side,
6744 CE_ENTERED_BY_PLAYER, -1);
6751 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
6753 int jx = player->jx, jy = player->jy;
6754 int old_jx = jx, old_jy = jy;
6755 int moved = MF_NO_ACTION;
6757 if (!player->active || (!dx && !dy))
6761 if (!FrameReached(&player->move_delay, player->move_delay_value) &&
6765 if (!FrameReached(&player->move_delay, player->move_delay_value) &&
6766 !(tape.playing && tape.file_version < FILE_VERSION_2_0))
6770 /* remove the last programmed player action */
6771 player->programmed_action = 0;
6775 /* should only happen if pre-1.2 tape recordings are played */
6776 /* this is only for backward compatibility */
6778 int original_move_delay_value = player->move_delay_value;
6781 printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%ld]\n",
6785 /* scroll remaining steps with finest movement resolution */
6786 player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
6788 while (player->MovPos)
6790 ScrollPlayer(player, SCROLL_GO_ON);
6791 ScrollScreen(NULL, SCROLL_GO_ON);
6797 player->move_delay_value = original_move_delay_value;
6800 if (player->last_move_dir & (MV_LEFT | MV_RIGHT))
6802 if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
6803 moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
6807 if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
6808 moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
6814 if (moved & MF_MOVING && !ScreenMovPos &&
6815 (player == local_player || !options.network))
6817 int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
6818 int offset = (setup.scroll_delay ? 3 : 0);
6820 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
6822 /* actual player has left the screen -- scroll in that direction */
6823 if (jx != old_jx) /* player has moved horizontally */
6824 scroll_x += (jx - old_jx);
6825 else /* player has moved vertically */
6826 scroll_y += (jy - old_jy);
6830 if (jx != old_jx) /* player has moved horizontally */
6832 if ((player->MovDir == MV_LEFT && scroll_x > jx - MIDPOSX + offset) ||
6833 (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
6834 scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
6836 /* don't scroll over playfield boundaries */
6837 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
6838 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
6840 /* don't scroll more than one field at a time */
6841 scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
6843 /* don't scroll against the player's moving direction */
6844 if ((player->MovDir == MV_LEFT && scroll_x > old_scroll_x) ||
6845 (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
6846 scroll_x = old_scroll_x;
6848 else /* player has moved vertically */
6850 if ((player->MovDir == MV_UP && scroll_y > jy - MIDPOSY + offset) ||
6851 (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
6852 scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
6854 /* don't scroll over playfield boundaries */
6855 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
6856 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
6858 /* don't scroll more than one field at a time */
6859 scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
6861 /* don't scroll against the player's moving direction */
6862 if ((player->MovDir == MV_UP && scroll_y > old_scroll_y) ||
6863 (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
6864 scroll_y = old_scroll_y;
6868 if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
6870 if (!options.network && !AllPlayersInVisibleScreen())
6872 scroll_x = old_scroll_x;
6873 scroll_y = old_scroll_y;
6877 ScrollScreen(player, SCROLL_INIT);
6878 ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
6885 InitPlayerGfxAnimation(player, ACTION_DEFAULT);
6887 if (!(moved & MF_MOVING) && !player->is_pushing)
6892 player->StepFrame = 0;
6894 if (moved & MF_MOVING)
6896 if (old_jx != jx && old_jy == jy)
6897 player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
6898 else if (old_jx == jx && old_jy != jy)
6899 player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
6901 DrawLevelField(jx, jy); /* for "crumbled sand" */
6903 player->last_move_dir = player->MovDir;
6904 player->is_moving = TRUE;
6906 player->is_snapping = FALSE;
6910 player->is_switching = FALSE;
6916 static int change_sides[4][2] =
6918 /* enter side leave side */
6919 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* moving left */
6920 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* moving right */
6921 { CH_SIDE_BOTTOM, CH_SIDE_TOP }, /* moving up */
6922 { CH_SIDE_TOP, CH_SIDE_BOTTOM } /* moving down */
6924 int move_direction = player->MovDir;
6925 int enter_side = change_sides[MV_DIR_BIT(move_direction)][0];
6926 int leave_side = change_sides[MV_DIR_BIT(move_direction)][1];
6929 if (IS_CUSTOM_ELEMENT(Feld[old_jx][old_jy]))
6931 CheckTriggeredElementSideChange(old_jx, old_jy, Feld[old_jx][old_jy],
6932 leave_side, CE_OTHER_GETS_LEFT);
6933 CheckElementSideChange(old_jx, old_jy, Feld[old_jx][old_jy],
6934 leave_side, CE_LEFT_BY_PLAYER, -1);
6937 if (IS_CUSTOM_ELEMENT(Feld[jx][jy]))
6939 CheckTriggeredElementSideChange(jx, jy, Feld[jx][jy],
6940 enter_side, CE_OTHER_GETS_ENTERED);
6941 CheckElementSideChange(jx, jy, Feld[jx][jy],
6942 enter_side, CE_ENTERED_BY_PLAYER, -1);
6953 CheckGravityMovement(player);
6956 player->last_move_dir = MV_NO_MOVING;
6958 player->is_moving = FALSE;
6961 if (game.engine_version < VERSION_IDENT(3,0,7,0))
6963 TestIfHeroTouchesBadThing(jx, jy);
6964 TestIfPlayerTouchesCustomElement(jx, jy);
6967 if (!player->active)
6973 void ScrollPlayer(struct PlayerInfo *player, int mode)
6975 int jx = player->jx, jy = player->jy;
6976 int last_jx = player->last_jx, last_jy = player->last_jy;
6977 int move_stepsize = TILEX / player->move_delay_value;
6979 if (!player->active || !player->MovPos)
6982 if (mode == SCROLL_INIT)
6984 player->actual_frame_counter = FrameCounter;
6985 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
6987 if (Feld[last_jx][last_jy] == EL_EMPTY)
6988 Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
6995 else if (!FrameReached(&player->actual_frame_counter, 1))
6998 player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
6999 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
7001 if (Feld[last_jx][last_jy] == EL_PLAYER_IS_LEAVING)
7002 Feld[last_jx][last_jy] = EL_EMPTY;
7004 /* before DrawPlayer() to draw correct player graphic for this case */
7005 if (player->MovPos == 0)
7006 CheckGravityMovement(player);
7009 DrawPlayer(player); /* needed here only to cleanup last field */
7012 if (player->MovPos == 0) /* player reached destination field */
7014 if (IS_PASSABLE(Feld[last_jx][last_jy]))
7016 /* continue with normal speed after quickly moving through gate */
7017 HALVE_PLAYER_SPEED(player);
7019 /* be able to make the next move without delay */
7020 player->move_delay = 0;
7023 player->last_jx = jx;
7024 player->last_jy = jy;
7026 if (Feld[jx][jy] == EL_EXIT_OPEN ||
7027 Feld[jx][jy] == EL_SP_EXIT_OPEN ||
7028 Feld[jx][jy] == EL_SP_EXIT_OPENING) /* <-- special case */
7030 DrawPlayer(player); /* needed here only to cleanup last field */
7033 if (local_player->friends_still_needed == 0 ||
7034 IS_SP_ELEMENT(Feld[jx][jy]))
7035 player->LevelSolved = player->GameOver = TRUE;
7038 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
7040 TestIfHeroTouchesBadThing(jx, jy);
7041 TestIfPlayerTouchesCustomElement(jx, jy);
7043 TestIfElementTouchesCustomElement(jx, jy); /* for empty space */
7046 if (!player->active)
7050 if (tape.single_step && tape.recording && !tape.pausing &&
7051 !player->programmed_action)
7052 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
7056 void ScrollScreen(struct PlayerInfo *player, int mode)
7058 static unsigned long screen_frame_counter = 0;
7060 if (mode == SCROLL_INIT)
7062 /* set scrolling step size according to actual player's moving speed */
7063 ScrollStepSize = TILEX / player->move_delay_value;
7065 screen_frame_counter = FrameCounter;
7066 ScreenMovDir = player->MovDir;
7067 ScreenMovPos = player->MovPos;
7068 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
7071 else if (!FrameReached(&screen_frame_counter, 1))
7076 ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
7077 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
7078 redraw_mask |= REDRAW_FIELD;
7081 ScreenMovDir = MV_NO_MOVING;
7084 void TestIfPlayerTouchesCustomElement(int x, int y)
7086 static int xy[4][2] =
7093 static int change_sides[4][2] =
7095 /* center side border side */
7096 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
7097 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
7098 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
7099 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
7101 static int touch_dir[4] =
7108 int center_element = Feld[x][y]; /* should always be non-moving! */
7113 int xx = x + xy[i][0];
7114 int yy = y + xy[i][1];
7115 int center_side = change_sides[i][0];
7116 int border_side = change_sides[i][1];
7119 if (!IN_LEV_FIELD(xx, yy))
7122 if (IS_PLAYER(x, y))
7124 if (game.engine_version < VERSION_IDENT(3,0,7,0))
7125 border_element = Feld[xx][yy]; /* may be moving! */
7126 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
7127 border_element = Feld[xx][yy];
7128 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
7129 border_element = MovingOrBlocked2Element(xx, yy);
7131 continue; /* center and border element do not touch */
7133 CheckTriggeredElementSideChange(xx, yy, border_element, border_side,
7134 CE_OTHER_GETS_TOUCHED);
7135 CheckElementSideChange(xx, yy, border_element, border_side,
7136 CE_TOUCHED_BY_PLAYER, -1);
7138 else if (IS_PLAYER(xx, yy))
7140 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
7142 struct PlayerInfo *player = PLAYERINFO(xx, yy);
7144 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
7145 continue; /* center and border element do not touch */
7148 CheckTriggeredElementSideChange(x, y, center_element, center_side,
7149 CE_OTHER_GETS_TOUCHED);
7150 CheckElementSideChange(x, y, center_element, center_side,
7151 CE_TOUCHED_BY_PLAYER, -1);
7158 void TestIfElementTouchesCustomElement(int x, int y)
7160 static int xy[4][2] =
7167 static int change_sides[4][2] =
7169 /* center side border side */
7170 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
7171 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
7172 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
7173 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
7175 static int touch_dir[4] =
7182 boolean change_center_element = FALSE;
7183 int center_element_change_page = 0;
7184 int center_element = Feld[x][y]; /* should always be non-moving! */
7189 int xx = x + xy[i][0];
7190 int yy = y + xy[i][1];
7191 int center_side = change_sides[i][0];
7192 int border_side = change_sides[i][1];
7195 if (!IN_LEV_FIELD(xx, yy))
7198 if (game.engine_version < VERSION_IDENT(3,0,7,0))
7199 border_element = Feld[xx][yy]; /* may be moving! */
7200 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
7201 border_element = Feld[xx][yy];
7202 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
7203 border_element = MovingOrBlocked2Element(xx, yy);
7205 continue; /* center and border element do not touch */
7207 /* check for change of center element (but change it only once) */
7208 if (IS_CUSTOM_ELEMENT(center_element) &&
7209 HAS_ANY_CHANGE_EVENT(center_element, CE_OTHER_IS_TOUCHING) &&
7210 !change_center_element)
7212 for (j=0; j < element_info[center_element].num_change_pages; j++)
7214 struct ElementChangeInfo *change =
7215 &element_info[center_element].change_page[j];
7217 if (change->can_change &&
7218 change->events & CH_EVENT_BIT(CE_OTHER_IS_TOUCHING) &&
7219 change->sides & border_side &&
7220 change->trigger_element == border_element)
7222 change_center_element = TRUE;
7223 center_element_change_page = j;
7230 /* check for change of border element */
7231 if (IS_CUSTOM_ELEMENT(border_element) &&
7232 HAS_ANY_CHANGE_EVENT(border_element, CE_OTHER_IS_TOUCHING))
7234 for (j=0; j < element_info[border_element].num_change_pages; j++)
7236 struct ElementChangeInfo *change =
7237 &element_info[border_element].change_page[j];
7239 if (change->can_change &&
7240 change->events & CH_EVENT_BIT(CE_OTHER_IS_TOUCHING) &&
7241 change->sides & center_side &&
7242 change->trigger_element == center_element)
7244 CheckElementSideChange(xx, yy, border_element, CH_SIDE_ANY,
7245 CE_OTHER_IS_TOUCHING, j);
7252 if (change_center_element)
7253 CheckElementSideChange(x, y, center_element, CH_SIDE_ANY,
7254 CE_OTHER_IS_TOUCHING, center_element_change_page);
7257 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
7259 int i, kill_x = -1, kill_y = -1;
7260 static int test_xy[4][2] =
7267 static int test_dir[4] =
7277 int test_x, test_y, test_move_dir, test_element;
7279 test_x = good_x + test_xy[i][0];
7280 test_y = good_y + test_xy[i][1];
7281 if (!IN_LEV_FIELD(test_x, test_y))
7285 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NO_MOVING);
7288 test_element = Feld[test_x][test_y];
7290 test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
7293 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
7294 2nd case: DONT_TOUCH style bad thing does not move away from good thing
7296 if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
7297 (DONT_TOUCH(test_element) && test_move_dir != test_dir[i]))
7305 if (kill_x != -1 || kill_y != -1)
7307 if (IS_PLAYER(good_x, good_y))
7309 struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
7311 if (player->shield_deadly_time_left > 0)
7312 Bang(kill_x, kill_y);
7313 else if (!PLAYER_PROTECTED(good_x, good_y))
7317 Bang(good_x, good_y);
7321 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
7323 int i, kill_x = -1, kill_y = -1;
7324 int bad_element = Feld[bad_x][bad_y];
7325 static int test_xy[4][2] =
7332 static int touch_dir[4] =
7339 static int test_dir[4] =
7347 if (bad_element == EL_EXPLOSION) /* skip just exploding bad things */
7352 int test_x, test_y, test_move_dir, test_element;
7354 test_x = bad_x + test_xy[i][0];
7355 test_y = bad_y + test_xy[i][1];
7356 if (!IN_LEV_FIELD(test_x, test_y))
7360 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NO_MOVING);
7362 test_element = Feld[test_x][test_y];
7364 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
7365 2nd case: DONT_TOUCH style bad thing does not move away from good thing
7367 if ((DONT_RUN_INTO(bad_element) && bad_move_dir == test_dir[i]) ||
7368 (DONT_TOUCH(bad_element) && test_move_dir != test_dir[i]))
7370 /* good thing is player or penguin that does not move away */
7371 if (IS_PLAYER(test_x, test_y))
7373 struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
7375 if (bad_element == EL_ROBOT && player->is_moving)
7376 continue; /* robot does not kill player if he is moving */
7378 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
7380 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
7381 continue; /* center and border element do not touch */
7388 else if (test_element == EL_PENGUIN)
7397 if (kill_x != -1 || kill_y != -1)
7399 if (IS_PLAYER(kill_x, kill_y))
7401 struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
7403 if (player->shield_deadly_time_left > 0)
7405 else if (!PLAYER_PROTECTED(kill_x, kill_y))
7409 Bang(kill_x, kill_y);
7413 void TestIfHeroTouchesBadThing(int x, int y)
7415 TestIfGoodThingHitsBadThing(x, y, MV_NO_MOVING);
7418 void TestIfHeroRunsIntoBadThing(int x, int y, int move_dir)
7420 TestIfGoodThingHitsBadThing(x, y, move_dir);
7423 void TestIfBadThingTouchesHero(int x, int y)
7425 TestIfBadThingHitsGoodThing(x, y, MV_NO_MOVING);
7428 void TestIfBadThingRunsIntoHero(int x, int y, int move_dir)
7430 TestIfBadThingHitsGoodThing(x, y, move_dir);
7433 void TestIfFriendTouchesBadThing(int x, int y)
7435 TestIfGoodThingHitsBadThing(x, y, MV_NO_MOVING);
7438 void TestIfBadThingTouchesFriend(int x, int y)
7440 TestIfBadThingHitsGoodThing(x, y, MV_NO_MOVING);
7443 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
7445 int i, kill_x = bad_x, kill_y = bad_y;
7446 static int xy[4][2] =
7458 x = bad_x + xy[i][0];
7459 y = bad_y + xy[i][1];
7460 if (!IN_LEV_FIELD(x, y))
7463 element = Feld[x][y];
7464 if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
7465 element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
7473 if (kill_x != bad_x || kill_y != bad_y)
7477 void KillHero(struct PlayerInfo *player)
7479 int jx = player->jx, jy = player->jy;
7481 if (!player->active)
7484 /* remove accessible field at the player's position */
7485 Feld[jx][jy] = EL_EMPTY;
7487 /* deactivate shield (else Bang()/Explode() would not work right) */
7488 player->shield_normal_time_left = 0;
7489 player->shield_deadly_time_left = 0;
7495 static void KillHeroUnlessProtected(int x, int y)
7497 if (!PLAYER_PROTECTED(x, y))
7498 KillHero(PLAYERINFO(x, y));
7501 void BuryHero(struct PlayerInfo *player)
7503 int jx = player->jx, jy = player->jy;
7505 if (!player->active)
7509 PlaySoundLevelElementAction(jx, jy, player->element_nr, ACTION_DYING);
7511 PlaySoundLevel(jx, jy, SND_CLASS_PLAYER_DYING);
7513 PlaySoundLevel(jx, jy, SND_GAME_LOSING);
7515 player->GameOver = TRUE;
7519 void RemoveHero(struct PlayerInfo *player)
7521 int jx = player->jx, jy = player->jy;
7522 int i, found = FALSE;
7524 player->present = FALSE;
7525 player->active = FALSE;
7527 if (!ExplodeField[jx][jy])
7528 StorePlayer[jx][jy] = 0;
7530 for (i=0; i<MAX_PLAYERS; i++)
7531 if (stored_player[i].active)
7535 AllPlayersGone = TRUE;
7542 =============================================================================
7543 checkDiagonalPushing()
7544 -----------------------------------------------------------------------------
7545 check if diagonal input device direction results in pushing of object
7546 (by checking if the alternative direction is walkable, diggable, ...)
7547 =============================================================================
7550 static boolean checkDiagonalPushing(struct PlayerInfo *player,
7551 int x, int y, int real_dx, int real_dy)
7553 int jx, jy, dx, dy, xx, yy;
7555 if (real_dx == 0 || real_dy == 0) /* no diagonal direction => push */
7558 /* diagonal direction: check alternative direction */
7563 xx = jx + (dx == 0 ? real_dx : 0);
7564 yy = jy + (dy == 0 ? real_dy : 0);
7566 return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
7570 =============================================================================
7572 -----------------------------------------------------------------------------
7573 x, y: field next to player (non-diagonal) to try to dig to
7574 real_dx, real_dy: direction as read from input device (can be diagonal)
7575 =============================================================================
7578 int DigField(struct PlayerInfo *player,
7579 int x, int y, int real_dx, int real_dy, int mode)
7581 static int change_sides[4] =
7583 CH_SIDE_RIGHT, /* moving left */
7584 CH_SIDE_LEFT, /* moving right */
7585 CH_SIDE_BOTTOM, /* moving up */
7586 CH_SIDE_TOP, /* moving down */
7588 boolean use_spring_bug = (game.engine_version < VERSION_IDENT(2,2,0,0));
7589 int jx = player->jx, jy = player->jy;
7590 int dx = x - jx, dy = y - jy;
7591 int nextx = x + dx, nexty = y + dy;
7592 int move_direction = (dx == -1 ? MV_LEFT :
7593 dx == +1 ? MV_RIGHT :
7595 dy == +1 ? MV_DOWN : MV_NO_MOVING);
7596 int dig_side = change_sides[MV_DIR_BIT(move_direction)];
7599 if (player->MovPos == 0)
7601 player->is_digging = FALSE;
7602 player->is_collecting = FALSE;
7605 if (player->MovPos == 0) /* last pushing move finished */
7606 player->is_pushing = FALSE;
7608 if (mode == DF_NO_PUSH) /* player just stopped pushing */
7610 player->is_switching = FALSE;
7611 player->push_delay = 0;
7613 return MF_NO_ACTION;
7616 if (IS_MOVING(x, y) || IS_PLAYER(x, y))
7617 return MF_NO_ACTION;
7620 if (IS_TUBE(Feld[jx][jy]) || IS_TUBE(Back[jx][jy]))
7622 if (IS_TUBE(Feld[jx][jy]) ||
7623 (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0)))
7627 int tube_element = (IS_TUBE(Feld[jx][jy]) ? Feld[jx][jy] : Back[jx][jy]);
7628 int tube_leave_directions[][2] =
7630 { EL_TUBE_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
7631 { EL_TUBE_VERTICAL, MV_UP | MV_DOWN },
7632 { EL_TUBE_HORIZONTAL, MV_LEFT | MV_RIGHT },
7633 { EL_TUBE_VERTICAL_LEFT, MV_LEFT | MV_UP | MV_DOWN },
7634 { EL_TUBE_VERTICAL_RIGHT, MV_RIGHT | MV_UP | MV_DOWN },
7635 { EL_TUBE_HORIZONTAL_UP, MV_LEFT | MV_RIGHT | MV_UP },
7636 { EL_TUBE_HORIZONTAL_DOWN, MV_LEFT | MV_RIGHT | MV_DOWN },
7637 { EL_TUBE_LEFT_UP, MV_LEFT | MV_UP },
7638 { EL_TUBE_LEFT_DOWN, MV_LEFT | MV_DOWN },
7639 { EL_TUBE_RIGHT_UP, MV_RIGHT | MV_UP },
7640 { EL_TUBE_RIGHT_DOWN, MV_RIGHT | MV_DOWN },
7641 { -1, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN }
7644 while (tube_leave_directions[i][0] != tube_element)
7647 if (tube_leave_directions[i][0] == -1) /* should not happen */
7651 if (!(tube_leave_directions[i][1] & move_direction))
7652 return MF_NO_ACTION; /* tube has no opening in this direction */
7655 element = Feld[x][y];
7657 if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
7658 game.engine_version >= VERSION_IDENT(2,2,0,0))
7659 return MF_NO_ACTION;
7663 case EL_SP_PORT_LEFT:
7664 case EL_SP_PORT_RIGHT:
7666 case EL_SP_PORT_DOWN:
7667 case EL_SP_PORT_HORIZONTAL:
7668 case EL_SP_PORT_VERTICAL:
7669 case EL_SP_PORT_ANY:
7670 case EL_SP_GRAVITY_PORT_LEFT:
7671 case EL_SP_GRAVITY_PORT_RIGHT:
7672 case EL_SP_GRAVITY_PORT_UP:
7673 case EL_SP_GRAVITY_PORT_DOWN:
7675 element != EL_SP_PORT_LEFT &&
7676 element != EL_SP_GRAVITY_PORT_LEFT &&
7677 element != EL_SP_PORT_HORIZONTAL &&
7678 element != EL_SP_PORT_ANY) ||
7680 element != EL_SP_PORT_RIGHT &&
7681 element != EL_SP_GRAVITY_PORT_RIGHT &&
7682 element != EL_SP_PORT_HORIZONTAL &&
7683 element != EL_SP_PORT_ANY) ||
7685 element != EL_SP_PORT_UP &&
7686 element != EL_SP_GRAVITY_PORT_UP &&
7687 element != EL_SP_PORT_VERTICAL &&
7688 element != EL_SP_PORT_ANY) ||
7690 element != EL_SP_PORT_DOWN &&
7691 element != EL_SP_GRAVITY_PORT_DOWN &&
7692 element != EL_SP_PORT_VERTICAL &&
7693 element != EL_SP_PORT_ANY) ||
7694 !IN_LEV_FIELD(nextx, nexty) ||
7695 !IS_FREE(nextx, nexty))
7696 return MF_NO_ACTION;
7698 if (element == EL_SP_GRAVITY_PORT_LEFT ||
7699 element == EL_SP_GRAVITY_PORT_RIGHT ||
7700 element == EL_SP_GRAVITY_PORT_UP ||
7701 element == EL_SP_GRAVITY_PORT_DOWN)
7702 game.gravity = !game.gravity;
7704 /* automatically move to the next field with double speed */
7705 player->programmed_action = move_direction;
7706 DOUBLE_PLAYER_SPEED(player);
7708 PlaySoundLevel(x, y, SND_CLASS_SP_PORT_PASSING);
7712 case EL_TUBE_VERTICAL:
7713 case EL_TUBE_HORIZONTAL:
7714 case EL_TUBE_VERTICAL_LEFT:
7715 case EL_TUBE_VERTICAL_RIGHT:
7716 case EL_TUBE_HORIZONTAL_UP:
7717 case EL_TUBE_HORIZONTAL_DOWN:
7718 case EL_TUBE_LEFT_UP:
7719 case EL_TUBE_LEFT_DOWN:
7720 case EL_TUBE_RIGHT_UP:
7721 case EL_TUBE_RIGHT_DOWN:
7724 int tube_enter_directions[][2] =
7726 { EL_TUBE_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
7727 { EL_TUBE_VERTICAL, MV_UP | MV_DOWN },
7728 { EL_TUBE_HORIZONTAL, MV_LEFT | MV_RIGHT },
7729 { EL_TUBE_VERTICAL_LEFT, MV_RIGHT | MV_UP | MV_DOWN },
7730 { EL_TUBE_VERTICAL_RIGHT, MV_LEFT | MV_UP | MV_DOWN },
7731 { EL_TUBE_HORIZONTAL_UP, MV_LEFT | MV_RIGHT | MV_DOWN },
7732 { EL_TUBE_HORIZONTAL_DOWN, MV_LEFT | MV_RIGHT | MV_UP },
7733 { EL_TUBE_LEFT_UP, MV_RIGHT | MV_DOWN },
7734 { EL_TUBE_LEFT_DOWN, MV_RIGHT | MV_UP },
7735 { EL_TUBE_RIGHT_UP, MV_LEFT | MV_DOWN },
7736 { EL_TUBE_RIGHT_DOWN, MV_LEFT | MV_UP },
7737 { -1, MV_NO_MOVING }
7740 while (tube_enter_directions[i][0] != element)
7743 if (tube_enter_directions[i][0] == -1) /* should not happen */
7747 if (!(tube_enter_directions[i][1] & move_direction))
7748 return MF_NO_ACTION; /* tube has no opening in this direction */
7750 PlaySoundLevel(x, y, SND_CLASS_TUBE_WALKING);
7756 if (IS_WALKABLE(element))
7758 int sound_action = ACTION_WALKING;
7760 if (element >= EL_GATE_1 && element <= EL_GATE_4)
7762 if (!player->key[element - EL_GATE_1])
7763 return MF_NO_ACTION;
7765 else if (element >= EL_GATE_1_GRAY && element <= EL_GATE_4_GRAY)
7767 if (!player->key[element - EL_GATE_1_GRAY])
7768 return MF_NO_ACTION;
7770 else if (element == EL_EXIT_OPEN ||
7771 element == EL_SP_EXIT_OPEN ||
7772 element == EL_SP_EXIT_OPENING)
7774 sound_action = ACTION_PASSING; /* player is passing exit */
7776 else if (element == EL_EMPTY)
7778 sound_action = ACTION_MOVING; /* nothing to walk on */
7781 /* play sound from background or player, whatever is available */
7782 if (element_info[element].sound[sound_action] != SND_UNDEFINED)
7783 PlaySoundLevelElementAction(x, y, element, sound_action);
7785 PlaySoundLevelElementAction(x, y, player->element_nr, sound_action);
7789 else if (IS_PASSABLE(element))
7791 if (!IN_LEV_FIELD(nextx, nexty) || !IS_FREE(nextx, nexty))
7792 return MF_NO_ACTION;
7795 if (CAN_MOVE(element)) /* only fixed elements can be passed! */
7796 return MF_NO_ACTION;
7799 if (element >= EL_EM_GATE_1 && element <= EL_EM_GATE_4)
7801 if (!player->key[element - EL_EM_GATE_1])
7802 return MF_NO_ACTION;
7804 else if (element >= EL_EM_GATE_1_GRAY && element <= EL_EM_GATE_4_GRAY)
7806 if (!player->key[element - EL_EM_GATE_1_GRAY])
7807 return MF_NO_ACTION;
7810 /* automatically move to the next field with double speed */
7811 player->programmed_action = move_direction;
7812 DOUBLE_PLAYER_SPEED(player);
7814 PlaySoundLevelAction(x, y, ACTION_PASSING);
7818 else if (IS_DIGGABLE(element))
7822 if (mode != DF_SNAP)
7825 GfxElement[x][y] = GFX_ELEMENT(element);
7828 (GFX_CRUMBLED(element) ? EL_SAND : GFX_ELEMENT(element));
7830 player->is_digging = TRUE;
7833 PlaySoundLevelElementAction(x, y, element, ACTION_DIGGING);
7835 CheckTriggeredElementChange(x, y, element, CE_OTHER_GETS_DIGGED);
7838 if (mode == DF_SNAP)
7839 TestIfElementTouchesCustomElement(x, y); /* for empty space */
7844 else if (IS_COLLECTIBLE(element))
7848 if (mode != DF_SNAP)
7850 GfxElement[x][y] = element;
7851 player->is_collecting = TRUE;
7854 if (element == EL_SPEED_PILL)
7855 player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
7856 else if (element == EL_EXTRA_TIME && level.time > 0)
7859 DrawText(DX_TIME, DY_TIME, int2str(TimeLeft, 3), FONT_TEXT_2);
7861 else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
7863 player->shield_normal_time_left += 10;
7864 if (element == EL_SHIELD_DEADLY)
7865 player->shield_deadly_time_left += 10;
7867 else if (element == EL_DYNAMITE || element == EL_SP_DISK_RED)
7869 if (player->inventory_size < MAX_INVENTORY_SIZE)
7870 player->inventory_element[player->inventory_size++] = element;
7872 DrawText(DX_DYNAMITE, DY_DYNAMITE,
7873 int2str(local_player->inventory_size, 3), FONT_TEXT_2);
7875 else if (element == EL_DYNABOMB_INCREASE_NUMBER)
7877 player->dynabomb_count++;
7878 player->dynabombs_left++;
7880 else if (element == EL_DYNABOMB_INCREASE_SIZE)
7882 player->dynabomb_size++;
7884 else if (element == EL_DYNABOMB_INCREASE_POWER)
7886 player->dynabomb_xl = TRUE;
7888 else if ((element >= EL_KEY_1 && element <= EL_KEY_4) ||
7889 (element >= EL_EM_KEY_1 && element <= EL_EM_KEY_4))
7891 int key_nr = (element >= EL_KEY_1 && element <= EL_KEY_4 ?
7892 element - EL_KEY_1 : element - EL_EM_KEY_1);
7894 player->key[key_nr] = TRUE;
7896 DrawMiniGraphicExt(drawto, DX_KEYS + key_nr * MINI_TILEX, DY_KEYS,
7897 el2edimg(EL_KEY_1 + key_nr));
7898 redraw_mask |= REDRAW_DOOR_1;
7900 else if (IS_ENVELOPE(element))
7903 player->show_envelope = element;
7905 ShowEnvelope(element - EL_ENVELOPE_1);
7908 else if (IS_DROPPABLE(element)) /* can be collected and dropped */
7912 for (i=0; i < element_info[element].collect_count; i++)
7913 if (player->inventory_size < MAX_INVENTORY_SIZE)
7914 player->inventory_element[player->inventory_size++] = element;
7916 DrawText(DX_DYNAMITE, DY_DYNAMITE,
7917 int2str(local_player->inventory_size, 3), FONT_TEXT_2);
7919 else if (element_info[element].collect_count > 0)
7921 local_player->gems_still_needed -=
7922 element_info[element].collect_count;
7923 if (local_player->gems_still_needed < 0)
7924 local_player->gems_still_needed = 0;
7926 DrawText(DX_EMERALDS, DY_EMERALDS,
7927 int2str(local_player->gems_still_needed, 3), FONT_TEXT_2);
7930 RaiseScoreElement(element);
7931 PlaySoundLevelElementAction(x, y, element, ACTION_COLLECTING);
7933 CheckTriggeredElementChange(x, y, element, CE_OTHER_GETS_COLLECTED);
7936 if (mode == DF_SNAP)
7937 TestIfElementTouchesCustomElement(x, y); /* for empty space */
7942 else if (IS_PUSHABLE(element))
7944 if (mode == DF_SNAP && element != EL_BD_ROCK)
7945 return MF_NO_ACTION;
7947 if (CAN_FALL(element) && dy)
7948 return MF_NO_ACTION;
7950 if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
7951 !(element == EL_SPRING && use_spring_bug))
7952 return MF_NO_ACTION;
7955 if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
7956 ((move_direction & MV_VERTICAL &&
7957 ((element_info[element].move_pattern & MV_LEFT &&
7958 IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
7959 (element_info[element].move_pattern & MV_RIGHT &&
7960 IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
7961 (move_direction & MV_HORIZONTAL &&
7962 ((element_info[element].move_pattern & MV_UP &&
7963 IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
7964 (element_info[element].move_pattern & MV_DOWN &&
7965 IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
7966 return MF_NO_ACTION;
7970 /* do not push elements already moving away faster than player */
7971 if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
7972 ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
7973 return MF_NO_ACTION;
7975 if (element == EL_SPRING && MovDir[x][y] != MV_NO_MOVING)
7976 return MF_NO_ACTION;
7980 if (game.engine_version >= VERSION_IDENT(3,0,7,1))
7982 if (player->push_delay_value == -1)
7983 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
7985 else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
7987 if (!player->is_pushing)
7988 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
7992 if (game.engine_version >= VERSION_IDENT(2,2,0,7) &&
7993 (game.engine_version < VERSION_IDENT(3,0,7,1) ||
7994 !player_is_pushing))
7995 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
7998 if (!player->is_pushing &&
7999 game.engine_version >= VERSION_IDENT(2,2,0,7))
8000 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
8004 printf("::: push delay: %ld [%d, %d] [%d]\n",
8005 player->push_delay_value, FrameCounter, game.engine_version,
8006 player->is_pushing);
8009 player->is_pushing = TRUE;
8011 if (!(IN_LEV_FIELD(nextx, nexty) &&
8012 (IS_FREE(nextx, nexty) ||
8013 (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY &&
8014 IS_SB_ELEMENT(element)))))
8015 return MF_NO_ACTION;
8017 if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
8018 return MF_NO_ACTION;
8020 if (player->push_delay == 0) /* new pushing; restart delay */
8021 player->push_delay = FrameCounter;
8023 if (!FrameReached(&player->push_delay, player->push_delay_value) &&
8024 !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
8025 element != EL_SPRING && element != EL_BALLOON)
8027 /* make sure that there is no move delay before next try to push */
8028 if (game.engine_version >= VERSION_IDENT(3,0,7,1))
8029 player->move_delay = INITIAL_MOVE_DELAY_OFF;
8031 return MF_NO_ACTION;
8035 printf("::: NOW PUSHING... [%d]\n", FrameCounter);
8038 if (IS_SB_ELEMENT(element))
8040 if (element == EL_SOKOBAN_FIELD_FULL)
8042 Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
8043 local_player->sokobanfields_still_needed++;
8046 if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
8048 Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
8049 local_player->sokobanfields_still_needed--;
8052 Feld[x][y] = EL_SOKOBAN_OBJECT;
8054 if (Back[x][y] == Back[nextx][nexty])
8055 PlaySoundLevelAction(x, y, ACTION_PUSHING);
8056 else if (Back[x][y] != 0)
8057 PlaySoundLevelElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
8060 PlaySoundLevelElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
8063 if (local_player->sokobanfields_still_needed == 0 &&
8064 game.emulation == EMU_SOKOBAN)
8066 player->LevelSolved = player->GameOver = TRUE;
8067 PlaySoundLevel(x, y, SND_GAME_SOKOBAN_SOLVING);
8071 PlaySoundLevelElementAction(x, y, element, ACTION_PUSHING);
8073 InitMovingField(x, y, move_direction);
8074 GfxAction[x][y] = ACTION_PUSHING;
8076 if (mode == DF_SNAP)
8077 ContinueMoving(x, y);
8079 MovPos[x][y] = (dx != 0 ? dx : dy);
8081 Pushed[x][y] = TRUE;
8082 Pushed[nextx][nexty] = TRUE;
8084 if (game.engine_version < VERSION_IDENT(2,2,0,7))
8085 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
8087 player->push_delay_value = -1; /* get new value later */
8089 CheckTriggeredElementSideChange(x, y, element, dig_side,
8090 CE_OTHER_GETS_PUSHED);
8091 CheckElementSideChange(x, y, element, dig_side,
8092 CE_PUSHED_BY_PLAYER, -1);
8096 else if (IS_SWITCHABLE(element))
8098 if (PLAYER_SWITCHING(player, x, y))
8101 player->is_switching = TRUE;
8102 player->switch_x = x;
8103 player->switch_y = y;
8105 PlaySoundLevelElementAction(x, y, element, ACTION_ACTIVATING);
8107 if (element == EL_ROBOT_WHEEL)
8109 Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
8113 DrawLevelField(x, y);
8115 else if (element == EL_SP_TERMINAL)
8119 for (yy=0; yy < lev_fieldy; yy++) for (xx=0; xx < lev_fieldx; xx++)
8121 if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
8123 else if (Feld[xx][yy] == EL_SP_TERMINAL)
8124 Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
8127 else if (IS_BELT_SWITCH(element))
8129 ToggleBeltSwitch(x, y);
8131 else if (element == EL_SWITCHGATE_SWITCH_UP ||
8132 element == EL_SWITCHGATE_SWITCH_DOWN)
8134 ToggleSwitchgateSwitch(x, y);
8136 else if (element == EL_LIGHT_SWITCH ||
8137 element == EL_LIGHT_SWITCH_ACTIVE)
8139 ToggleLightSwitch(x, y);
8142 PlaySoundLevel(x, y, element == EL_LIGHT_SWITCH ?
8143 SND_LIGHT_SWITCH_ACTIVATING :
8144 SND_LIGHT_SWITCH_DEACTIVATING);
8147 else if (element == EL_TIMEGATE_SWITCH)
8149 ActivateTimegateSwitch(x, y);
8151 else if (element == EL_BALLOON_SWITCH_LEFT ||
8152 element == EL_BALLOON_SWITCH_RIGHT ||
8153 element == EL_BALLOON_SWITCH_UP ||
8154 element == EL_BALLOON_SWITCH_DOWN ||
8155 element == EL_BALLOON_SWITCH_ANY)
8157 if (element == EL_BALLOON_SWITCH_ANY)
8158 game.balloon_dir = move_direction;
8160 game.balloon_dir = (element == EL_BALLOON_SWITCH_LEFT ? MV_LEFT :
8161 element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
8162 element == EL_BALLOON_SWITCH_UP ? MV_UP :
8163 element == EL_BALLOON_SWITCH_DOWN ? MV_DOWN :
8166 else if (element == EL_LAMP)
8168 Feld[x][y] = EL_LAMP_ACTIVE;
8169 local_player->lights_still_needed--;
8171 DrawLevelField(x, y);
8173 else if (element == EL_TIME_ORB_FULL)
8175 Feld[x][y] = EL_TIME_ORB_EMPTY;
8177 DrawText(DX_TIME, DY_TIME, int2str(TimeLeft, 3), FONT_TEXT_2);
8179 DrawLevelField(x, y);
8182 PlaySoundStereo(SND_TIME_ORB_FULL_COLLECTING, SOUND_MIDDLE);
8190 if (!PLAYER_SWITCHING(player, x, y))
8192 player->is_switching = TRUE;
8193 player->switch_x = x;
8194 player->switch_y = y;
8196 CheckTriggeredElementSideChange(x, y, element, dig_side,
8197 CE_OTHER_IS_SWITCHING);
8198 CheckElementSideChange(x, y, element, dig_side, CE_SWITCHED, -1);
8201 CheckTriggeredElementSideChange(x, y, element, dig_side,
8202 CE_OTHER_GETS_PRESSED);
8203 CheckElementSideChange(x, y, element, dig_side,
8204 CE_PRESSED_BY_PLAYER, -1);
8207 return MF_NO_ACTION;
8210 player->push_delay = 0;
8212 if (Feld[x][y] != element) /* really digged/collected something */
8213 player->is_collecting = !player->is_digging;
8218 boolean SnapField(struct PlayerInfo *player, int dx, int dy)
8220 int jx = player->jx, jy = player->jy;
8221 int x = jx + dx, y = jy + dy;
8222 int snap_direction = (dx == -1 ? MV_LEFT :
8223 dx == +1 ? MV_RIGHT :
8225 dy == +1 ? MV_DOWN : MV_NO_MOVING);
8227 if (player->MovPos && game.engine_version >= VERSION_IDENT(2,2,0,0))
8230 if (!player->active || !IN_LEV_FIELD(x, y))
8238 if (player->MovPos == 0)
8239 player->is_pushing = FALSE;
8241 player->is_snapping = FALSE;
8243 if (player->MovPos == 0)
8245 player->is_moving = FALSE;
8246 player->is_digging = FALSE;
8247 player->is_collecting = FALSE;
8253 if (player->is_snapping)
8256 player->MovDir = snap_direction;
8258 player->is_moving = FALSE;
8259 player->is_digging = FALSE;
8260 player->is_collecting = FALSE;
8262 if (DigField(player, x, y, 0, 0, DF_SNAP) == MF_NO_ACTION)
8265 player->is_snapping = TRUE;
8267 player->is_moving = FALSE;
8268 player->is_digging = FALSE;
8269 player->is_collecting = FALSE;
8271 DrawLevelField(x, y);
8277 boolean DropElement(struct PlayerInfo *player)
8279 int jx = player->jx, jy = player->jy;
8282 if (!player->active || player->MovPos)
8285 old_element = Feld[jx][jy];
8287 /* check if player has anything that can be dropped */
8288 if (player->inventory_size == 0 && player->dynabombs_left == 0)
8291 /* check if anything can be dropped at the current position */
8292 if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
8295 /* collected custom elements can only be dropped on empty fields */
8296 if (player->inventory_size > 0 &&
8297 IS_CUSTOM_ELEMENT(player->inventory_element[player->inventory_size - 1])
8298 && old_element != EL_EMPTY)
8301 if (old_element != EL_EMPTY)
8302 Back[jx][jy] = old_element; /* store old element on this field */
8304 MovDelay[jx][jy] = 96;
8306 ResetGfxAnimation(jx, jy);
8307 ResetRandomAnimationValue(jx, jy);
8309 if (player->inventory_size > 0)
8311 int new_element = player->inventory_element[--player->inventory_size];
8313 Feld[jx][jy] = (new_element == EL_DYNAMITE ? EL_DYNAMITE_ACTIVE :
8314 new_element == EL_SP_DISK_RED ? EL_SP_DISK_RED_ACTIVE :
8317 DrawText(DX_DYNAMITE, DY_DYNAMITE,
8318 int2str(local_player->inventory_size, 3), FONT_TEXT_2);
8320 if (IN_SCR_FIELD(SCREENX(jx), SCREENY(jy)))
8321 DrawGraphicThruMask(SCREENX(jx), SCREENY(jy), el2img(Feld[jx][jy]), 0);
8323 PlaySoundLevelAction(jx, jy, ACTION_DROPPING);
8325 CheckTriggeredElementChange(jx, jy, new_element, CE_OTHER_GETS_DROPPED);
8326 CheckElementChange(jx, jy, new_element, CE_DROPPED_BY_PLAYER);
8328 TestIfElementTouchesCustomElement(jx, jy);
8330 else /* player is dropping a dyna bomb */
8332 player->dynabombs_left--;
8335 EL_DYNABOMB_PLAYER_1_ACTIVE + (player->element_nr - EL_PLAYER_1);
8337 if (IN_SCR_FIELD(SCREENX(jx), SCREENY(jy)))
8338 DrawGraphicThruMask(SCREENX(jx), SCREENY(jy), el2img(Feld[jx][jy]), 0);
8340 PlaySoundLevelAction(jx, jy, ACTION_DROPPING);
8346 /* ------------------------------------------------------------------------- */
8347 /* game sound playing functions */
8348 /* ------------------------------------------------------------------------- */
8350 static int *loop_sound_frame = NULL;
8351 static int *loop_sound_volume = NULL;
8353 void InitPlaySoundLevel()
8355 int num_sounds = getSoundListSize();
8357 if (loop_sound_frame != NULL)
8358 free(loop_sound_frame);
8360 if (loop_sound_volume != NULL)
8361 free(loop_sound_volume);
8363 loop_sound_frame = checked_calloc(num_sounds * sizeof(int));
8364 loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
8367 static void PlaySoundLevel(int x, int y, int nr)
8369 int sx = SCREENX(x), sy = SCREENY(y);
8370 int volume, stereo_position;
8371 int max_distance = 8;
8372 int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
8374 if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
8375 (!setup.sound_loops && IS_LOOP_SOUND(nr)))
8378 if (!IN_LEV_FIELD(x, y) ||
8379 sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
8380 sy < -max_distance || sy >= SCR_FIELDY + max_distance)
8383 volume = SOUND_MAX_VOLUME;
8385 if (!IN_SCR_FIELD(sx, sy))
8387 int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
8388 int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
8390 volume -= volume * (dx > dy ? dx : dy) / max_distance;
8393 stereo_position = (SOUND_MAX_LEFT +
8394 (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
8395 (SCR_FIELDX + 2 * max_distance));
8397 if (IS_LOOP_SOUND(nr))
8399 /* This assures that quieter loop sounds do not overwrite louder ones,
8400 while restarting sound volume comparison with each new game frame. */
8402 if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
8405 loop_sound_volume[nr] = volume;
8406 loop_sound_frame[nr] = FrameCounter;
8409 PlaySoundExt(nr, volume, stereo_position, type);
8412 static void PlaySoundLevelNearest(int x, int y, int sound_action)
8414 PlaySoundLevel(x < LEVELX(BX1) ? LEVELX(BX1) :
8415 x > LEVELX(BX2) ? LEVELX(BX2) : x,
8416 y < LEVELY(BY1) ? LEVELY(BY1) :
8417 y > LEVELY(BY2) ? LEVELY(BY2) : y,
8421 static void PlaySoundLevelAction(int x, int y, int action)
8423 PlaySoundLevelElementAction(x, y, Feld[x][y], action);
8426 static void PlaySoundLevelElementAction(int x, int y, int element, int action)
8428 int sound_effect = element_info[element].sound[action];
8430 if (sound_effect != SND_UNDEFINED)
8431 PlaySoundLevel(x, y, sound_effect);
8434 static void PlaySoundLevelActionIfLoop(int x, int y, int action)
8436 int sound_effect = element_info[Feld[x][y]].sound[action];
8438 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
8439 PlaySoundLevel(x, y, sound_effect);
8442 static void StopSoundLevelActionIfLoop(int x, int y, int action)
8444 int sound_effect = element_info[Feld[x][y]].sound[action];
8446 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
8447 StopSoundExt(sound_effect, SND_CTRL_STOP_SOUND);
8450 void RaiseScore(int value)
8452 local_player->score += value;
8453 DrawText(DX_SCORE, DY_SCORE, int2str(local_player->score, 5), FONT_TEXT_2);
8456 void RaiseScoreElement(int element)
8462 case EL_EMERALD_YELLOW:
8463 case EL_EMERALD_RED:
8464 case EL_EMERALD_PURPLE:
8465 case EL_SP_INFOTRON:
8466 RaiseScore(level.score[SC_EMERALD]);
8469 RaiseScore(level.score[SC_DIAMOND]);
8472 RaiseScore(level.score[SC_CRYSTAL]);
8475 RaiseScore(level.score[SC_PEARL]);
8478 case EL_BD_BUTTERFLY:
8479 case EL_SP_ELECTRON:
8480 RaiseScore(level.score[SC_BUG]);
8484 case EL_SP_SNIKSNAK:
8485 RaiseScore(level.score[SC_SPACESHIP]);
8488 case EL_DARK_YAMYAM:
8489 RaiseScore(level.score[SC_YAMYAM]);
8492 RaiseScore(level.score[SC_ROBOT]);
8495 RaiseScore(level.score[SC_PACMAN]);
8498 RaiseScore(level.score[SC_NUT]);
8501 case EL_SP_DISK_RED:
8502 case EL_DYNABOMB_INCREASE_NUMBER:
8503 case EL_DYNABOMB_INCREASE_SIZE:
8504 case EL_DYNABOMB_INCREASE_POWER:
8505 RaiseScore(level.score[SC_DYNAMITE]);
8507 case EL_SHIELD_NORMAL:
8508 case EL_SHIELD_DEADLY:
8509 RaiseScore(level.score[SC_SHIELD]);
8512 RaiseScore(level.score[SC_TIME_BONUS]);
8518 RaiseScore(level.score[SC_KEY]);
8521 RaiseScore(element_info[element].collect_score);
8526 void RequestQuitGame(boolean ask_if_really_quit)
8528 if (AllPlayersGone ||
8529 !ask_if_really_quit ||
8530 level_editor_test_game ||
8531 Request("Do you really want to quit the game ?",
8532 REQ_ASK | REQ_STAY_CLOSED))
8534 #if defined(PLATFORM_UNIX)
8535 if (options.network)
8536 SendToServer_StopPlaying();
8540 game_status = GAME_MODE_MAIN;
8546 OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
8551 /* ---------- new game button stuff ---------------------------------------- */
8553 /* graphic position values for game buttons */
8554 #define GAME_BUTTON_XSIZE 30
8555 #define GAME_BUTTON_YSIZE 30
8556 #define GAME_BUTTON_XPOS 5
8557 #define GAME_BUTTON_YPOS 215
8558 #define SOUND_BUTTON_XPOS 5
8559 #define SOUND_BUTTON_YPOS (GAME_BUTTON_YPOS + GAME_BUTTON_YSIZE)
8561 #define GAME_BUTTON_STOP_XPOS (GAME_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
8562 #define GAME_BUTTON_PAUSE_XPOS (GAME_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
8563 #define GAME_BUTTON_PLAY_XPOS (GAME_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
8564 #define SOUND_BUTTON_MUSIC_XPOS (SOUND_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
8565 #define SOUND_BUTTON_LOOPS_XPOS (SOUND_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
8566 #define SOUND_BUTTON_SIMPLE_XPOS (SOUND_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
8573 } gamebutton_info[NUM_GAME_BUTTONS] =
8576 GAME_BUTTON_STOP_XPOS, GAME_BUTTON_YPOS,
8581 GAME_BUTTON_PAUSE_XPOS, GAME_BUTTON_YPOS,
8586 GAME_BUTTON_PLAY_XPOS, GAME_BUTTON_YPOS,
8591 SOUND_BUTTON_MUSIC_XPOS, SOUND_BUTTON_YPOS,
8592 SOUND_CTRL_ID_MUSIC,
8593 "background music on/off"
8596 SOUND_BUTTON_LOOPS_XPOS, SOUND_BUTTON_YPOS,
8597 SOUND_CTRL_ID_LOOPS,
8598 "sound loops on/off"
8601 SOUND_BUTTON_SIMPLE_XPOS, SOUND_BUTTON_YPOS,
8602 SOUND_CTRL_ID_SIMPLE,
8603 "normal sounds on/off"
8607 void CreateGameButtons()
8611 for (i=0; i<NUM_GAME_BUTTONS; i++)
8613 Bitmap *gd_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
8614 struct GadgetInfo *gi;
8617 unsigned long event_mask;
8618 int gd_xoffset, gd_yoffset;
8619 int gd_x1, gd_x2, gd_y1, gd_y2;
8622 gd_xoffset = gamebutton_info[i].x;
8623 gd_yoffset = gamebutton_info[i].y;
8624 gd_x1 = DOOR_GFX_PAGEX4 + gd_xoffset;
8625 gd_x2 = DOOR_GFX_PAGEX3 + gd_xoffset;
8627 if (id == GAME_CTRL_ID_STOP ||
8628 id == GAME_CTRL_ID_PAUSE ||
8629 id == GAME_CTRL_ID_PLAY)
8631 button_type = GD_TYPE_NORMAL_BUTTON;
8633 event_mask = GD_EVENT_RELEASED;
8634 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
8635 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
8639 button_type = GD_TYPE_CHECK_BUTTON;
8641 ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
8642 (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
8643 (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
8644 event_mask = GD_EVENT_PRESSED;
8645 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset;
8646 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
8649 gi = CreateGadget(GDI_CUSTOM_ID, id,
8650 GDI_INFO_TEXT, gamebutton_info[i].infotext,
8651 GDI_X, DX + gd_xoffset,
8652 GDI_Y, DY + gd_yoffset,
8653 GDI_WIDTH, GAME_BUTTON_XSIZE,
8654 GDI_HEIGHT, GAME_BUTTON_YSIZE,
8655 GDI_TYPE, button_type,
8656 GDI_STATE, GD_BUTTON_UNPRESSED,
8657 GDI_CHECKED, checked,
8658 GDI_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y1,
8659 GDI_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y1,
8660 GDI_ALT_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y2,
8661 GDI_ALT_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y2,
8662 GDI_EVENT_MASK, event_mask,
8663 GDI_CALLBACK_ACTION, HandleGameButtons,
8667 Error(ERR_EXIT, "cannot create gadget");
8669 game_gadget[id] = gi;
8673 void FreeGameButtons()
8677 for (i=0; i<NUM_GAME_BUTTONS; i++)
8678 FreeGadget(game_gadget[i]);
8681 static void MapGameButtons()
8685 for (i=0; i<NUM_GAME_BUTTONS; i++)
8686 MapGadget(game_gadget[i]);
8689 void UnmapGameButtons()
8693 for (i=0; i<NUM_GAME_BUTTONS; i++)
8694 UnmapGadget(game_gadget[i]);
8697 static void HandleGameButtons(struct GadgetInfo *gi)
8699 int id = gi->custom_id;
8701 if (game_status != GAME_MODE_PLAYING)
8706 case GAME_CTRL_ID_STOP:
8707 RequestQuitGame(TRUE);
8710 case GAME_CTRL_ID_PAUSE:
8711 if (options.network)
8713 #if defined(PLATFORM_UNIX)
8715 SendToServer_ContinuePlaying();
8717 SendToServer_PausePlaying();
8721 TapeTogglePause(TAPE_TOGGLE_MANUAL);
8724 case GAME_CTRL_ID_PLAY:
8727 #if defined(PLATFORM_UNIX)
8728 if (options.network)
8729 SendToServer_ContinuePlaying();
8733 tape.pausing = FALSE;
8734 DrawVideoDisplay(VIDEO_STATE_PAUSE_OFF,0);
8739 case SOUND_CTRL_ID_MUSIC:
8740 if (setup.sound_music)
8742 setup.sound_music = FALSE;
8745 else if (audio.music_available)
8747 setup.sound = setup.sound_music = TRUE;
8749 SetAudioMode(setup.sound);
8750 PlayMusic(level_nr);
8754 case SOUND_CTRL_ID_LOOPS:
8755 if (setup.sound_loops)
8756 setup.sound_loops = FALSE;
8757 else if (audio.loops_available)
8759 setup.sound = setup.sound_loops = TRUE;
8760 SetAudioMode(setup.sound);
8764 case SOUND_CTRL_ID_SIMPLE:
8765 if (setup.sound_simple)
8766 setup.sound_simple = FALSE;
8767 else if (audio.sound_available)
8769 setup.sound = setup.sound_simple = TRUE;
8770 SetAudioMode(setup.sound);