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 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 GfxAction[x][y] = ACTION_DEFAULT;
1154 GfxRandom[x][y] = INIT_GFX_RANDOM();
1155 GfxElement[x][y] = EL_UNDEFINED;
1159 for(y=0; y<lev_fieldy; y++)
1161 for(x=0; x<lev_fieldx; x++)
1163 if (emulate_bd && !IS_BD_ELEMENT(Feld[x][y]))
1165 if (emulate_sb && !IS_SB_ELEMENT(Feld[x][y]))
1167 if (emulate_sp && !IS_SP_ELEMENT(Feld[x][y]))
1170 InitField(x, y, TRUE);
1176 game.emulation = (emulate_bd ? EMU_BOULDERDASH :
1177 emulate_sb ? EMU_SOKOBAN :
1178 emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
1180 /* correct non-moving belts to start moving left */
1182 if (game.belt_dir[i] == MV_NO_MOVING)
1183 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
1185 /* check if any connected player was not found in playfield */
1186 for (i=0; i<MAX_PLAYERS; i++)
1188 struct PlayerInfo *player = &stored_player[i];
1190 if (player->connected && !player->present)
1192 for (j=0; j<MAX_PLAYERS; j++)
1194 struct PlayerInfo *some_player = &stored_player[j];
1195 int jx = some_player->jx, jy = some_player->jy;
1197 /* assign first free player found that is present in the playfield */
1198 if (some_player->present && !some_player->connected)
1200 player->present = TRUE;
1201 player->active = TRUE;
1202 some_player->present = FALSE;
1204 StorePlayer[jx][jy] = player->element_nr;
1205 player->jx = player->last_jx = jx;
1206 player->jy = player->last_jy = jy;
1216 /* when playing a tape, eliminate all players who do not participate */
1218 for (i=0; i<MAX_PLAYERS; i++)
1220 if (stored_player[i].active && !tape.player_participates[i])
1222 struct PlayerInfo *player = &stored_player[i];
1223 int jx = player->jx, jy = player->jy;
1225 player->active = FALSE;
1226 StorePlayer[jx][jy] = 0;
1227 Feld[jx][jy] = EL_EMPTY;
1231 else if (!options.network && !setup.team_mode) /* && !tape.playing */
1233 /* when in single player mode, eliminate all but the first active player */
1235 for (i=0; i<MAX_PLAYERS; i++)
1237 if (stored_player[i].active)
1239 for (j=i+1; j<MAX_PLAYERS; j++)
1241 if (stored_player[j].active)
1243 struct PlayerInfo *player = &stored_player[j];
1244 int jx = player->jx, jy = player->jy;
1246 player->active = FALSE;
1247 StorePlayer[jx][jy] = 0;
1248 Feld[jx][jy] = EL_EMPTY;
1255 /* when recording the game, store which players take part in the game */
1258 for (i=0; i<MAX_PLAYERS; i++)
1259 if (stored_player[i].active)
1260 tape.player_participates[i] = TRUE;
1265 for (i=0; i<MAX_PLAYERS; i++)
1267 struct PlayerInfo *player = &stored_player[i];
1269 printf("Player %d: present == %d, connected == %d, active == %d.\n",
1274 if (local_player == player)
1275 printf("Player %d is local player.\n", i+1);
1279 if (BorderElement == EL_EMPTY)
1282 SBX_Right = lev_fieldx - SCR_FIELDX;
1284 SBY_Lower = lev_fieldy - SCR_FIELDY;
1289 SBX_Right = lev_fieldx - SCR_FIELDX + 1;
1291 SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
1294 if (lev_fieldx + (SBX_Left == -1 ? 2 : 0) <= SCR_FIELDX)
1295 SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
1297 if (lev_fieldy + (SBY_Upper == -1 ? 2 : 0) <= SCR_FIELDY)
1298 SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
1300 /* if local player not found, look for custom element that might create
1301 the player (make some assumptions about the right custom element) */
1302 if (!local_player->present)
1304 int start_x = 0, start_y = 0;
1305 int found_rating = 0;
1306 int found_element = EL_UNDEFINED;
1308 for(y=0; y < lev_fieldy; y++) for(x=0; x < lev_fieldx; x++)
1310 int element = Feld[x][y];
1315 if (!IS_CUSTOM_ELEMENT(element))
1318 if (CAN_CHANGE(element))
1320 for (i=0; i < element_info[element].num_change_pages; i++)
1322 content = element_info[element].change_page[i].target_element;
1323 is_player = ELEM_IS_PLAYER(content);
1325 if (is_player && (found_rating < 3 || element < found_element))
1331 found_element = element;
1336 for(yy=0; yy < 3; yy++) for(xx=0; xx < 3; xx++)
1338 content = element_info[element].content[xx][yy];
1339 is_player = ELEM_IS_PLAYER(content);
1341 if (is_player && (found_rating < 2 || element < found_element))
1343 start_x = x + xx - 1;
1344 start_y = y + yy - 1;
1347 found_element = element;
1350 if (!CAN_CHANGE(element))
1353 for (i=0; i < element_info[element].num_change_pages; i++)
1355 content = element_info[element].change_page[i].content[xx][yy];
1356 is_player = ELEM_IS_PLAYER(content);
1358 if (is_player && (found_rating < 1 || element < found_element))
1360 start_x = x + xx - 1;
1361 start_y = y + yy - 1;
1364 found_element = element;
1370 scroll_x = (start_x < SBX_Left + MIDPOSX ? SBX_Left :
1371 start_x > SBX_Right + MIDPOSX ? SBX_Right :
1374 scroll_y = (start_y < SBY_Upper + MIDPOSY ? SBY_Upper :
1375 start_y > SBY_Lower + MIDPOSY ? SBY_Lower :
1381 scroll_x = (local_player->jx < SBX_Left + MIDPOSX ? SBX_Left :
1382 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
1383 local_player->jx - MIDPOSX);
1385 scroll_y = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
1386 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
1387 local_player->jy - MIDPOSY);
1389 scroll_x = SBX_Left;
1390 scroll_y = SBY_Upper;
1391 if (local_player->jx >= SBX_Left + MIDPOSX)
1392 scroll_x = (local_player->jx <= SBX_Right + MIDPOSX ?
1393 local_player->jx - MIDPOSX :
1395 if (local_player->jy >= SBY_Upper + MIDPOSY)
1396 scroll_y = (local_player->jy <= SBY_Lower + MIDPOSY ?
1397 local_player->jy - MIDPOSY :
1402 CloseDoor(DOOR_CLOSE_1);
1407 /* after drawing the level, correct some elements */
1408 if (game.timegate_time_left == 0)
1409 CloseAllOpenTimegates();
1411 if (setup.soft_scrolling)
1412 BlitBitmap(fieldbuffer, backbuffer, FX, FY, SXSIZE, SYSIZE, SX, SY);
1414 redraw_mask |= REDRAW_FROM_BACKBUFFER;
1417 /* copy default game door content to main double buffer */
1418 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
1419 DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
1422 DrawText(DX + XX_LEVEL, DY + YY_LEVEL, int2str(level_nr, 2), FONT_TEXT_2);
1425 DrawTextExt(drawto, DX + XX_EMERALDS, DY + YY_EMERALDS,
1426 int2str(level_nr, 3), FONT_LEVEL_NUMBER, BLIT_OPAQUE);
1427 BlitBitmap(drawto, drawto,
1428 DX + XX_EMERALDS, DY + YY_EMERALDS + 1,
1429 getFontWidth(FONT_LEVEL_NUMBER) * 3,
1430 getFontHeight(FONT_LEVEL_NUMBER) - 1,
1431 DX + XX_LEVEL - 1, DY + YY_LEVEL + 1);
1434 DrawGameDoorValues();
1438 game_gadget[SOUND_CTRL_ID_MUSIC]->checked = setup.sound_music;
1439 game_gadget[SOUND_CTRL_ID_LOOPS]->checked = setup.sound_loops;
1440 game_gadget[SOUND_CTRL_ID_SIMPLE]->checked = setup.sound_simple;
1444 /* copy actual game door content to door double buffer for OpenDoor() */
1445 BlitBitmap(drawto, bitmap_db_door,
1446 DX, DY, DXSIZE, DYSIZE, DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1);
1448 OpenDoor(DOOR_OPEN_ALL);
1450 PlaySoundStereo(SND_GAME_STARTING, SOUND_MIDDLE);
1451 if (setup.sound_music)
1452 PlayMusic(level_nr);
1454 KeyboardAutoRepeatOffUnlessAutoplay();
1459 printf("Player %d %sactive.\n",
1460 i + 1, (stored_player[i].active ? "" : "not "));
1464 void InitMovDir(int x, int y)
1466 int i, element = Feld[x][y];
1467 static int xy[4][2] =
1474 static int direction[3][4] =
1476 { MV_RIGHT, MV_UP, MV_LEFT, MV_DOWN },
1477 { MV_LEFT, MV_DOWN, MV_RIGHT, MV_UP },
1478 { MV_LEFT, MV_RIGHT, MV_UP, MV_DOWN }
1487 Feld[x][y] = EL_BUG;
1488 MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
1491 case EL_SPACESHIP_RIGHT:
1492 case EL_SPACESHIP_UP:
1493 case EL_SPACESHIP_LEFT:
1494 case EL_SPACESHIP_DOWN:
1495 Feld[x][y] = EL_SPACESHIP;
1496 MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
1499 case EL_BD_BUTTERFLY_RIGHT:
1500 case EL_BD_BUTTERFLY_UP:
1501 case EL_BD_BUTTERFLY_LEFT:
1502 case EL_BD_BUTTERFLY_DOWN:
1503 Feld[x][y] = EL_BD_BUTTERFLY;
1504 MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
1507 case EL_BD_FIREFLY_RIGHT:
1508 case EL_BD_FIREFLY_UP:
1509 case EL_BD_FIREFLY_LEFT:
1510 case EL_BD_FIREFLY_DOWN:
1511 Feld[x][y] = EL_BD_FIREFLY;
1512 MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
1515 case EL_PACMAN_RIGHT:
1517 case EL_PACMAN_LEFT:
1518 case EL_PACMAN_DOWN:
1519 Feld[x][y] = EL_PACMAN;
1520 MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
1523 case EL_SP_SNIKSNAK:
1524 MovDir[x][y] = MV_UP;
1527 case EL_SP_ELECTRON:
1528 MovDir[x][y] = MV_LEFT;
1535 Feld[x][y] = EL_MOLE;
1536 MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
1540 if (IS_CUSTOM_ELEMENT(element))
1542 if (element_info[element].move_direction_initial != MV_NO_MOVING)
1543 MovDir[x][y] = element_info[element].move_direction_initial;
1544 else if (element_info[element].move_pattern == MV_ALL_DIRECTIONS ||
1545 element_info[element].move_pattern == MV_TURNING_LEFT ||
1546 element_info[element].move_pattern == MV_TURNING_RIGHT)
1547 MovDir[x][y] = 1 << RND(4);
1548 else if (element_info[element].move_pattern == MV_HORIZONTAL)
1549 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
1550 else if (element_info[element].move_pattern == MV_VERTICAL)
1551 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
1552 else if (element_info[element].move_pattern & MV_ANY_DIRECTION)
1553 MovDir[x][y] = element_info[element].move_pattern;
1554 else if (element_info[element].move_pattern == MV_ALONG_LEFT_SIDE ||
1555 element_info[element].move_pattern == MV_ALONG_RIGHT_SIDE)
1559 int x1 = x + xy[i][0];
1560 int y1 = y + xy[i][1];
1562 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
1564 if (element_info[element].move_pattern == MV_ALONG_RIGHT_SIDE)
1565 MovDir[x][y] = direction[0][i];
1567 MovDir[x][y] = direction[1][i];
1576 MovDir[x][y] = 1 << RND(4);
1578 if (element != EL_BUG &&
1579 element != EL_SPACESHIP &&
1580 element != EL_BD_BUTTERFLY &&
1581 element != EL_BD_FIREFLY)
1586 int x1 = x + xy[i][0];
1587 int y1 = y + xy[i][1];
1589 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
1591 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
1593 MovDir[x][y] = direction[0][i];
1596 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
1597 element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
1599 MovDir[x][y] = direction[1][i];
1609 void InitAmoebaNr(int x, int y)
1612 int group_nr = AmoebeNachbarNr(x, y);
1616 for (i=1; i<MAX_NUM_AMOEBA; i++)
1618 if (AmoebaCnt[i] == 0)
1626 AmoebaNr[x][y] = group_nr;
1627 AmoebaCnt[group_nr]++;
1628 AmoebaCnt2[group_nr]++;
1634 boolean raise_level = FALSE;
1636 if (local_player->MovPos)
1640 if (tape.auto_play) /* tape might already be stopped here */
1641 tape.auto_play_level_solved = TRUE;
1643 if (tape.playing && tape.auto_play)
1644 tape.auto_play_level_solved = TRUE;
1647 local_player->LevelSolved = FALSE;
1649 PlaySoundStereo(SND_GAME_WINNING, SOUND_MIDDLE);
1653 if (!tape.playing && setup.sound_loops)
1654 PlaySoundExt(SND_GAME_LEVELTIME_BONUS, SOUND_MAX_VOLUME, SOUND_MIDDLE,
1655 SND_CTRL_PLAY_LOOP);
1657 while (TimeLeft > 0)
1659 if (!tape.playing && !setup.sound_loops)
1660 PlaySoundStereo(SND_GAME_LEVELTIME_BONUS, SOUND_MIDDLE);
1661 if (TimeLeft > 0 && !(TimeLeft % 10))
1662 RaiseScore(level.score[SC_TIME_BONUS]);
1663 if (TimeLeft > 100 && !(TimeLeft % 10))
1667 DrawText(DX_TIME, DY_TIME, int2str(TimeLeft, 3), FONT_TEXT_2);
1674 if (!tape.playing && setup.sound_loops)
1675 StopSound(SND_GAME_LEVELTIME_BONUS);
1677 else if (level.time == 0) /* level without time limit */
1679 if (!tape.playing && setup.sound_loops)
1680 PlaySoundExt(SND_GAME_LEVELTIME_BONUS, SOUND_MAX_VOLUME, SOUND_MIDDLE,
1681 SND_CTRL_PLAY_LOOP);
1683 while (TimePlayed < 999)
1685 if (!tape.playing && !setup.sound_loops)
1686 PlaySoundStereo(SND_GAME_LEVELTIME_BONUS, SOUND_MIDDLE);
1687 if (TimePlayed < 999 && !(TimePlayed % 10))
1688 RaiseScore(level.score[SC_TIME_BONUS]);
1689 if (TimePlayed < 900 && !(TimePlayed % 10))
1693 DrawText(DX_TIME, DY_TIME, int2str(TimePlayed, 3), FONT_TEXT_2);
1700 if (!tape.playing && setup.sound_loops)
1701 StopSound(SND_GAME_LEVELTIME_BONUS);
1704 /* close exit door after last player */
1705 if ((Feld[ExitX][ExitY] == EL_EXIT_OPEN ||
1706 Feld[ExitX][ExitY] == EL_SP_EXIT_OPEN) && AllPlayersGone)
1708 int element = Feld[ExitX][ExitY];
1710 Feld[ExitX][ExitY] = (element == EL_EXIT_OPEN ? EL_EXIT_CLOSING :
1711 EL_SP_EXIT_CLOSING);
1713 PlaySoundLevelElementAction(ExitX, ExitY, element, ACTION_CLOSING);
1716 /* Hero disappears */
1717 DrawLevelField(ExitX, ExitY);
1723 CloseDoor(DOOR_CLOSE_1);
1728 SaveTape(tape.level_nr); /* Ask to save tape */
1731 if (level_nr == leveldir_current->handicap_level)
1733 leveldir_current->handicap_level++;
1734 SaveLevelSetup_SeriesInfo();
1737 if (level_editor_test_game)
1738 local_player->score = -1; /* no highscore when playing from editor */
1739 else if (level_nr < leveldir_current->last_level)
1740 raise_level = TRUE; /* advance to next level */
1742 if ((hi_pos = NewHiScore()) >= 0)
1744 game_status = GAME_MODE_SCORES;
1745 DrawHallOfFame(hi_pos);
1754 game_status = GAME_MODE_MAIN;
1771 LoadScore(level_nr);
1773 if (strcmp(setup.player_name, EMPTY_PLAYER_NAME) == 0 ||
1774 local_player->score < highscore[MAX_SCORE_ENTRIES - 1].Score)
1777 for (k=0; k<MAX_SCORE_ENTRIES; k++)
1779 if (local_player->score > highscore[k].Score)
1781 /* player has made it to the hall of fame */
1783 if (k < MAX_SCORE_ENTRIES - 1)
1785 int m = MAX_SCORE_ENTRIES - 1;
1788 for (l=k; l<MAX_SCORE_ENTRIES; l++)
1789 if (!strcmp(setup.player_name, highscore[l].Name))
1791 if (m == k) /* player's new highscore overwrites his old one */
1797 strcpy(highscore[l].Name, highscore[l - 1].Name);
1798 highscore[l].Score = highscore[l - 1].Score;
1805 strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
1806 highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
1807 highscore[k].Score = local_player->score;
1813 else if (!strncmp(setup.player_name, highscore[k].Name,
1814 MAX_PLAYER_NAME_LEN))
1815 break; /* player already there with a higher score */
1821 SaveScore(level_nr);
1826 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
1828 if (player->GfxAction != action || player->GfxDir != dir)
1831 printf("Player frame reset! (%d => %d, %d => %d)\n",
1832 player->GfxAction, action, player->GfxDir, dir);
1835 player->GfxAction = action;
1836 player->GfxDir = dir;
1838 player->StepFrame = 0;
1842 static void ResetRandomAnimationValue(int x, int y)
1844 GfxRandom[x][y] = INIT_GFX_RANDOM();
1847 static void ResetGfxAnimation(int x, int y)
1850 GfxAction[x][y] = ACTION_DEFAULT;
1853 void InitMovingField(int x, int y, int direction)
1855 int element = Feld[x][y];
1856 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
1857 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
1861 if (!WasJustMoving[x][y] || direction != MovDir[x][y])
1862 ResetGfxAnimation(x, y);
1864 MovDir[newx][newy] = MovDir[x][y] = direction;
1866 if (Feld[newx][newy] == EL_EMPTY)
1867 Feld[newx][newy] = EL_BLOCKED;
1869 if (direction == MV_DOWN && CAN_FALL(element))
1870 GfxAction[x][y] = ACTION_FALLING;
1872 GfxAction[x][y] = ACTION_MOVING;
1874 GfxFrame[newx][newy] = GfxFrame[x][y];
1875 GfxAction[newx][newy] = GfxAction[x][y];
1876 GfxRandom[newx][newy] = GfxRandom[x][y];
1879 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
1881 int direction = MovDir[x][y];
1882 int newx = x + (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
1883 int newy = y + (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
1889 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
1891 int oldx = x, oldy = y;
1892 int direction = MovDir[x][y];
1894 if (direction == MV_LEFT)
1896 else if (direction == MV_RIGHT)
1898 else if (direction == MV_UP)
1900 else if (direction == MV_DOWN)
1903 *comes_from_x = oldx;
1904 *comes_from_y = oldy;
1907 int MovingOrBlocked2Element(int x, int y)
1909 int element = Feld[x][y];
1911 if (element == EL_BLOCKED)
1915 Blocked2Moving(x, y, &oldx, &oldy);
1916 return Feld[oldx][oldy];
1922 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
1924 /* like MovingOrBlocked2Element(), but if element is moving
1925 and (x,y) is the field the moving element is just leaving,
1926 return EL_BLOCKED instead of the element value */
1927 int element = Feld[x][y];
1929 if (IS_MOVING(x, y))
1931 if (element == EL_BLOCKED)
1935 Blocked2Moving(x, y, &oldx, &oldy);
1936 return Feld[oldx][oldy];
1945 static void RemoveField(int x, int y)
1947 Feld[x][y] = EL_EMPTY;
1954 ChangeDelay[x][y] = 0;
1955 ChangePage[x][y] = -1;
1956 Pushed[x][y] = FALSE;
1958 GfxElement[x][y] = EL_UNDEFINED;
1959 GfxAction[x][y] = ACTION_DEFAULT;
1962 void RemoveMovingField(int x, int y)
1964 int oldx = x, oldy = y, newx = x, newy = y;
1965 int element = Feld[x][y];
1966 int next_element = EL_UNDEFINED;
1968 if (element != EL_BLOCKED && !IS_MOVING(x, y))
1971 if (IS_MOVING(x, y))
1973 Moving2Blocked(x, y, &newx, &newy);
1974 if (Feld[newx][newy] != EL_BLOCKED)
1977 else if (element == EL_BLOCKED)
1979 Blocked2Moving(x, y, &oldx, &oldy);
1980 if (!IS_MOVING(oldx, oldy))
1984 if (element == EL_BLOCKED &&
1985 (Feld[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
1986 Feld[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
1987 Feld[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
1988 Feld[oldx][oldy] == EL_AMOEBA_DROPPING))
1989 next_element = get_next_element(Feld[oldx][oldy]);
1991 RemoveField(oldx, oldy);
1992 RemoveField(newx, newy);
1994 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
1996 if (next_element != EL_UNDEFINED)
1997 Feld[oldx][oldy] = next_element;
1999 DrawLevelField(oldx, oldy);
2000 DrawLevelField(newx, newy);
2003 void DrawDynamite(int x, int y)
2005 int sx = SCREENX(x), sy = SCREENY(y);
2006 int graphic = el2img(Feld[x][y]);
2009 if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
2012 if (IS_WALKABLE_INSIDE(Back[x][y]))
2016 DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
2017 else if (Store[x][y])
2018 DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
2020 frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
2023 if (Back[x][y] || Store[x][y])
2024 DrawGraphicThruMask(sx, sy, graphic, frame);
2026 DrawGraphic(sx, sy, graphic, frame);
2028 if (game.emulation == EMU_SUPAPLEX)
2029 DrawGraphic(sx, sy, IMG_SP_DISK_RED, frame);
2030 else if (Store[x][y])
2031 DrawGraphicThruMask(sx, sy, graphic, frame);
2033 DrawGraphic(sx, sy, graphic, frame);
2037 void CheckDynamite(int x, int y)
2039 if (MovDelay[x][y] != 0) /* dynamite is still waiting to explode */
2043 if (MovDelay[x][y] != 0)
2046 PlaySoundLevelActionIfLoop(x, y, ACTION_ACTIVE);
2053 StopSoundLevelActionIfLoop(x, y, ACTION_ACTIVE);
2055 if (Feld[x][y] == EL_DYNAMITE_ACTIVE ||
2056 Feld[x][y] == EL_SP_DISK_RED_ACTIVE)
2057 StopSound(SND_DYNAMITE_ACTIVE);
2059 StopSound(SND_DYNABOMB_ACTIVE);
2065 void RelocatePlayer(int x, int y, int element)
2067 struct PlayerInfo *player = &stored_player[element - EL_PLAYER_1];
2070 RemoveField(x, y); /* temporarily remove newly placed player */
2071 DrawLevelField(x, y);
2074 if (player->present)
2076 while (player->MovPos)
2078 ScrollPlayer(player, SCROLL_GO_ON);
2079 ScrollScreen(NULL, SCROLL_GO_ON);
2085 Delay(GAME_FRAME_DELAY);
2088 DrawPlayer(player); /* needed here only to cleanup last field */
2089 DrawLevelField(player->jx, player->jy); /* remove player graphic */
2091 player->is_moving = FALSE;
2094 Feld[x][y] = element;
2095 InitPlayerField(x, y, element, TRUE);
2097 if (player == local_player)
2099 int scroll_xx = -999, scroll_yy = -999;
2101 while (scroll_xx != scroll_x || scroll_yy != scroll_y)
2104 int fx = FX, fy = FY;
2106 scroll_xx = (local_player->jx < SBX_Left + MIDPOSX ? SBX_Left :
2107 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
2108 local_player->jx - MIDPOSX);
2110 scroll_yy = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
2111 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
2112 local_player->jy - MIDPOSY);
2114 dx = (scroll_xx < scroll_x ? +1 : scroll_xx > scroll_x ? -1 : 0);
2115 dy = (scroll_yy < scroll_y ? +1 : scroll_yy > scroll_y ? -1 : 0);
2120 fx += dx * TILEX / 2;
2121 fy += dy * TILEY / 2;
2123 ScrollLevel(dx, dy);
2126 /* scroll in two steps of half tile size to make things smoother */
2127 BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
2129 Delay(GAME_FRAME_DELAY);
2131 /* scroll second step to align at full tile size */
2133 Delay(GAME_FRAME_DELAY);
2138 void Explode(int ex, int ey, int phase, int mode)
2142 int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
2143 int last_phase = num_phase * delay;
2144 int half_phase = (num_phase / 2) * delay;
2145 int first_phase_after_start = EX_PHASE_START + 1;
2147 if (game.explosions_delayed)
2149 ExplodeField[ex][ey] = mode;
2153 if (phase == EX_PHASE_START) /* initialize 'Store[][]' field */
2155 int center_element = Feld[ex][ey];
2158 /* --- This is only really needed (and now handled) in "Impact()". --- */
2159 /* do not explode moving elements that left the explode field in time */
2160 if (game.engine_version >= VERSION_IDENT(2,2,0,7) &&
2161 center_element == EL_EMPTY && (mode == EX_NORMAL || mode == EX_CENTER))
2165 if (mode == EX_NORMAL || mode == EX_CENTER)
2166 PlaySoundLevelAction(ex, ey, ACTION_EXPLODING);
2168 /* remove things displayed in background while burning dynamite */
2169 if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
2172 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
2174 /* put moving element to center field (and let it explode there) */
2175 center_element = MovingOrBlocked2Element(ex, ey);
2176 RemoveMovingField(ex, ey);
2177 Feld[ex][ey] = center_element;
2180 for (y = ey - 1; y <= ey + 1; y++) for(x = ex - 1; x <= ex + 1; x++)
2182 int xx = x - ex + 1;
2183 int yy = y - ey + 1;
2186 if (!IN_LEV_FIELD(x, y) ||
2187 ((mode != EX_NORMAL || center_element == EL_AMOEBA_TO_DIAMOND) &&
2188 (x != ex || y != ey)))
2191 element = Feld[x][y];
2193 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
2195 element = MovingOrBlocked2Element(x, y);
2197 if (!IS_EXPLOSION_PROOF(element))
2198 RemoveMovingField(x, y);
2204 if (IS_EXPLOSION_PROOF(element))
2207 /* indestructible elements can only explode in center (but not flames) */
2208 if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey)) ||
2209 element == EL_FLAMES)
2214 if ((IS_INDESTRUCTIBLE(element) &&
2215 (game.engine_version < VERSION_IDENT(2,2,0,0) ||
2216 (!IS_WALKABLE_OVER(element) && !IS_WALKABLE_UNDER(element)))) ||
2217 element == EL_FLAMES)
2221 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
2223 if (IS_ACTIVE_BOMB(element))
2225 /* re-activate things under the bomb like gate or penguin */
2226 Feld[x][y] = (Store[x][y] ? Store[x][y] : EL_EMPTY);
2233 /* save walkable background elements while explosion on same tile */
2235 if (IS_INDESTRUCTIBLE(element))
2236 Back[x][y] = element;
2238 if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element))
2239 Back[x][y] = element;
2242 /* ignite explodable elements reached by other explosion */
2243 if (element == EL_EXPLOSION)
2244 element = Store2[x][y];
2247 if (AmoebaNr[x][y] &&
2248 (element == EL_AMOEBA_FULL ||
2249 element == EL_BD_AMOEBA ||
2250 element == EL_AMOEBA_GROWING))
2252 AmoebaCnt[AmoebaNr[x][y]]--;
2253 AmoebaCnt2[AmoebaNr[x][y]]--;
2259 if (IS_PLAYER(ex, ey) && !PLAYER_PROTECTED(ex, ey))
2261 switch(StorePlayer[ex][ey])
2264 Store[x][y] = EL_EMERALD_RED;
2267 Store[x][y] = EL_EMERALD;
2270 Store[x][y] = EL_EMERALD_PURPLE;
2274 Store[x][y] = EL_EMERALD_YELLOW;
2278 if (game.emulation == EMU_SUPAPLEX)
2279 Store[x][y] = EL_EMPTY;
2281 else if (center_element == EL_MOLE)
2282 Store[x][y] = EL_EMERALD_RED;
2283 else if (center_element == EL_PENGUIN)
2284 Store[x][y] = EL_EMERALD_PURPLE;
2285 else if (center_element == EL_BUG)
2286 Store[x][y] = ((x == ex && y == ey) ? EL_DIAMOND : EL_EMERALD);
2287 else if (center_element == EL_BD_BUTTERFLY)
2288 Store[x][y] = EL_BD_DIAMOND;
2289 else if (center_element == EL_SP_ELECTRON)
2290 Store[x][y] = EL_SP_INFOTRON;
2291 else if (center_element == EL_AMOEBA_TO_DIAMOND)
2292 Store[x][y] = level.amoeba_content;
2293 else if (center_element == EL_YAMYAM)
2294 Store[x][y] = level.yamyam_content[game.yamyam_content_nr][xx][yy];
2295 else if (IS_CUSTOM_ELEMENT(center_element) &&
2296 element_info[center_element].content[xx][yy] != EL_EMPTY)
2297 Store[x][y] = element_info[center_element].content[xx][yy];
2298 else if (element == EL_WALL_EMERALD)
2299 Store[x][y] = EL_EMERALD;
2300 else if (element == EL_WALL_DIAMOND)
2301 Store[x][y] = EL_DIAMOND;
2302 else if (element == EL_WALL_BD_DIAMOND)
2303 Store[x][y] = EL_BD_DIAMOND;
2304 else if (element == EL_WALL_EMERALD_YELLOW)
2305 Store[x][y] = EL_EMERALD_YELLOW;
2306 else if (element == EL_WALL_EMERALD_RED)
2307 Store[x][y] = EL_EMERALD_RED;
2308 else if (element == EL_WALL_EMERALD_PURPLE)
2309 Store[x][y] = EL_EMERALD_PURPLE;
2310 else if (element == EL_WALL_PEARL)
2311 Store[x][y] = EL_PEARL;
2312 else if (element == EL_WALL_CRYSTAL)
2313 Store[x][y] = EL_CRYSTAL;
2314 else if (IS_CUSTOM_ELEMENT(element) && !CAN_EXPLODE(element))
2315 Store[x][y] = element_info[element].content[1][1];
2317 Store[x][y] = EL_EMPTY;
2319 if (x != ex || y != ey ||
2320 center_element == EL_AMOEBA_TO_DIAMOND || mode == EX_BORDER)
2321 Store2[x][y] = element;
2324 if (AmoebaNr[x][y] &&
2325 (element == EL_AMOEBA_FULL ||
2326 element == EL_BD_AMOEBA ||
2327 element == EL_AMOEBA_GROWING))
2329 AmoebaCnt[AmoebaNr[x][y]]--;
2330 AmoebaCnt2[AmoebaNr[x][y]]--;
2336 MovDir[x][y] = MovPos[x][y] = 0;
2341 Feld[x][y] = EL_EXPLOSION;
2343 GfxElement[x][y] = center_element;
2345 GfxElement[x][y] = EL_UNDEFINED;
2348 ExplodePhase[x][y] = 1;
2352 if (center_element == EL_YAMYAM)
2353 game.yamyam_content_nr =
2354 (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
2365 ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
2369 /* activate this even in non-DEBUG version until cause for crash in
2370 getGraphicAnimationFrame() (see below) is found and eliminated */
2374 if (GfxElement[x][y] == EL_UNDEFINED)
2377 printf("Explode(): x = %d, y = %d: GfxElement == EL_UNDEFINED\n", x, y);
2378 printf("Explode(): This should never happen!\n");
2381 GfxElement[x][y] = EL_EMPTY;
2385 if (phase == first_phase_after_start)
2387 int element = Store2[x][y];
2389 if (element == EL_BLACK_ORB)
2391 Feld[x][y] = Store2[x][y];
2396 else if (phase == half_phase)
2398 int element = Store2[x][y];
2400 if (IS_PLAYER(x, y))
2401 KillHeroUnlessProtected(x, y);
2402 else if (CAN_EXPLODE_BY_FIRE(element))
2404 Feld[x][y] = Store2[x][y];
2408 else if (element == EL_AMOEBA_TO_DIAMOND)
2409 AmoebeUmwandeln(x, y);
2412 if (phase == last_phase)
2416 element = Feld[x][y] = Store[x][y];
2417 Store[x][y] = Store2[x][y] = 0;
2418 GfxElement[x][y] = EL_UNDEFINED;
2420 if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
2421 element = Feld[x][y] = Back[x][y];
2424 MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
2425 ChangeDelay[x][y] = 0;
2426 ChangePage[x][y] = -1;
2428 InitField(x, y, FALSE);
2429 if (CAN_MOVE(element))
2431 DrawLevelField(x, y);
2433 TestIfElementTouchesCustomElement(x, y);
2435 if (GFX_CRUMBLED(element))
2436 DrawLevelFieldCrumbledSandNeighbours(x, y);
2438 if (IS_PLAYER(x, y) && !PLAYERINFO(x,y)->present)
2439 StorePlayer[x][y] = 0;
2441 if (ELEM_IS_PLAYER(element))
2442 RelocatePlayer(x, y, element);
2444 else if (phase >= delay && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
2447 int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
2449 int stored = Store[x][y];
2450 int graphic = (game.emulation != EMU_SUPAPLEX ? IMG_EXPLOSION :
2451 stored == EL_SP_INFOTRON ? IMG_SP_EXPLOSION_INFOTRON :
2454 int frame = getGraphicAnimationFrame(graphic, phase - delay);
2457 DrawLevelFieldCrumbledSand(x, y);
2459 if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
2461 DrawLevelElement(x, y, Back[x][y]);
2462 DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
2464 else if (IS_WALKABLE_UNDER(Back[x][y]))
2466 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
2467 DrawLevelElementThruMask(x, y, Back[x][y]);
2469 else if (!IS_WALKABLE_INSIDE(Back[x][y]))
2470 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
2474 void DynaExplode(int ex, int ey)
2477 int dynabomb_size = 1;
2478 boolean dynabomb_xl = FALSE;
2479 struct PlayerInfo *player;
2480 static int xy[4][2] =
2488 if (IS_ACTIVE_BOMB(Feld[ex][ey]))
2490 player = &stored_player[Feld[ex][ey] - EL_DYNABOMB_PLAYER_1_ACTIVE];
2491 dynabomb_size = player->dynabomb_size;
2492 dynabomb_xl = player->dynabomb_xl;
2493 player->dynabombs_left++;
2496 Explode(ex, ey, EX_PHASE_START, EX_CENTER);
2500 for (j=1; j<=dynabomb_size; j++)
2502 int x = ex + j * xy[i % 4][0];
2503 int y = ey + j * xy[i % 4][1];
2506 if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
2509 element = Feld[x][y];
2511 /* do not restart explosions of fields with active bombs */
2512 if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
2515 Explode(x, y, EX_PHASE_START, EX_BORDER);
2517 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
2518 if (element != EL_EMPTY &&
2519 element != EL_SAND &&
2520 element != EL_EXPLOSION &&
2527 void Bang(int x, int y)
2530 int element = MovingOrBlocked2Element(x, y);
2532 int element = Feld[x][y];
2536 if (IS_PLAYER(x, y) && !PLAYER_PROTECTED(x, y))
2538 if (IS_PLAYER(x, y))
2541 struct PlayerInfo *player = PLAYERINFO(x, y);
2543 element = Feld[x][y] = (player->use_murphy_graphic ? EL_SP_MURPHY :
2544 player->element_nr);
2549 PlaySoundLevelAction(x, y, ACTION_EXPLODING);
2551 if (game.emulation == EMU_SUPAPLEX)
2552 PlaySoundLevel(x, y, SND_SP_ELEMENT_EXPLODING);
2554 PlaySoundLevel(x, y, SND_ELEMENT_EXPLODING);
2559 if (IS_PLAYER(x, y)) /* remove objects that might cause smaller explosion */
2567 case EL_BD_BUTTERFLY:
2570 case EL_DARK_YAMYAM:
2574 RaiseScoreElement(element);
2575 Explode(x, y, EX_PHASE_START, EX_NORMAL);
2577 case EL_DYNABOMB_PLAYER_1_ACTIVE:
2578 case EL_DYNABOMB_PLAYER_2_ACTIVE:
2579 case EL_DYNABOMB_PLAYER_3_ACTIVE:
2580 case EL_DYNABOMB_PLAYER_4_ACTIVE:
2581 case EL_DYNABOMB_INCREASE_NUMBER:
2582 case EL_DYNABOMB_INCREASE_SIZE:
2583 case EL_DYNABOMB_INCREASE_POWER:
2588 case EL_LAMP_ACTIVE:
2589 if (IS_PLAYER(x, y))
2590 Explode(x, y, EX_PHASE_START, EX_NORMAL);
2592 Explode(x, y, EX_PHASE_START, EX_CENTER);
2595 if (CAN_EXPLODE_1X1(element))
2596 Explode(x, y, EX_PHASE_START, EX_CENTER);
2598 Explode(x, y, EX_PHASE_START, EX_NORMAL);
2602 CheckTriggeredElementChange(x, y, element, CE_OTHER_IS_EXPLODING);
2605 void SplashAcid(int x, int y)
2607 int element = Feld[x][y];
2609 if (element != EL_ACID_SPLASH_LEFT &&
2610 element != EL_ACID_SPLASH_RIGHT)
2612 PlaySoundLevel(x, y, SND_ACID_SPLASHING);
2614 if (IN_LEV_FIELD(x-1, y) && IS_FREE(x-1, y) &&
2615 (!IN_LEV_FIELD(x-1, y-1) ||
2616 !CAN_FALL(MovingOrBlocked2Element(x-1, y-1))))
2617 Feld[x-1][y] = EL_ACID_SPLASH_LEFT;
2619 if (IN_LEV_FIELD(x+1, y) && IS_FREE(x+1, y) &&
2620 (!IN_LEV_FIELD(x+1, y-1) ||
2621 !CAN_FALL(MovingOrBlocked2Element(x+1, y-1))))
2622 Feld[x+1][y] = EL_ACID_SPLASH_RIGHT;
2626 static void InitBeltMovement()
2628 static int belt_base_element[4] =
2630 EL_CONVEYOR_BELT_1_LEFT,
2631 EL_CONVEYOR_BELT_2_LEFT,
2632 EL_CONVEYOR_BELT_3_LEFT,
2633 EL_CONVEYOR_BELT_4_LEFT
2635 static int belt_base_active_element[4] =
2637 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
2638 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
2639 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
2640 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
2645 /* set frame order for belt animation graphic according to belt direction */
2652 int element = belt_base_active_element[belt_nr] + j;
2653 int graphic = el2img(element);
2655 if (game.belt_dir[i] == MV_LEFT)
2656 graphic_info[graphic].anim_mode &= ~ANIM_REVERSE;
2658 graphic_info[graphic].anim_mode |= ANIM_REVERSE;
2662 for(y=0; y<lev_fieldy; y++)
2664 for(x=0; x<lev_fieldx; x++)
2666 int element = Feld[x][y];
2670 if (IS_BELT(element) && game.belt_dir[i] != MV_NO_MOVING)
2672 int e_belt_nr = getBeltNrFromBeltElement(element);
2675 if (e_belt_nr == belt_nr)
2677 int belt_part = Feld[x][y] - belt_base_element[belt_nr];
2679 Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
2687 static void ToggleBeltSwitch(int x, int y)
2689 static int belt_base_element[4] =
2691 EL_CONVEYOR_BELT_1_LEFT,
2692 EL_CONVEYOR_BELT_2_LEFT,
2693 EL_CONVEYOR_BELT_3_LEFT,
2694 EL_CONVEYOR_BELT_4_LEFT
2696 static int belt_base_active_element[4] =
2698 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
2699 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
2700 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
2701 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
2703 static int belt_base_switch_element[4] =
2705 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
2706 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
2707 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
2708 EL_CONVEYOR_BELT_4_SWITCH_LEFT
2710 static int belt_move_dir[4] =
2718 int element = Feld[x][y];
2719 int belt_nr = getBeltNrFromBeltSwitchElement(element);
2720 int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
2721 int belt_dir = belt_move_dir[belt_dir_nr];
2724 if (!IS_BELT_SWITCH(element))
2727 game.belt_dir_nr[belt_nr] = belt_dir_nr;
2728 game.belt_dir[belt_nr] = belt_dir;
2730 if (belt_dir_nr == 3)
2733 /* set frame order for belt animation graphic according to belt direction */
2736 int element = belt_base_active_element[belt_nr] + i;
2737 int graphic = el2img(element);
2739 if (belt_dir == MV_LEFT)
2740 graphic_info[graphic].anim_mode &= ~ANIM_REVERSE;
2742 graphic_info[graphic].anim_mode |= ANIM_REVERSE;
2745 for (yy=0; yy<lev_fieldy; yy++)
2747 for (xx=0; xx<lev_fieldx; xx++)
2749 int element = Feld[xx][yy];
2751 if (IS_BELT_SWITCH(element))
2753 int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
2755 if (e_belt_nr == belt_nr)
2757 Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
2758 DrawLevelField(xx, yy);
2761 else if (IS_BELT(element) && belt_dir != MV_NO_MOVING)
2763 int e_belt_nr = getBeltNrFromBeltElement(element);
2765 if (e_belt_nr == belt_nr)
2767 int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
2769 Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
2770 DrawLevelField(xx, yy);
2773 else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NO_MOVING)
2775 int e_belt_nr = getBeltNrFromBeltActiveElement(element);
2777 if (e_belt_nr == belt_nr)
2779 int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
2781 Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
2782 DrawLevelField(xx, yy);
2789 static void ToggleSwitchgateSwitch(int x, int y)
2793 game.switchgate_pos = !game.switchgate_pos;
2795 for (yy=0; yy<lev_fieldy; yy++)
2797 for (xx=0; xx<lev_fieldx; xx++)
2799 int element = Feld[xx][yy];
2801 if (element == EL_SWITCHGATE_SWITCH_UP ||
2802 element == EL_SWITCHGATE_SWITCH_DOWN)
2804 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
2805 DrawLevelField(xx, yy);
2807 else if (element == EL_SWITCHGATE_OPEN ||
2808 element == EL_SWITCHGATE_OPENING)
2810 Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
2812 PlaySoundLevelAction(xx, yy, ACTION_CLOSING);
2814 PlaySoundLevel(xx, yy, SND_SWITCHGATE_CLOSING);
2817 else if (element == EL_SWITCHGATE_CLOSED ||
2818 element == EL_SWITCHGATE_CLOSING)
2820 Feld[xx][yy] = EL_SWITCHGATE_OPENING;
2822 PlaySoundLevelAction(xx, yy, ACTION_OPENING);
2824 PlaySoundLevel(xx, yy, SND_SWITCHGATE_OPENING);
2831 static int getInvisibleActiveFromInvisibleElement(int element)
2833 return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
2834 element == EL_INVISIBLE_WALL ? EL_INVISIBLE_WALL_ACTIVE :
2835 element == EL_INVISIBLE_SAND ? EL_INVISIBLE_SAND_ACTIVE :
2839 static int getInvisibleFromInvisibleActiveElement(int element)
2841 return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
2842 element == EL_INVISIBLE_WALL_ACTIVE ? EL_INVISIBLE_WALL :
2843 element == EL_INVISIBLE_SAND_ACTIVE ? EL_INVISIBLE_SAND :
2847 static void RedrawAllLightSwitchesAndInvisibleElements()
2851 for (y=0; y<lev_fieldy; y++)
2853 for (x=0; x<lev_fieldx; x++)
2855 int element = Feld[x][y];
2857 if (element == EL_LIGHT_SWITCH &&
2858 game.light_time_left > 0)
2860 Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
2861 DrawLevelField(x, y);
2863 else if (element == EL_LIGHT_SWITCH_ACTIVE &&
2864 game.light_time_left == 0)
2866 Feld[x][y] = EL_LIGHT_SWITCH;
2867 DrawLevelField(x, y);
2869 else if (element == EL_INVISIBLE_STEELWALL ||
2870 element == EL_INVISIBLE_WALL ||
2871 element == EL_INVISIBLE_SAND)
2873 if (game.light_time_left > 0)
2874 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
2876 DrawLevelField(x, y);
2878 else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
2879 element == EL_INVISIBLE_WALL_ACTIVE ||
2880 element == EL_INVISIBLE_SAND_ACTIVE)
2882 if (game.light_time_left == 0)
2883 Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
2885 DrawLevelField(x, y);
2891 static void ToggleLightSwitch(int x, int y)
2893 int element = Feld[x][y];
2895 game.light_time_left =
2896 (element == EL_LIGHT_SWITCH ?
2897 level.time_light * FRAMES_PER_SECOND : 0);
2899 RedrawAllLightSwitchesAndInvisibleElements();
2902 static void ActivateTimegateSwitch(int x, int y)
2906 game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
2908 for (yy=0; yy<lev_fieldy; yy++)
2910 for (xx=0; xx<lev_fieldx; xx++)
2912 int element = Feld[xx][yy];
2914 if (element == EL_TIMEGATE_CLOSED ||
2915 element == EL_TIMEGATE_CLOSING)
2917 Feld[xx][yy] = EL_TIMEGATE_OPENING;
2918 PlaySoundLevel(xx, yy, SND_TIMEGATE_OPENING);
2922 else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
2924 Feld[xx][yy] = EL_TIMEGATE_SWITCH;
2925 DrawLevelField(xx, yy);
2932 Feld[x][y] = EL_TIMEGATE_SWITCH_ACTIVE;
2935 inline static int getElementMoveStepsize(int x, int y)
2937 int element = Feld[x][y];
2938 int direction = MovDir[x][y];
2939 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
2940 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
2941 int horiz_move = (dx != 0);
2942 int sign = (horiz_move ? dx : dy);
2943 int step = sign * element_info[element].move_stepsize;
2945 /* special values for move stepsize for spring and things on conveyor belt */
2948 if (CAN_FALL(element) &&
2949 y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
2950 step = sign * MOVE_STEPSIZE_NORMAL / 2;
2951 else if (element == EL_SPRING)
2952 step = sign * MOVE_STEPSIZE_NORMAL * 2;
2958 void Impact(int x, int y)
2960 boolean lastline = (y == lev_fieldy-1);
2961 boolean object_hit = FALSE;
2962 boolean impact = (lastline || object_hit);
2963 int element = Feld[x][y];
2964 int smashed = EL_UNDEFINED;
2966 if (!lastline) /* check if element below was hit */
2968 if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
2971 object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
2972 MovDir[x][y + 1] != MV_DOWN ||
2973 MovPos[x][y + 1] <= TILEY / 2));
2975 /* do not smash moving elements that left the smashed field in time */
2976 if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
2977 ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
2981 smashed = MovingOrBlocked2Element(x, y + 1);
2983 impact = (lastline || object_hit);
2986 if (!lastline && smashed == EL_ACID) /* element falls into acid */
2992 /* only reset graphic animation if graphic really changes after impact */
2994 el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
2996 ResetGfxAnimation(x, y);
2997 DrawLevelField(x, y);
3000 if (impact && CAN_EXPLODE_IMPACT(element))
3005 else if (impact && element == EL_PEARL)
3007 Feld[x][y] = EL_PEARL_BREAKING;
3008 PlaySoundLevel(x, y, SND_PEARL_BREAKING);
3011 else if (impact && CheckElementChange(x, y, element, CE_IMPACT))
3013 PlaySoundLevelElementAction(x, y, element, ACTION_IMPACT);
3018 if (impact && element == EL_AMOEBA_DROP)
3020 if (object_hit && IS_PLAYER(x, y + 1))
3021 KillHeroUnlessProtected(x, y + 1);
3022 else if (object_hit && smashed == EL_PENGUIN)
3026 Feld[x][y] = EL_AMOEBA_GROWING;
3027 Store[x][y] = EL_AMOEBA_WET;
3029 ResetRandomAnimationValue(x, y);
3034 if (object_hit) /* check which object was hit */
3036 if (CAN_PASS_MAGIC_WALL(element) &&
3037 (smashed == EL_MAGIC_WALL ||
3038 smashed == EL_BD_MAGIC_WALL))
3041 int activated_magic_wall =
3042 (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
3043 EL_BD_MAGIC_WALL_ACTIVE);
3045 /* activate magic wall / mill */
3046 for (yy=0; yy<lev_fieldy; yy++)
3047 for (xx=0; xx<lev_fieldx; xx++)
3048 if (Feld[xx][yy] == smashed)
3049 Feld[xx][yy] = activated_magic_wall;
3051 game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
3052 game.magic_wall_active = TRUE;
3054 PlaySoundLevel(x, y, (smashed == EL_MAGIC_WALL ?
3055 SND_MAGIC_WALL_ACTIVATING :
3056 SND_BD_MAGIC_WALL_ACTIVATING));
3059 if (IS_PLAYER(x, y + 1))
3061 if (CAN_SMASH_PLAYER(element))
3063 KillHeroUnlessProtected(x, y + 1);
3067 else if (smashed == EL_PENGUIN)
3069 if (CAN_SMASH_PLAYER(element))
3075 else if (element == EL_BD_DIAMOND)
3077 if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
3083 else if ((element == EL_SP_INFOTRON ||
3084 element == EL_SP_ZONK) &&
3085 (smashed == EL_SP_SNIKSNAK ||
3086 smashed == EL_SP_ELECTRON ||
3087 smashed == EL_SP_DISK_ORANGE))
3093 else if (CAN_SMASH_ENEMIES(element) && IS_CLASSIC_ENEMY(smashed))
3099 else if (CAN_SMASH_EVERYTHING(element))
3101 if (IS_CLASSIC_ENEMY(smashed) ||
3102 CAN_EXPLODE_SMASHED(smashed))
3107 else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
3109 if (smashed == EL_LAMP ||
3110 smashed == EL_LAMP_ACTIVE)
3115 else if (smashed == EL_NUT)
3117 Feld[x][y + 1] = EL_NUT_BREAKING;
3118 PlaySoundLevel(x, y, SND_NUT_BREAKING);
3119 RaiseScoreElement(EL_NUT);
3122 else if (smashed == EL_PEARL)
3124 Feld[x][y + 1] = EL_PEARL_BREAKING;
3125 PlaySoundLevel(x, y, SND_PEARL_BREAKING);
3128 else if (smashed == EL_DIAMOND)
3130 Feld[x][y + 1] = EL_DIAMOND_BREAKING;
3131 PlaySoundLevel(x, y, SND_DIAMOND_BREAKING);
3134 else if (IS_BELT_SWITCH(smashed))
3136 ToggleBeltSwitch(x, y + 1);
3138 else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
3139 smashed == EL_SWITCHGATE_SWITCH_DOWN)
3141 ToggleSwitchgateSwitch(x, y + 1);
3143 else if (smashed == EL_LIGHT_SWITCH ||
3144 smashed == EL_LIGHT_SWITCH_ACTIVE)
3146 ToggleLightSwitch(x, y + 1);
3150 CheckElementChange(x, y + 1, smashed, CE_SMASHED);
3152 CheckTriggeredElementSideChange(x, y + 1, smashed, CH_SIDE_TOP,
3153 CE_OTHER_IS_SWITCHING);
3154 CheckElementSideChange(x, y + 1, smashed, CH_SIDE_TOP,
3160 CheckElementChange(x, y + 1, smashed, CE_SMASHED);
3165 /* play sound of magic wall / mill */
3167 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
3168 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE))
3170 if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
3171 PlaySoundLevel(x, y, SND_MAGIC_WALL_FILLING);
3172 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
3173 PlaySoundLevel(x, y, SND_BD_MAGIC_WALL_FILLING);
3178 /* play sound of object that hits the ground */
3179 if (lastline || object_hit)
3180 PlaySoundLevelElementAction(x, y, element, ACTION_IMPACT);
3183 void TurnRound(int x, int y)
3195 { 0, 0 }, { 0, 0 }, { 0, 0 },
3200 int left, right, back;
3204 { MV_DOWN, MV_UP, MV_RIGHT },
3205 { MV_UP, MV_DOWN, MV_LEFT },
3207 { MV_LEFT, MV_RIGHT, MV_DOWN },
3211 { MV_RIGHT, MV_LEFT, MV_UP }
3214 int element = Feld[x][y];
3215 int old_move_dir = MovDir[x][y];
3216 int left_dir = turn[old_move_dir].left;
3217 int right_dir = turn[old_move_dir].right;
3218 int back_dir = turn[old_move_dir].back;
3220 int left_dx = move_xy[left_dir].x, left_dy = move_xy[left_dir].y;
3221 int right_dx = move_xy[right_dir].x, right_dy = move_xy[right_dir].y;
3222 int move_dx = move_xy[old_move_dir].x, move_dy = move_xy[old_move_dir].y;
3223 int back_dx = move_xy[back_dir].x, back_dy = move_xy[back_dir].y;
3225 int left_x = x + left_dx, left_y = y + left_dy;
3226 int right_x = x + right_dx, right_y = y + right_dy;
3227 int move_x = x + move_dx, move_y = y + move_dy;
3231 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
3233 TestIfBadThingTouchesOtherBadThing(x, y);
3235 if (ENEMY_CAN_ENTER_FIELD(right_x, right_y))
3236 MovDir[x][y] = right_dir;
3237 else if (!ENEMY_CAN_ENTER_FIELD(move_x, move_y))
3238 MovDir[x][y] = left_dir;
3240 if (element == EL_BUG && MovDir[x][y] != old_move_dir)
3242 else if (element == EL_BD_BUTTERFLY) /* && MovDir[x][y] == left_dir) */
3245 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
3246 element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
3248 TestIfBadThingTouchesOtherBadThing(x, y);
3250 if (ENEMY_CAN_ENTER_FIELD(left_x, left_y))
3251 MovDir[x][y] = left_dir;
3252 else if (!ENEMY_CAN_ENTER_FIELD(move_x, move_y))
3253 MovDir[x][y] = right_dir;
3255 if ((element == EL_SPACESHIP ||
3256 element == EL_SP_SNIKSNAK ||
3257 element == EL_SP_ELECTRON)
3258 && MovDir[x][y] != old_move_dir)
3260 else if (element == EL_BD_FIREFLY) /* && MovDir[x][y] == right_dir) */
3263 else if (element == EL_YAMYAM)
3265 boolean can_turn_left = YAMYAM_CAN_ENTER_FIELD(left_x, left_y);
3266 boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(right_x, right_y);
3268 if (can_turn_left && can_turn_right)
3269 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
3270 else if (can_turn_left)
3271 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
3272 else if (can_turn_right)
3273 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
3275 MovDir[x][y] = back_dir;
3277 MovDelay[x][y] = 16 + 16 * RND(3);
3279 else if (element == EL_DARK_YAMYAM)
3281 boolean can_turn_left = DARK_YAMYAM_CAN_ENTER_FIELD(left_x, left_y);
3282 boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(right_x, right_y);
3284 if (can_turn_left && can_turn_right)
3285 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
3286 else if (can_turn_left)
3287 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
3288 else if (can_turn_right)
3289 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
3291 MovDir[x][y] = back_dir;
3293 MovDelay[x][y] = 16 + 16 * RND(3);
3295 else if (element == EL_PACMAN)
3297 boolean can_turn_left = PACMAN_CAN_ENTER_FIELD(left_x, left_y);
3298 boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(right_x, right_y);
3300 if (can_turn_left && can_turn_right)
3301 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
3302 else if (can_turn_left)
3303 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
3304 else if (can_turn_right)
3305 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
3307 MovDir[x][y] = back_dir;
3309 MovDelay[x][y] = 6 + RND(40);
3311 else if (element == EL_PIG)
3313 boolean can_turn_left = PIG_CAN_ENTER_FIELD(left_x, left_y);
3314 boolean can_turn_right = PIG_CAN_ENTER_FIELD(right_x, right_y);
3315 boolean can_move_on = PIG_CAN_ENTER_FIELD(move_x, move_y);
3316 boolean should_turn_left, should_turn_right, should_move_on;
3318 int rnd = RND(rnd_value);
3320 should_turn_left = (can_turn_left &&
3322 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
3323 y + back_dy + left_dy)));
3324 should_turn_right = (can_turn_right &&
3326 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
3327 y + back_dy + right_dy)));
3328 should_move_on = (can_move_on &&
3331 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
3332 y + move_dy + left_dy) ||
3333 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
3334 y + move_dy + right_dy)));
3336 if (should_turn_left || should_turn_right || should_move_on)
3338 if (should_turn_left && should_turn_right && should_move_on)
3339 MovDir[x][y] = (rnd < rnd_value / 3 ? left_dir :
3340 rnd < 2 * rnd_value / 3 ? right_dir :
3342 else if (should_turn_left && should_turn_right)
3343 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
3344 else if (should_turn_left && should_move_on)
3345 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
3346 else if (should_turn_right && should_move_on)
3347 MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
3348 else if (should_turn_left)
3349 MovDir[x][y] = left_dir;
3350 else if (should_turn_right)
3351 MovDir[x][y] = right_dir;
3352 else if (should_move_on)
3353 MovDir[x][y] = old_move_dir;
3355 else if (can_move_on && rnd > rnd_value / 8)
3356 MovDir[x][y] = old_move_dir;
3357 else if (can_turn_left && can_turn_right)
3358 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
3359 else if (can_turn_left && rnd > rnd_value / 8)
3360 MovDir[x][y] = left_dir;
3361 else if (can_turn_right && rnd > rnd_value/8)
3362 MovDir[x][y] = right_dir;
3364 MovDir[x][y] = back_dir;
3366 xx = x + move_xy[MovDir[x][y]].x;
3367 yy = y + move_xy[MovDir[x][y]].y;
3369 if (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy]))
3370 MovDir[x][y] = old_move_dir;
3374 else if (element == EL_DRAGON)
3376 boolean can_turn_left = IN_LEV_FIELD_AND_IS_FREE(left_x, left_y);
3377 boolean can_turn_right = IN_LEV_FIELD_AND_IS_FREE(right_x, right_y);
3378 boolean can_move_on = IN_LEV_FIELD_AND_IS_FREE(move_x, move_y);
3380 int rnd = RND(rnd_value);
3382 if (can_move_on && rnd > rnd_value / 8)
3383 MovDir[x][y] = old_move_dir;
3384 else if (can_turn_left && can_turn_right)
3385 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
3386 else if (can_turn_left && rnd > rnd_value / 8)
3387 MovDir[x][y] = left_dir;
3388 else if (can_turn_right && rnd > rnd_value / 8)
3389 MovDir[x][y] = right_dir;
3391 MovDir[x][y] = back_dir;
3393 xx = x + move_xy[MovDir[x][y]].x;
3394 yy = y + move_xy[MovDir[x][y]].y;
3396 if (!IS_FREE(xx, yy))
3397 MovDir[x][y] = old_move_dir;
3401 else if (element == EL_MOLE)
3403 boolean can_move_on =
3404 (MOLE_CAN_ENTER_FIELD(move_x, move_y,
3405 IS_AMOEBOID(Feld[move_x][move_y]) ||
3406 Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
3409 boolean can_turn_left =
3410 (MOLE_CAN_ENTER_FIELD(left_x, left_y,
3411 IS_AMOEBOID(Feld[left_x][left_y])));
3413 boolean can_turn_right =
3414 (MOLE_CAN_ENTER_FIELD(right_x, right_y,
3415 IS_AMOEBOID(Feld[right_x][right_y])));
3417 if (can_turn_left && can_turn_right)
3418 MovDir[x][y] = (RND(2) ? left_dir : right_dir);
3419 else if (can_turn_left)
3420 MovDir[x][y] = left_dir;
3422 MovDir[x][y] = right_dir;
3425 if (MovDir[x][y] != old_move_dir)
3428 else if (element == EL_BALLOON)
3430 MovDir[x][y] = game.balloon_dir;
3433 else if (element == EL_SPRING)
3435 if (MovDir[x][y] & MV_HORIZONTAL &&
3436 (!IN_LEV_FIELD_AND_IS_FREE(move_x, move_y) ||
3437 IN_LEV_FIELD_AND_IS_FREE(x, y + 1)))
3438 MovDir[x][y] = MV_NO_MOVING;
3442 else if (element == EL_ROBOT ||
3443 element == EL_SATELLITE ||
3444 element == EL_PENGUIN)
3446 int attr_x = -1, attr_y = -1;
3457 for (i=0; i<MAX_PLAYERS; i++)
3459 struct PlayerInfo *player = &stored_player[i];
3460 int jx = player->jx, jy = player->jy;
3462 if (!player->active)
3466 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
3474 if (element == EL_ROBOT && ZX >= 0 && ZY >= 0)
3480 if (element == EL_PENGUIN)
3483 static int xy[4][2] =
3493 int ex = x + xy[i % 4][0];
3494 int ey = y + xy[i % 4][1];
3496 if (IN_LEV_FIELD(ex, ey) && Feld[ex][ey] == EL_EXIT_OPEN)
3505 MovDir[x][y] = MV_NO_MOVING;
3507 MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
3508 else if (attr_x > x)
3509 MovDir[x][y] |= (AllPlayersGone ? MV_LEFT : MV_RIGHT);
3511 MovDir[x][y] |= (AllPlayersGone ? MV_DOWN : MV_UP);
3512 else if (attr_y > y)
3513 MovDir[x][y] |= (AllPlayersGone ? MV_UP : MV_DOWN);
3515 if (element == EL_ROBOT)
3519 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
3520 MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
3521 Moving2Blocked(x, y, &newx, &newy);
3523 if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
3524 MovDelay[x][y] = 8 + 8 * !RND(3);
3526 MovDelay[x][y] = 16;
3528 else if (element == EL_PENGUIN)
3534 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
3536 boolean first_horiz = RND(2);
3537 int new_move_dir = MovDir[x][y];
3540 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
3541 Moving2Blocked(x, y, &newx, &newy);
3543 if (PENGUIN_CAN_ENTER_FIELD(newx, newy))
3547 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
3548 Moving2Blocked(x, y, &newx, &newy);
3550 if (PENGUIN_CAN_ENTER_FIELD(newx, newy))
3553 MovDir[x][y] = old_move_dir;
3557 else /* (element == EL_SATELLITE) */
3563 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
3565 boolean first_horiz = RND(2);
3566 int new_move_dir = MovDir[x][y];
3569 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
3570 Moving2Blocked(x, y, &newx, &newy);
3572 if (ELEMENT_CAN_ENTER_FIELD_OR_ACID_2(newx, newy))
3576 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
3577 Moving2Blocked(x, y, &newx, &newy);
3579 if (ELEMENT_CAN_ENTER_FIELD_OR_ACID_2(newx, newy))
3582 MovDir[x][y] = old_move_dir;
3587 else if (element_info[element].move_pattern == MV_ALL_DIRECTIONS ||
3588 element_info[element].move_pattern == MV_TURNING_LEFT ||
3589 element_info[element].move_pattern == MV_TURNING_RIGHT)
3591 boolean can_turn_left = ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
3592 boolean can_turn_right = ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
3594 if (element_info[element].move_pattern == MV_TURNING_LEFT)
3595 MovDir[x][y] = left_dir;
3596 else if (element_info[element].move_pattern == MV_TURNING_RIGHT)
3597 MovDir[x][y] = right_dir;
3598 else if (can_turn_left && can_turn_right)
3599 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
3600 else if (can_turn_left)
3601 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
3602 else if (can_turn_right)
3603 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
3605 MovDir[x][y] = back_dir;
3607 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
3609 else if (element_info[element].move_pattern == MV_HORIZONTAL ||
3610 element_info[element].move_pattern == MV_VERTICAL)
3612 if (element_info[element].move_pattern & old_move_dir)
3613 MovDir[x][y] = back_dir;
3614 else if (element_info[element].move_pattern == MV_HORIZONTAL)
3615 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
3616 else if (element_info[element].move_pattern == MV_VERTICAL)
3617 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
3619 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
3621 else if (element_info[element].move_pattern & MV_ANY_DIRECTION)
3623 MovDir[x][y] = element_info[element].move_pattern;
3624 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
3626 else if (element_info[element].move_pattern == MV_ALONG_LEFT_SIDE)
3628 if (ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
3629 MovDir[x][y] = left_dir;
3630 else if (!ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
3631 MovDir[x][y] = right_dir;
3633 if (MovDir[x][y] != old_move_dir)
3634 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
3636 else if (element_info[element].move_pattern == MV_ALONG_RIGHT_SIDE)
3638 if (ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
3639 MovDir[x][y] = right_dir;
3640 else if (!ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
3641 MovDir[x][y] = left_dir;
3643 if (MovDir[x][y] != old_move_dir)
3644 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
3646 else if (element_info[element].move_pattern == MV_TOWARDS_PLAYER ||
3647 element_info[element].move_pattern == MV_AWAY_FROM_PLAYER)
3649 int attr_x = -1, attr_y = -1;
3652 (element_info[element].move_pattern == MV_AWAY_FROM_PLAYER);
3663 for (i=0; i<MAX_PLAYERS; i++)
3665 struct PlayerInfo *player = &stored_player[i];
3666 int jx = player->jx, jy = player->jy;
3668 if (!player->active)
3672 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
3680 MovDir[x][y] = MV_NO_MOVING;
3682 MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
3683 else if (attr_x > x)
3684 MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
3686 MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
3687 else if (attr_y > y)
3688 MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
3690 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
3692 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
3694 boolean first_horiz = RND(2);
3695 int new_move_dir = MovDir[x][y];
3698 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
3699 Moving2Blocked(x, y, &newx, &newy);
3701 if (ELEMENT_CAN_ENTER_FIELD_OR_ACID(element, newx, newy))
3705 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
3706 Moving2Blocked(x, y, &newx, &newy);
3708 if (ELEMENT_CAN_ENTER_FIELD_OR_ACID(element, newx, newy))
3711 MovDir[x][y] = old_move_dir;
3714 else if (element_info[element].move_pattern == MV_WHEN_PUSHED)
3716 if (!IN_LEV_FIELD_AND_IS_FREE(move_x, move_y))
3717 MovDir[x][y] = MV_NO_MOVING;
3723 static boolean JustBeingPushed(int x, int y)
3727 for (i=0; i<MAX_PLAYERS; i++)
3729 struct PlayerInfo *player = &stored_player[i];
3731 if (player->active && player->is_pushing && player->MovPos)
3733 int next_jx = player->jx + (player->jx - player->last_jx);
3734 int next_jy = player->jy + (player->jy - player->last_jy);
3736 if (x == next_jx && y == next_jy)
3744 void StartMoving(int x, int y)
3746 boolean use_spring_bug = (game.engine_version < VERSION_IDENT(2,2,0,0));
3747 boolean started_moving = FALSE; /* some elements can fall _and_ move */
3748 int element = Feld[x][y];
3753 /* !!! this should be handled more generic (not only for mole) !!! */
3754 if (element != EL_MOLE && GfxAction[x][y] != ACTION_DIGGING)
3755 GfxAction[x][y] = ACTION_DEFAULT;
3757 if (CAN_FALL(element) && y < lev_fieldy - 1)
3759 if ((x > 0 && IS_PLAYER(x - 1, y)) ||
3760 (x < lev_fieldx-1 && IS_PLAYER(x + 1, y)))
3761 if (JustBeingPushed(x, y))
3764 if (element == EL_QUICKSAND_FULL)
3766 if (IS_FREE(x, y + 1))
3768 InitMovingField(x, y, MV_DOWN);
3769 started_moving = TRUE;
3771 Feld[x][y] = EL_QUICKSAND_EMPTYING;
3772 Store[x][y] = EL_ROCK;
3774 PlaySoundLevelAction(x, y, ACTION_EMPTYING);
3776 PlaySoundLevel(x, y, SND_QUICKSAND_EMPTYING);
3779 else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
3781 if (!MovDelay[x][y])
3782 MovDelay[x][y] = TILEY + 1;
3791 Feld[x][y] = EL_QUICKSAND_EMPTY;
3792 Feld[x][y + 1] = EL_QUICKSAND_FULL;
3793 Store[x][y + 1] = Store[x][y];
3796 PlaySoundLevelAction(x, y, ACTION_FILLING);
3798 PlaySoundLevel(x, y, SND_QUICKSAND_FILLING);
3802 else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
3803 Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
3805 InitMovingField(x, y, MV_DOWN);
3806 started_moving = TRUE;
3808 Feld[x][y] = EL_QUICKSAND_FILLING;
3809 Store[x][y] = element;
3811 PlaySoundLevelAction(x, y, ACTION_FILLING);
3813 PlaySoundLevel(x, y, SND_QUICKSAND_FILLING);
3816 else if (element == EL_MAGIC_WALL_FULL)
3818 if (IS_FREE(x, y + 1))
3820 InitMovingField(x, y, MV_DOWN);
3821 started_moving = TRUE;
3823 Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
3824 Store[x][y] = EL_CHANGED(Store[x][y]);
3826 else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
3828 if (!MovDelay[x][y])
3829 MovDelay[x][y] = TILEY/4 + 1;
3838 Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
3839 Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
3840 Store[x][y + 1] = EL_CHANGED(Store[x][y]);
3844 else if (element == EL_BD_MAGIC_WALL_FULL)
3846 if (IS_FREE(x, y + 1))
3848 InitMovingField(x, y, MV_DOWN);
3849 started_moving = TRUE;
3851 Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
3852 Store[x][y] = EL_CHANGED2(Store[x][y]);
3854 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
3856 if (!MovDelay[x][y])
3857 MovDelay[x][y] = TILEY/4 + 1;
3866 Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
3867 Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
3868 Store[x][y + 1] = EL_CHANGED2(Store[x][y]);
3872 else if (CAN_PASS_MAGIC_WALL(element) &&
3873 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
3874 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE))
3876 InitMovingField(x, y, MV_DOWN);
3877 started_moving = TRUE;
3880 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
3881 EL_BD_MAGIC_WALL_FILLING);
3882 Store[x][y] = element;
3885 else if (CAN_SMASH(element) && Feld[x][y + 1] == EL_ACID)
3887 else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
3892 InitMovingField(x, y, MV_DOWN);
3893 started_moving = TRUE;
3895 Store[x][y] = EL_ACID;
3897 /* !!! TEST !!! better use "_FALLING" etc. !!! */
3898 GfxAction[x][y + 1] = ACTION_ACTIVE;
3902 else if ((game.engine_version < VERSION_IDENT(2,2,0,7) &&
3903 CAN_SMASH(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
3904 (Feld[x][y + 1] == EL_BLOCKED)) ||
3905 (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
3906 CAN_SMASH(element) && WasJustFalling[x][y] &&
3907 (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))))
3911 else if (game.engine_version < VERSION_IDENT(2,2,0,7) &&
3912 CAN_SMASH(element) && Feld[x][y + 1] == EL_BLOCKED &&
3913 WasJustMoving[x][y] && !Pushed[x][y + 1])
3915 else if (CAN_SMASH(element) && Feld[x][y + 1] == EL_BLOCKED &&
3916 WasJustMoving[x][y])
3921 /* this is needed for a special case not covered by calling "Impact()"
3922 from "ContinueMoving()": if an element moves to a tile directly below
3923 another element which was just falling on that tile (which was empty
3924 in the previous frame), the falling element above would just stop
3925 instead of smashing the element below (in previous version, the above
3926 element was just checked for "moving" instead of "falling", resulting
3927 in incorrect smashes caused by horizontal movement of the above
3928 element; also, the case of the player being the element to smash was
3929 simply not covered here... :-/ ) */
3933 else if (IS_FREE(x, y + 1) && element == EL_SPRING && use_spring_bug)
3935 if (MovDir[x][y] == MV_NO_MOVING)
3937 InitMovingField(x, y, MV_DOWN);
3938 started_moving = TRUE;
3941 else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
3943 if (WasJustFalling[x][y]) /* prevent animation from being restarted */
3944 MovDir[x][y] = MV_DOWN;
3946 InitMovingField(x, y, MV_DOWN);
3947 started_moving = TRUE;
3949 else if (element == EL_AMOEBA_DROP)
3951 Feld[x][y] = EL_AMOEBA_GROWING;
3952 Store[x][y] = EL_AMOEBA_WET;
3954 /* Store[x][y + 1] must be zero, because:
3955 (EL_QUICKSAND_FULL -> EL_ROCK): Store[x][y + 1] == EL_QUICKSAND_EMPTY
3958 #if OLD_GAME_BEHAVIOUR
3959 else if (IS_SLIPPERY(Feld[x][y + 1]) && !Store[x][y + 1])
3961 else if (IS_SLIPPERY(Feld[x][y + 1]) && !Store[x][y + 1] &&
3962 !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
3963 element != EL_DX_SUPABOMB)
3966 else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
3967 (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
3968 !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
3969 element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
3972 boolean can_fall_left = (x > 0 && IS_FREE(x - 1, y) &&
3973 (IS_FREE(x - 1, y + 1) ||
3974 Feld[x - 1][y + 1] == EL_ACID));
3975 boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
3976 (IS_FREE(x + 1, y + 1) ||
3977 Feld[x + 1][y + 1] == EL_ACID));
3978 boolean can_fall_any = (can_fall_left || can_fall_right);
3979 boolean can_fall_both = (can_fall_left && can_fall_right);
3981 if (can_fall_any && IS_CUSTOM_ELEMENT(Feld[x][y + 1]))
3983 int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
3985 if (slippery_type == SLIPPERY_ONLY_LEFT)
3986 can_fall_right = FALSE;
3987 else if (slippery_type == SLIPPERY_ONLY_RIGHT)
3988 can_fall_left = FALSE;
3989 else if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
3990 can_fall_right = FALSE;
3991 else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
3992 can_fall_left = FALSE;
3994 can_fall_any = (can_fall_left || can_fall_right);
3995 can_fall_both = (can_fall_left && can_fall_right);
4000 if (can_fall_both &&
4001 (game.emulation != EMU_BOULDERDASH &&
4002 element != EL_BD_ROCK && element != EL_BD_DIAMOND))
4003 can_fall_left = !(can_fall_right = RND(2));
4005 InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
4006 started_moving = TRUE;
4009 else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
4011 boolean left_is_free = (x > 0 && IS_FREE(x - 1, y));
4012 boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
4013 int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
4014 int belt_dir = game.belt_dir[belt_nr];
4016 if ((belt_dir == MV_LEFT && left_is_free) ||
4017 (belt_dir == MV_RIGHT && right_is_free))
4019 InitMovingField(x, y, belt_dir);
4020 started_moving = TRUE;
4022 GfxAction[x][y] = ACTION_DEFAULT;
4027 /* not "else if" because of elements that can fall and move (EL_SPRING) */
4028 if (CAN_MOVE(element) && !started_moving)
4033 if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
4036 if ((element == EL_SATELLITE ||
4037 element == EL_BALLOON ||
4038 element == EL_SPRING)
4039 && JustBeingPushed(x, y))
4045 if (element == EL_SPRING && MovDir[x][y] == MV_DOWN)
4046 Feld[x][y + 1] = EL_EMPTY; /* was set to EL_BLOCKED above */
4048 if (element == EL_SPRING && MovDir[x][y] != MV_NO_MOVING)
4050 Moving2Blocked(x, y, &newx, &newy);
4051 if (Feld[newx][newy] == EL_BLOCKED)
4052 Feld[newx][newy] = EL_EMPTY; /* was set to EL_BLOCKED above */
4057 if (!MovDelay[x][y]) /* start new movement phase */
4059 /* all objects that can change their move direction after each step
4060 (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall */
4062 if (element != EL_YAMYAM &&
4063 element != EL_DARK_YAMYAM &&
4064 element != EL_PACMAN &&
4065 !(element_info[element].move_pattern & MV_ANY_DIRECTION) &&
4066 element_info[element].move_pattern != MV_TURNING_LEFT &&
4067 element_info[element].move_pattern != MV_TURNING_RIGHT)
4071 if (MovDelay[x][y] && (element == EL_BUG ||
4072 element == EL_SPACESHIP ||
4073 element == EL_SP_SNIKSNAK ||
4074 element == EL_SP_ELECTRON ||
4075 element == EL_MOLE))
4076 DrawLevelField(x, y);
4080 if (MovDelay[x][y]) /* wait some time before next movement */
4085 if (element == EL_YAMYAM)
4088 el_act_dir2img(EL_YAMYAM, ACTION_WAITING, MV_LEFT));
4089 DrawLevelElementAnimation(x, y, element);
4093 if (MovDelay[x][y]) /* element still has to wait some time */
4096 /* !!! PLACE THIS SOMEWHERE AFTER "TurnRound()" !!! */
4097 ResetGfxAnimation(x, y);
4099 GfxAction[x][y] = ACTION_WAITING;
4102 if (element == EL_ROBOT ||
4104 element == EL_PACMAN ||
4106 element == EL_YAMYAM ||
4107 element == EL_DARK_YAMYAM)
4110 DrawLevelElementAnimation(x, y, element);
4112 DrawLevelElementAnimationIfNeeded(x, y, element);
4114 PlaySoundLevelAction(x, y, ACTION_WAITING);
4116 else if (element == EL_SP_ELECTRON)
4117 DrawLevelElementAnimationIfNeeded(x, y, element);
4118 else if (element == EL_DRAGON)
4121 int dir = MovDir[x][y];
4122 int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
4123 int dy = (dir == MV_UP ? -1 : dir == MV_DOWN ? +1 : 0);
4124 int graphic = (dir == MV_LEFT ? IMG_FLAMES_1_LEFT :
4125 dir == MV_RIGHT ? IMG_FLAMES_1_RIGHT :
4126 dir == MV_UP ? IMG_FLAMES_1_UP :
4127 dir == MV_DOWN ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
4128 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
4131 printf("::: %d, %d\n", GfxAction[x][y], GfxFrame[x][y]);
4134 GfxAction[x][y] = ACTION_ATTACKING;
4136 if (IS_PLAYER(x, y))
4137 DrawPlayerField(x, y);
4139 DrawLevelField(x, y);
4141 PlaySoundLevelActionIfLoop(x, y, ACTION_ATTACKING);
4143 for (i=1; i <= 3; i++)
4145 int xx = x + i * dx;
4146 int yy = y + i * dy;
4147 int sx = SCREENX(xx);
4148 int sy = SCREENY(yy);
4149 int flame_graphic = graphic + (i - 1);
4151 if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
4156 int flamed = MovingOrBlocked2Element(xx, yy);
4158 if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_FIRE(flamed))
4161 RemoveMovingField(xx, yy);
4163 Feld[xx][yy] = EL_FLAMES;
4164 if (IN_SCR_FIELD(sx, sy))
4166 DrawLevelFieldCrumbledSand(xx, yy);
4167 DrawGraphic(sx, sy, flame_graphic, frame);
4172 if (Feld[xx][yy] == EL_FLAMES)
4173 Feld[xx][yy] = EL_EMPTY;
4174 DrawLevelField(xx, yy);
4179 if (MovDelay[x][y]) /* element still has to wait some time */
4181 PlaySoundLevelAction(x, y, ACTION_WAITING);
4186 /* special case of "moving" animation of waiting elements (FIX THIS !!!);
4187 for all other elements GfxAction will be set by InitMovingField() */
4188 if (element == EL_BD_BUTTERFLY || element == EL_BD_FIREFLY)
4189 GfxAction[x][y] = ACTION_MOVING;
4192 /* now make next step */
4194 Moving2Blocked(x, y, &newx, &newy); /* get next screen position */
4196 if (DONT_COLLIDE_WITH(element) &&
4197 IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
4198 !PLAYER_PROTECTED(newx, newy))
4201 TestIfBadThingRunsIntoHero(x, y, MovDir[x][y]);
4204 /* player killed by element which is deadly when colliding with */
4206 KillHero(PLAYERINFO(newx, newy));
4211 else if ((element == EL_PENGUIN ||
4212 element == EL_ROBOT ||
4213 element == EL_SATELLITE ||
4214 element == EL_BALLOON ||
4215 IS_CUSTOM_ELEMENT(element)) &&
4216 IN_LEV_FIELD(newx, newy) &&
4217 MovDir[x][y] == MV_DOWN && Feld[newx][newy] == EL_ACID)
4220 Store[x][y] = EL_ACID;
4222 else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
4224 if (Feld[newx][newy] == EL_EXIT_OPEN)
4226 Feld[x][y] = EL_EMPTY;
4227 DrawLevelField(x, y);
4229 PlaySoundLevel(newx, newy, SND_PENGUIN_PASSING);
4230 if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
4231 DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
4233 local_player->friends_still_needed--;
4234 if (!local_player->friends_still_needed &&
4235 !local_player->GameOver && AllPlayersGone)
4236 local_player->LevelSolved = local_player->GameOver = TRUE;
4240 else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
4242 if (DigField(local_player, newx, newy, 0, 0, DF_DIG) == MF_MOVING)
4243 DrawLevelField(newx, newy);
4245 MovDir[x][y] = MV_NO_MOVING;
4247 else if (!IS_FREE(newx, newy))
4249 GfxAction[x][y] = ACTION_WAITING;
4251 if (IS_PLAYER(x, y))
4252 DrawPlayerField(x, y);
4254 DrawLevelField(x, y);
4258 else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
4260 if (IS_FOOD_PIG(Feld[newx][newy]))
4262 if (IS_MOVING(newx, newy))
4263 RemoveMovingField(newx, newy);
4266 Feld[newx][newy] = EL_EMPTY;
4267 DrawLevelField(newx, newy);
4270 PlaySoundLevel(x, y, SND_PIG_DIGGING);
4272 else if (!IS_FREE(newx, newy))
4274 if (IS_PLAYER(x, y))
4275 DrawPlayerField(x, y);
4277 DrawLevelField(x, y);
4281 else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
4283 if (!IS_FREE(newx, newy))
4285 if (IS_PLAYER(x, y))
4286 DrawPlayerField(x, y);
4288 DrawLevelField(x, y);
4294 boolean wanna_flame = !RND(10);
4295 int dx = newx - x, dy = newy - y;
4296 int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
4297 int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
4298 int element1 = (IN_LEV_FIELD(newx1, newy1) ?
4299 MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
4300 int element2 = (IN_LEV_FIELD(newx2, newy2) ?
4301 MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
4304 IS_CLASSIC_ENEMY(element1) ||
4305 IS_CLASSIC_ENEMY(element2)) &&
4306 element1 != EL_DRAGON && element2 != EL_DRAGON &&
4307 element1 != EL_FLAMES && element2 != EL_FLAMES)
4310 ResetGfxAnimation(x, y);
4311 GfxAction[x][y] = ACTION_ATTACKING;
4314 if (IS_PLAYER(x, y))
4315 DrawPlayerField(x, y);
4317 DrawLevelField(x, y);
4319 PlaySoundLevel(x, y, SND_DRAGON_ATTACKING);
4321 MovDelay[x][y] = 50;
4323 Feld[newx][newy] = EL_FLAMES;
4324 if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
4325 Feld[newx1][newy1] = EL_FLAMES;
4326 if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
4327 Feld[newx2][newy2] = EL_FLAMES;
4333 else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
4334 Feld[newx][newy] == EL_DIAMOND)
4336 if (IS_MOVING(newx, newy))
4337 RemoveMovingField(newx, newy);
4340 Feld[newx][newy] = EL_EMPTY;
4341 DrawLevelField(newx, newy);
4344 PlaySoundLevel(x, y, SND_YAMYAM_DIGGING);
4346 else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
4347 IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
4349 if (AmoebaNr[newx][newy])
4351 AmoebaCnt2[AmoebaNr[newx][newy]]--;
4352 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
4353 Feld[newx][newy] == EL_BD_AMOEBA)
4354 AmoebaCnt[AmoebaNr[newx][newy]]--;
4357 if (IS_MOVING(newx, newy))
4358 RemoveMovingField(newx, newy);
4361 Feld[newx][newy] = EL_EMPTY;
4362 DrawLevelField(newx, newy);
4365 PlaySoundLevel(x, y, SND_DARK_YAMYAM_DIGGING);
4367 else if ((element == EL_PACMAN || element == EL_MOLE)
4368 && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
4370 if (AmoebaNr[newx][newy])
4372 AmoebaCnt2[AmoebaNr[newx][newy]]--;
4373 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
4374 Feld[newx][newy] == EL_BD_AMOEBA)
4375 AmoebaCnt[AmoebaNr[newx][newy]]--;
4378 if (element == EL_MOLE)
4380 Feld[newx][newy] = EL_AMOEBA_SHRINKING;
4381 PlaySoundLevel(x, y, SND_MOLE_DIGGING);
4383 ResetGfxAnimation(x, y);
4384 GfxAction[x][y] = ACTION_DIGGING;
4385 DrawLevelField(x, y);
4387 MovDelay[newx][newy] = 0; /* start amoeba shrinking delay */
4388 return; /* wait for shrinking amoeba */
4390 else /* element == EL_PACMAN */
4392 Feld[newx][newy] = EL_EMPTY;
4393 DrawLevelField(newx, newy);
4394 PlaySoundLevel(x, y, SND_PACMAN_DIGGING);
4397 else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
4398 (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
4399 (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
4401 /* wait for shrinking amoeba to completely disappear */
4404 else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
4406 /* object was running against a wall */
4411 if (GFX_ELEMENT(element) != EL_SAND) /* !!! FIX THIS (crumble) !!! */
4412 DrawLevelElementAnimation(x, y, element);
4414 if (element == EL_BUG ||
4415 element == EL_SPACESHIP ||
4416 element == EL_SP_SNIKSNAK)
4417 DrawLevelField(x, y);
4418 else if (element == EL_MOLE)
4419 DrawLevelField(x, y);
4420 else if (element == EL_BD_BUTTERFLY ||
4421 element == EL_BD_FIREFLY)
4422 DrawLevelElementAnimationIfNeeded(x, y, element);
4423 else if (element == EL_SATELLITE)
4424 DrawLevelElementAnimationIfNeeded(x, y, element);
4425 else if (element == EL_SP_ELECTRON)
4426 DrawLevelElementAnimationIfNeeded(x, y, element);
4429 if (DONT_TOUCH(element))
4430 TestIfBadThingTouchesHero(x, y);
4433 PlaySoundLevelAction(x, y, ACTION_WAITING);
4439 InitMovingField(x, y, MovDir[x][y]);
4441 PlaySoundLevelAction(x, y, ACTION_MOVING);
4445 ContinueMoving(x, y);
4448 void ContinueMoving(int x, int y)
4450 int element = Feld[x][y];
4451 int direction = MovDir[x][y];
4452 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
4453 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
4454 int newx = x + dx, newy = y + dy;
4455 int nextx = newx + dx, nexty = newy + dy;
4456 boolean pushed = Pushed[x][y];
4458 MovPos[x][y] += getElementMoveStepsize(x, y);
4460 if (pushed) /* special case: moving object pushed by player */
4461 MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
4463 if (ABS(MovPos[x][y]) < TILEX)
4465 DrawLevelField(x, y);
4467 return; /* element is still moving */
4470 /* element reached destination field */
4472 Feld[x][y] = EL_EMPTY;
4473 Feld[newx][newy] = element;
4474 MovPos[x][y] = 0; /* force "not moving" for "crumbled sand" */
4476 if (element == EL_MOLE)
4478 Feld[x][y] = EL_SAND;
4480 DrawLevelFieldCrumbledSandNeighbours(x, y);
4482 else if (element == EL_QUICKSAND_FILLING)
4484 element = Feld[newx][newy] = get_next_element(element);
4485 Store[newx][newy] = Store[x][y];
4487 else if (element == EL_QUICKSAND_EMPTYING)
4489 Feld[x][y] = get_next_element(element);
4490 element = Feld[newx][newy] = Store[x][y];
4492 else if (element == EL_MAGIC_WALL_FILLING)
4494 element = Feld[newx][newy] = get_next_element(element);
4495 if (!game.magic_wall_active)
4496 element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
4497 Store[newx][newy] = Store[x][y];
4499 else if (element == EL_MAGIC_WALL_EMPTYING)
4501 Feld[x][y] = get_next_element(element);
4502 if (!game.magic_wall_active)
4503 Feld[x][y] = EL_MAGIC_WALL_DEAD;
4504 element = Feld[newx][newy] = Store[x][y];
4506 else if (element == EL_BD_MAGIC_WALL_FILLING)
4508 element = Feld[newx][newy] = get_next_element(element);
4509 if (!game.magic_wall_active)
4510 element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
4511 Store[newx][newy] = Store[x][y];
4513 else if (element == EL_BD_MAGIC_WALL_EMPTYING)
4515 Feld[x][y] = get_next_element(element);
4516 if (!game.magic_wall_active)
4517 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
4518 element = Feld[newx][newy] = Store[x][y];
4520 else if (element == EL_AMOEBA_DROPPING)
4522 Feld[x][y] = get_next_element(element);
4523 element = Feld[newx][newy] = Store[x][y];
4525 else if (element == EL_SOKOBAN_OBJECT)
4528 Feld[x][y] = Back[x][y];
4530 if (Back[newx][newy])
4531 Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
4533 Back[x][y] = Back[newx][newy] = 0;
4535 else if (Store[x][y] == EL_ACID)
4537 element = Feld[newx][newy] = EL_ACID;
4541 MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
4542 MovDelay[newx][newy] = 0;
4544 /* copy element change control values to new field */
4545 ChangeDelay[newx][newy] = ChangeDelay[x][y];
4546 ChangePage[newx][newy] = ChangePage[x][y];
4547 Changed[newx][newy] = Changed[x][y];
4548 ChangeEvent[newx][newy] = ChangeEvent[x][y];
4550 ChangeDelay[x][y] = 0;
4551 ChangePage[x][y] = -1;
4552 Changed[x][y] = CE_BITMASK_DEFAULT;
4553 ChangeEvent[x][y] = CE_BITMASK_DEFAULT;
4555 /* copy animation control values to new field */
4556 GfxFrame[newx][newy] = GfxFrame[x][y];
4557 GfxAction[newx][newy] = GfxAction[x][y]; /* keep action one frame */
4558 GfxRandom[newx][newy] = GfxRandom[x][y]; /* keep same random value */
4560 Pushed[x][y] = Pushed[newx][newy] = FALSE;
4562 ResetGfxAnimation(x, y); /* reset animation values for old field */
4565 /* 2.1.1 (does not work correctly for spring) */
4566 if (!CAN_MOVE(element))
4567 MovDir[newx][newy] = 0;
4571 /* (does not work for falling objects that slide horizontally) */
4572 if (CAN_FALL(element) && MovDir[newx][newy] == MV_DOWN)
4573 MovDir[newx][newy] = 0;
4576 if (!CAN_MOVE(element) ||
4577 (element == EL_SPRING && MovDir[newx][newy] == MV_DOWN))
4578 MovDir[newx][newy] = 0;
4581 if (!CAN_MOVE(element) ||
4582 (CAN_FALL(element) && direction == MV_DOWN))
4583 MovDir[newx][newy] = 0;
4588 DrawLevelField(x, y);
4589 DrawLevelField(newx, newy);
4591 Stop[newx][newy] = TRUE; /* ignore this element until the next frame */
4593 /* prevent pushed element from moving on in pushed direction */
4594 if (pushed && CAN_MOVE(element) &&
4595 element_info[element].move_pattern & MV_ANY_DIRECTION &&
4596 !(element_info[element].move_pattern & direction))
4597 TurnRound(newx, newy);
4599 if (!pushed) /* special case: moving object pushed by player */
4601 WasJustMoving[newx][newy] = 3;
4603 if (CAN_FALL(element) && direction == MV_DOWN)
4604 WasJustFalling[newx][newy] = 3;
4607 if (DONT_TOUCH(element)) /* object may be nasty to player or others */
4609 TestIfBadThingTouchesHero(newx, newy);
4610 TestIfBadThingTouchesFriend(newx, newy);
4611 TestIfBadThingTouchesOtherBadThing(newx, newy);
4613 else if (element == EL_PENGUIN)
4614 TestIfFriendTouchesBadThing(newx, newy);
4616 if (CAN_FALL(element) && direction == MV_DOWN &&
4617 (newy == lev_fieldy - 1 || !IS_FREE(x, newy + 1)))
4621 TestIfElementTouchesCustomElement(x, y); /* for empty space */
4625 if (ChangePage[newx][newy] != -1) /* delayed change */
4626 ChangeElement(newx, newy, ChangePage[newx][newy]);
4629 if (!IN_LEV_FIELD(nextx, nexty) || !IS_FREE(nextx, nexty))
4630 CheckElementSideChange(newx, newy, Feld[newx][newy], direction,
4633 TestIfPlayerTouchesCustomElement(newx, newy);
4634 TestIfElementTouchesCustomElement(newx, newy);
4637 int AmoebeNachbarNr(int ax, int ay)
4640 int element = Feld[ax][ay];
4642 static int xy[4][2] =
4652 int x = ax + xy[i][0];
4653 int y = ay + xy[i][1];
4655 if (!IN_LEV_FIELD(x, y))
4658 if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
4659 group_nr = AmoebaNr[x][y];
4665 void AmoebenVereinigen(int ax, int ay)
4667 int i, x, y, xx, yy;
4668 int new_group_nr = AmoebaNr[ax][ay];
4669 static int xy[4][2] =
4677 if (new_group_nr == 0)
4685 if (!IN_LEV_FIELD(x, y))
4688 if ((Feld[x][y] == EL_AMOEBA_FULL ||
4689 Feld[x][y] == EL_BD_AMOEBA ||
4690 Feld[x][y] == EL_AMOEBA_DEAD) &&
4691 AmoebaNr[x][y] != new_group_nr)
4693 int old_group_nr = AmoebaNr[x][y];
4695 if (old_group_nr == 0)
4698 AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
4699 AmoebaCnt[old_group_nr] = 0;
4700 AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
4701 AmoebaCnt2[old_group_nr] = 0;
4703 for (yy=0; yy<lev_fieldy; yy++)
4705 for (xx=0; xx<lev_fieldx; xx++)
4707 if (AmoebaNr[xx][yy] == old_group_nr)
4708 AmoebaNr[xx][yy] = new_group_nr;
4715 void AmoebeUmwandeln(int ax, int ay)
4719 if (Feld[ax][ay] == EL_AMOEBA_DEAD)
4721 int group_nr = AmoebaNr[ax][ay];
4726 printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
4727 printf("AmoebeUmwandeln(): This should never happen!\n");
4732 for (y=0; y<lev_fieldy; y++)
4734 for (x=0; x<lev_fieldx; x++)
4736 if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
4739 Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
4743 PlaySoundLevel(ax, ay, (IS_GEM(level.amoeba_content) ?
4744 SND_AMOEBA_TURNING_TO_GEM :
4745 SND_AMOEBA_TURNING_TO_ROCK));
4750 static int xy[4][2] =
4763 if (!IN_LEV_FIELD(x, y))
4766 if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
4768 PlaySoundLevel(x, y, (IS_GEM(level.amoeba_content) ?
4769 SND_AMOEBA_TURNING_TO_GEM :
4770 SND_AMOEBA_TURNING_TO_ROCK));
4777 void AmoebeUmwandelnBD(int ax, int ay, int new_element)
4780 int group_nr = AmoebaNr[ax][ay];
4781 boolean done = FALSE;
4786 printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
4787 printf("AmoebeUmwandelnBD(): This should never happen!\n");
4792 for (y=0; y<lev_fieldy; y++)
4794 for (x=0; x<lev_fieldx; x++)
4796 if (AmoebaNr[x][y] == group_nr &&
4797 (Feld[x][y] == EL_AMOEBA_DEAD ||
4798 Feld[x][y] == EL_BD_AMOEBA ||
4799 Feld[x][y] == EL_AMOEBA_GROWING))
4802 Feld[x][y] = new_element;
4803 InitField(x, y, FALSE);
4804 DrawLevelField(x, y);
4811 PlaySoundLevel(ax, ay, (new_element == EL_BD_ROCK ?
4812 SND_BD_AMOEBA_TURNING_TO_ROCK :
4813 SND_BD_AMOEBA_TURNING_TO_GEM));
4816 void AmoebeWaechst(int x, int y)
4818 static unsigned long sound_delay = 0;
4819 static unsigned long sound_delay_value = 0;
4821 if (!MovDelay[x][y]) /* start new growing cycle */
4825 if (DelayReached(&sound_delay, sound_delay_value))
4828 PlaySoundLevelElementAction(x, y, Store[x][y], ACTION_GROWING);
4830 if (Store[x][y] == EL_BD_AMOEBA)
4831 PlaySoundLevel(x, y, SND_BD_AMOEBA_GROWING);
4833 PlaySoundLevel(x, y, SND_AMOEBA_GROWING);
4835 sound_delay_value = 30;
4839 if (MovDelay[x][y]) /* wait some time before growing bigger */
4842 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
4844 int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
4845 6 - MovDelay[x][y]);
4847 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
4850 if (!MovDelay[x][y])
4852 Feld[x][y] = Store[x][y];
4854 DrawLevelField(x, y);
4859 void AmoebaDisappearing(int x, int y)
4861 static unsigned long sound_delay = 0;
4862 static unsigned long sound_delay_value = 0;
4864 if (!MovDelay[x][y]) /* start new shrinking cycle */
4868 if (DelayReached(&sound_delay, sound_delay_value))
4869 sound_delay_value = 30;
4872 if (MovDelay[x][y]) /* wait some time before shrinking */
4875 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
4877 int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
4878 6 - MovDelay[x][y]);
4880 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
4883 if (!MovDelay[x][y])
4885 Feld[x][y] = EL_EMPTY;
4886 DrawLevelField(x, y);
4888 /* don't let mole enter this field in this cycle;
4889 (give priority to objects falling to this field from above) */
4895 void AmoebeAbleger(int ax, int ay)
4898 int element = Feld[ax][ay];
4899 int graphic = el2img(element);
4900 int newax = ax, neway = ay;
4901 static int xy[4][2] =
4909 if (!level.amoeba_speed)
4911 Feld[ax][ay] = EL_AMOEBA_DEAD;
4912 DrawLevelField(ax, ay);
4916 if (IS_ANIMATED(graphic))
4917 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
4919 if (!MovDelay[ax][ay]) /* start making new amoeba field */
4920 MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
4922 if (MovDelay[ax][ay]) /* wait some time before making new amoeba */
4925 if (MovDelay[ax][ay])
4929 if (element == EL_AMOEBA_WET) /* object is an acid / amoeba drop */
4932 int x = ax + xy[start][0];
4933 int y = ay + xy[start][1];
4935 if (!IN_LEV_FIELD(x, y))
4938 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
4939 if (IS_FREE(x, y) ||
4940 Feld[x][y] == EL_SAND || Feld[x][y] == EL_QUICKSAND_EMPTY)
4946 if (newax == ax && neway == ay)
4949 else /* normal or "filled" (BD style) amoeba */
4952 boolean waiting_for_player = FALSE;
4956 int j = (start + i) % 4;
4957 int x = ax + xy[j][0];
4958 int y = ay + xy[j][1];
4960 if (!IN_LEV_FIELD(x, y))
4963 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
4964 if (IS_FREE(x, y) ||
4965 Feld[x][y] == EL_SAND || Feld[x][y] == EL_QUICKSAND_EMPTY)
4971 else if (IS_PLAYER(x, y))
4972 waiting_for_player = TRUE;
4975 if (newax == ax && neway == ay) /* amoeba cannot grow */
4977 if (i == 4 && (!waiting_for_player || game.emulation == EMU_BOULDERDASH))
4979 Feld[ax][ay] = EL_AMOEBA_DEAD;
4980 DrawLevelField(ax, ay);
4981 AmoebaCnt[AmoebaNr[ax][ay]]--;
4983 if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0) /* amoeba is completely dead */
4985 if (element == EL_AMOEBA_FULL)
4986 AmoebeUmwandeln(ax, ay);
4987 else if (element == EL_BD_AMOEBA)
4988 AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
4993 else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
4995 /* amoeba gets larger by growing in some direction */
4997 int new_group_nr = AmoebaNr[ax][ay];
5000 if (new_group_nr == 0)
5002 printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
5003 printf("AmoebeAbleger(): This should never happen!\n");
5008 AmoebaNr[newax][neway] = new_group_nr;
5009 AmoebaCnt[new_group_nr]++;
5010 AmoebaCnt2[new_group_nr]++;
5012 /* if amoeba touches other amoeba(s) after growing, unify them */
5013 AmoebenVereinigen(newax, neway);
5015 if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
5017 AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
5023 if (element != EL_AMOEBA_WET || neway < ay || !IS_FREE(newax, neway) ||
5024 (neway == lev_fieldy - 1 && newax != ax))
5026 Feld[newax][neway] = EL_AMOEBA_GROWING; /* creation of new amoeba */
5027 Store[newax][neway] = element;
5029 else if (neway == ay)
5031 Feld[newax][neway] = EL_AMOEBA_DROP; /* drop left/right of amoeba */
5033 PlaySoundLevelAction(newax, neway, ACTION_GROWING);
5035 PlaySoundLevel(newax, neway, SND_AMOEBA_GROWING);
5040 InitMovingField(ax, ay, MV_DOWN); /* drop dripping from amoeba */
5041 Feld[ax][ay] = EL_AMOEBA_DROPPING;
5042 Store[ax][ay] = EL_AMOEBA_DROP;
5043 ContinueMoving(ax, ay);
5047 DrawLevelField(newax, neway);
5050 void Life(int ax, int ay)
5053 static int life[4] = { 2, 3, 3, 3 }; /* parameters for "game of life" */
5055 int element = Feld[ax][ay];
5056 int graphic = el2img(element);
5057 boolean changed = FALSE;
5059 if (IS_ANIMATED(graphic))
5060 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
5065 if (!MovDelay[ax][ay]) /* start new "game of life" cycle */
5066 MovDelay[ax][ay] = life_time;
5068 if (MovDelay[ax][ay]) /* wait some time before next cycle */
5071 if (MovDelay[ax][ay])
5075 for (y1=-1; y1<2; y1++) for(x1=-1; x1<2; x1++)
5077 int xx = ax+x1, yy = ay+y1;
5080 if (!IN_LEV_FIELD(xx, yy))
5083 for (y2=-1; y2<2; y2++) for (x2=-1; x2<2; x2++)
5085 int x = xx+x2, y = yy+y2;
5087 if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
5090 if (((Feld[x][y] == element ||
5091 (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y))) &&
5093 (IS_FREE(x, y) && Stop[x][y]))
5097 if (xx == ax && yy == ay) /* field in the middle */
5099 if (nachbarn < life[0] || nachbarn > life[1])
5101 Feld[xx][yy] = EL_EMPTY;
5103 DrawLevelField(xx, yy);
5104 Stop[xx][yy] = TRUE;
5108 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
5109 else if (IS_FREE(xx, yy) || Feld[xx][yy] == EL_SAND)
5110 { /* free border field */
5111 if (nachbarn >= life[2] && nachbarn <= life[3])
5113 Feld[xx][yy] = element;
5114 MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
5116 DrawLevelField(xx, yy);
5117 Stop[xx][yy] = TRUE;
5124 PlaySoundLevel(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
5125 SND_GAME_OF_LIFE_GROWING);
5128 static void InitRobotWheel(int x, int y)
5130 ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
5133 static void RunRobotWheel(int x, int y)
5135 PlaySoundLevel(x, y, SND_ROBOT_WHEEL_ACTIVE);
5138 static void StopRobotWheel(int x, int y)
5140 if (ZX == x && ZY == y)
5144 static void InitTimegateWheel(int x, int y)
5146 ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
5149 static void RunTimegateWheel(int x, int y)
5151 PlaySoundLevel(x, y, SND_TIMEGATE_SWITCH_ACTIVE);
5154 void CheckExit(int x, int y)
5156 if (local_player->gems_still_needed > 0 ||
5157 local_player->sokobanfields_still_needed > 0 ||
5158 local_player->lights_still_needed > 0)
5160 int element = Feld[x][y];
5161 int graphic = el2img(element);
5163 if (IS_ANIMATED(graphic))
5164 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
5169 if (AllPlayersGone) /* do not re-open exit door closed after last player */
5172 Feld[x][y] = EL_EXIT_OPENING;
5174 PlaySoundLevelNearest(x, y, SND_CLASS_EXIT_OPENING);
5177 void CheckExitSP(int x, int y)
5179 if (local_player->gems_still_needed > 0)
5181 int element = Feld[x][y];
5182 int graphic = el2img(element);
5184 if (IS_ANIMATED(graphic))
5185 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
5190 if (AllPlayersGone) /* do not re-open exit door closed after last player */
5193 Feld[x][y] = EL_SP_EXIT_OPENING;
5195 PlaySoundLevelNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
5198 static void CloseAllOpenTimegates()
5202 for (y=0; y<lev_fieldy; y++)
5204 for (x=0; x<lev_fieldx; x++)
5206 int element = Feld[x][y];
5208 if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
5210 Feld[x][y] = EL_TIMEGATE_CLOSING;
5212 PlaySoundLevelAction(x, y, ACTION_CLOSING);
5214 PlaySoundLevel(x, y, SND_TIMEGATE_CLOSING);
5221 void EdelsteinFunkeln(int x, int y)
5223 if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
5226 if (Feld[x][y] == EL_BD_DIAMOND)
5229 if (MovDelay[x][y] == 0) /* next animation frame */
5230 MovDelay[x][y] = 11 * !SimpleRND(500);
5232 if (MovDelay[x][y] != 0) /* wait some time before next frame */
5236 if (setup.direct_draw && MovDelay[x][y])
5237 SetDrawtoField(DRAW_BUFFERED);
5239 DrawLevelElementAnimation(x, y, Feld[x][y]);
5241 if (MovDelay[x][y] != 0)
5243 int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
5244 10 - MovDelay[x][y]);
5246 DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
5248 if (setup.direct_draw)
5252 dest_x = FX + SCREENX(x) * TILEX;
5253 dest_y = FY + SCREENY(y) * TILEY;
5255 BlitBitmap(drawto_field, window,
5256 dest_x, dest_y, TILEX, TILEY, dest_x, dest_y);
5257 SetDrawtoField(DRAW_DIRECT);
5263 void MauerWaechst(int x, int y)
5267 if (!MovDelay[x][y]) /* next animation frame */
5268 MovDelay[x][y] = 3 * delay;
5270 if (MovDelay[x][y]) /* wait some time before next frame */
5274 if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
5276 int graphic = el_dir2img(Feld[x][y], MovDir[x][y]);
5277 int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
5279 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5282 if (!MovDelay[x][y])
5284 if (MovDir[x][y] == MV_LEFT)
5286 if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
5287 DrawLevelField(x - 1, y);
5289 else if (MovDir[x][y] == MV_RIGHT)
5291 if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
5292 DrawLevelField(x + 1, y);
5294 else if (MovDir[x][y] == MV_UP)
5296 if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
5297 DrawLevelField(x, y - 1);
5301 if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
5302 DrawLevelField(x, y + 1);
5305 Feld[x][y] = Store[x][y];
5307 MovDir[x][y] = MV_NO_MOVING;
5308 DrawLevelField(x, y);
5313 void MauerAbleger(int ax, int ay)
5315 int element = Feld[ax][ay];
5316 int graphic = el2img(element);
5317 boolean oben_frei = FALSE, unten_frei = FALSE;
5318 boolean links_frei = FALSE, rechts_frei = FALSE;
5319 boolean oben_massiv = FALSE, unten_massiv = FALSE;
5320 boolean links_massiv = FALSE, rechts_massiv = FALSE;
5321 boolean new_wall = FALSE;
5323 if (IS_ANIMATED(graphic))
5324 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
5326 if (!MovDelay[ax][ay]) /* start building new wall */
5327 MovDelay[ax][ay] = 6;
5329 if (MovDelay[ax][ay]) /* wait some time before building new wall */
5332 if (MovDelay[ax][ay])
5336 if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
5338 if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
5340 if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
5342 if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
5345 if (element == EL_EXPANDABLE_WALL_VERTICAL ||
5346 element == EL_EXPANDABLE_WALL_ANY)
5350 Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
5351 Store[ax][ay-1] = element;
5352 MovDir[ax][ay-1] = MV_UP;
5353 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
5354 DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
5355 IMG_EXPANDABLE_WALL_GROWING_UP, 0);
5360 Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
5361 Store[ax][ay+1] = element;
5362 MovDir[ax][ay+1] = MV_DOWN;
5363 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
5364 DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
5365 IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
5370 if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
5371 element == EL_EXPANDABLE_WALL_ANY ||
5372 element == EL_EXPANDABLE_WALL)
5376 Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
5377 Store[ax-1][ay] = element;
5378 MovDir[ax-1][ay] = MV_LEFT;
5379 if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
5380 DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
5381 IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
5387 Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
5388 Store[ax+1][ay] = element;
5389 MovDir[ax+1][ay] = MV_RIGHT;
5390 if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
5391 DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
5392 IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
5397 if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
5398 DrawLevelField(ax, ay);
5400 if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
5402 if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
5403 unten_massiv = TRUE;
5404 if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
5405 links_massiv = TRUE;
5406 if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
5407 rechts_massiv = TRUE;
5409 if (((oben_massiv && unten_massiv) ||
5410 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
5411 element == EL_EXPANDABLE_WALL) &&
5412 ((links_massiv && rechts_massiv) ||
5413 element == EL_EXPANDABLE_WALL_VERTICAL))
5414 Feld[ax][ay] = EL_WALL;
5418 PlaySoundLevelAction(ax, ay, ACTION_GROWING);
5420 PlaySoundLevel(ax, ay, SND_EXPANDABLE_WALL_GROWING);
5424 void CheckForDragon(int x, int y)
5427 boolean dragon_found = FALSE;
5428 static int xy[4][2] =
5440 int xx = x + j*xy[i][0], yy = y + j*xy[i][1];
5442 if (IN_LEV_FIELD(xx, yy) &&
5443 (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
5445 if (Feld[xx][yy] == EL_DRAGON)
5446 dragon_found = TRUE;
5459 int xx = x + j*xy[i][0], yy = y + j*xy[i][1];
5461 if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
5463 Feld[xx][yy] = EL_EMPTY;
5464 DrawLevelField(xx, yy);
5473 static void InitBuggyBase(int x, int y)
5475 int element = Feld[x][y];
5476 int activating_delay = FRAMES_PER_SECOND / 4;
5479 (element == EL_SP_BUGGY_BASE ?
5480 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
5481 element == EL_SP_BUGGY_BASE_ACTIVATING ?
5483 element == EL_SP_BUGGY_BASE_ACTIVE ?
5484 1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
5487 static void WarnBuggyBase(int x, int y)
5490 static int xy[4][2] =
5500 int xx = x + xy[i][0], yy = y + xy[i][1];
5502 if (IS_PLAYER(xx, yy))
5504 PlaySoundLevel(x, y, SND_SP_BUGGY_BASE_ACTIVE);
5511 static void InitTrap(int x, int y)
5513 ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
5516 static void ActivateTrap(int x, int y)
5518 PlaySoundLevel(x, y, SND_TRAP_ACTIVATING);
5521 static void ChangeActiveTrap(int x, int y)
5523 int graphic = IMG_TRAP_ACTIVE;
5525 /* if new animation frame was drawn, correct crumbled sand border */
5526 if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
5527 DrawLevelFieldCrumbledSand(x, y);
5530 static void ChangeElementNowExt(int x, int y, int target_element)
5532 /* check if element under player changes from accessible to unaccessible
5533 (needed for special case of dropping element which then changes) */
5534 if (IS_PLAYER(x, y) && !PLAYER_PROTECTED(x, y) &&
5535 IS_ACCESSIBLE(Feld[x][y]) && !IS_ACCESSIBLE(target_element))
5542 Feld[x][y] = target_element;
5544 Changed[x][y] |= ChangeEvent[x][y]; /* ignore same changes in this frame */
5546 ResetGfxAnimation(x, y);
5547 ResetRandomAnimationValue(x, y);
5549 InitField(x, y, FALSE);
5550 if (CAN_MOVE(Feld[x][y]))
5553 DrawLevelField(x, y);
5555 if (GFX_CRUMBLED(Feld[x][y]))
5556 DrawLevelFieldCrumbledSandNeighbours(x, y);
5558 TestIfBadThingTouchesHero(x, y);
5559 TestIfPlayerTouchesCustomElement(x, y);
5560 TestIfElementTouchesCustomElement(x, y);
5562 if (ELEM_IS_PLAYER(target_element))
5563 RelocatePlayer(x, y, target_element);
5566 static boolean ChangeElementNow(int x, int y, int element, int page)
5568 struct ElementChangeInfo *change = &element_info[element].change_page[page];
5570 /* always use default change event to prevent running into a loop */
5571 if (ChangeEvent[x][y] == CE_BITMASK_DEFAULT)
5572 ChangeEvent[x][y] = CH_EVENT_BIT(CE_DELAY);
5574 /* do not change already changed elements with same change event */
5576 if (Changed[x][y] & ChangeEvent[x][y])
5583 Changed[x][y] |= ChangeEvent[x][y]; /* ignore same changes in this frame */
5585 CheckTriggeredElementChange(x, y, Feld[x][y], CE_OTHER_IS_CHANGING);
5587 if (change->explode)
5594 if (change->use_content)
5596 boolean complete_change = TRUE;
5597 boolean can_change[3][3];
5600 for (yy = 0; yy < 3; yy++) for(xx = 0; xx < 3 ; xx++)
5602 boolean half_destructible;
5603 int ex = x + xx - 1;
5604 int ey = y + yy - 1;
5607 can_change[xx][yy] = TRUE;
5609 if (ex == x && ey == y) /* do not check changing element itself */
5612 if (change->content[xx][yy] == EL_EMPTY_SPACE)
5614 can_change[xx][yy] = FALSE; /* do not change empty borders */
5619 if (!IN_LEV_FIELD(ex, ey))
5621 can_change[xx][yy] = FALSE;
5622 complete_change = FALSE;
5629 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
5630 e = MovingOrBlocked2Element(ex, ey);
5632 half_destructible = (IS_FREE(ex, ey) || IS_DIGGABLE(e));
5634 if ((change->power <= CP_NON_DESTRUCTIVE && !IS_FREE(ex, ey)) ||
5635 (change->power <= CP_HALF_DESTRUCTIVE && !half_destructible) ||
5636 (change->power <= CP_FULL_DESTRUCTIVE && IS_INDESTRUCTIBLE(e)))
5638 can_change[xx][yy] = FALSE;
5639 complete_change = FALSE;
5643 if (!change->only_complete || complete_change)
5645 boolean something_has_changed = FALSE;
5647 if (change->only_complete && change->use_random_change &&
5648 RND(100) < change->random)
5651 for (yy = 0; yy < 3; yy++) for(xx = 0; xx < 3 ; xx++)
5653 int ex = x + xx - 1;
5654 int ey = y + yy - 1;
5656 if (can_change[xx][yy] && (!change->use_random_change ||
5657 RND(100) < change->random))
5659 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
5660 RemoveMovingField(ex, ey);
5662 ChangeEvent[ex][ey] = ChangeEvent[x][y];
5664 ChangeElementNowExt(ex, ey, change->content[xx][yy]);
5666 something_has_changed = TRUE;
5668 /* for symmetry reasons, freeze newly created border elements */
5669 if (ex != x || ey != y)
5670 Stop[ex][ey] = TRUE; /* no more moving in this frame */
5674 if (something_has_changed)
5675 PlaySoundLevelElementAction(x, y, element, ACTION_CHANGING);
5680 ChangeElementNowExt(x, y, change->target_element);
5682 PlaySoundLevelElementAction(x, y, element, ACTION_CHANGING);
5688 static void ChangeElement(int x, int y, int page)
5690 int element = MovingOrBlocked2Element(x, y);
5691 struct ElementInfo *ei = &element_info[element];
5692 struct ElementChangeInfo *change = &ei->change_page[page];
5696 if (!CAN_CHANGE(element))
5699 printf("ChangeElement(): %d,%d: element = %d ('%s')\n",
5700 x, y, element, element_info[element].token_name);
5701 printf("ChangeElement(): This should never happen!\n");
5707 if (ChangeDelay[x][y] == 0) /* initialize element change */
5709 ChangeDelay[x][y] = ( change->delay_fixed * change->delay_frames +
5710 RND(change->delay_random * change->delay_frames)) + 1;
5712 ResetGfxAnimation(x, y);
5713 ResetRandomAnimationValue(x, y);
5715 if (change->pre_change_function)
5716 change->pre_change_function(x, y);
5719 ChangeDelay[x][y]--;
5721 if (ChangeDelay[x][y] != 0) /* continue element change */
5723 int graphic = el_act_dir2img(element, GfxAction[x][y], MovDir[x][y]);
5725 if (IS_ANIMATED(graphic))
5726 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
5728 if (change->change_function)
5729 change->change_function(x, y);
5731 else /* finish element change */
5733 if (ChangePage[x][y] != -1) /* remember page from delayed change */
5735 page = ChangePage[x][y];
5736 ChangePage[x][y] = -1;
5739 if (IS_MOVING(x, y)) /* never change a running system ;-) */
5741 ChangeDelay[x][y] = 1; /* try change after next move step */
5742 ChangePage[x][y] = page; /* remember page to use for change */
5747 if (ChangeElementNow(x, y, element, page))
5749 if (change->post_change_function)
5750 change->post_change_function(x, y);
5755 static boolean CheckTriggeredElementSideChange(int lx, int ly,
5756 int trigger_element,
5762 if (!(trigger_events[trigger_element] & CH_EVENT_BIT(trigger_event)))
5765 for (i=0; i < NUM_CUSTOM_ELEMENTS; i++)
5767 int element = EL_CUSTOM_START + i;
5769 boolean change_element = FALSE;
5772 if (!CAN_CHANGE(element) || !HAS_ANY_CHANGE_EVENT(element, trigger_event))
5775 for (j=0; j < element_info[element].num_change_pages; j++)
5777 struct ElementChangeInfo *change = &element_info[element].change_page[j];
5779 if (change->can_change &&
5781 change->events & CH_EVENT_BIT(trigger_event) &&
5783 change->sides & trigger_side &&
5784 change->trigger_element == trigger_element)
5787 if (!(change->events & CH_EVENT_BIT(trigger_event)))
5788 printf("::: !!! %d triggers %d: using wrong page %d [event %d]\n",
5789 trigger_element-EL_CUSTOM_START+1, i+1, j, trigger_event);
5792 change_element = TRUE;
5799 if (!change_element)
5802 for (y=0; y<lev_fieldy; y++) for (x=0; x<lev_fieldx; x++)
5805 if (x == lx && y == ly) /* do not change trigger element itself */
5809 if (Feld[x][y] == element)
5811 ChangeDelay[x][y] = 1;
5812 ChangeEvent[x][y] = CH_EVENT_BIT(trigger_event);
5813 ChangeElement(x, y, page);
5821 static boolean CheckTriggeredElementChange(int lx, int ly, int trigger_element,
5824 return CheckTriggeredElementSideChange(lx, ly, trigger_element, CH_SIDE_ANY,
5828 static boolean CheckElementSideChange(int x, int y, int element, int side,
5829 int trigger_event, int page)
5831 if (!CAN_CHANGE(element) || !HAS_ANY_CHANGE_EVENT(element, trigger_event))
5834 if (Feld[x][y] == EL_BLOCKED)
5836 Blocked2Moving(x, y, &x, &y);
5837 element = Feld[x][y];
5841 page = element_info[element].event_page_nr[trigger_event];
5843 if (!(element_info[element].change_page[page].sides & side))
5846 ChangeDelay[x][y] = 1;
5847 ChangeEvent[x][y] = CH_EVENT_BIT(trigger_event);
5848 ChangeElement(x, y, page);
5853 static boolean CheckElementChange(int x, int y, int element, int trigger_event)
5855 return CheckElementSideChange(x, y, element, CH_SIDE_ANY, trigger_event, -1);
5858 static void PlayerActions(struct PlayerInfo *player, byte player_action)
5860 static byte stored_player_action[MAX_PLAYERS];
5861 static int num_stored_actions = 0;
5862 boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
5863 int left = player_action & JOY_LEFT;
5864 int right = player_action & JOY_RIGHT;
5865 int up = player_action & JOY_UP;
5866 int down = player_action & JOY_DOWN;
5867 int button1 = player_action & JOY_BUTTON_1;
5868 int button2 = player_action & JOY_BUTTON_2;
5869 int dx = (left ? -1 : right ? 1 : 0);
5870 int dy = (up ? -1 : down ? 1 : 0);
5872 stored_player_action[player->index_nr] = 0;
5873 num_stored_actions++;
5875 if (!player->active || tape.pausing)
5881 snapped = SnapField(player, dx, dy);
5885 dropped = DropElement(player);
5887 moved = MovePlayer(player, dx, dy);
5890 if (tape.single_step && tape.recording && !tape.pausing)
5892 if (button1 || (dropped && !moved))
5894 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
5895 SnapField(player, 0, 0); /* stop snapping */
5899 stored_player_action[player->index_nr] = player_action;
5903 /* no actions for this player (no input at player's configured device) */
5905 DigField(player, 0, 0, 0, 0, DF_NO_PUSH);
5906 SnapField(player, 0, 0);
5907 CheckGravityMovement(player);
5909 if (player->MovPos == 0)
5910 InitPlayerGfxAnimation(player, ACTION_DEFAULT, player->MovDir);
5912 if (player->MovPos == 0) /* needed for tape.playing */
5913 player->is_moving = FALSE;
5916 if (tape.recording && num_stored_actions >= MAX_PLAYERS)
5918 TapeRecordAction(stored_player_action);
5919 num_stored_actions = 0;
5925 static unsigned long action_delay = 0;
5926 unsigned long action_delay_value;
5927 int magic_wall_x = 0, magic_wall_y = 0;
5928 int i, x, y, element, graphic;
5929 byte *recorded_player_action;
5930 byte summarized_player_action = 0;
5932 if (game_status != GAME_MODE_PLAYING)
5935 action_delay_value =
5936 (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
5938 if (tape.playing && tape.index_search && !tape.pausing)
5939 action_delay_value = 0;
5941 /* ---------- main game synchronization point ---------- */
5943 WaitUntilDelayReached(&action_delay, action_delay_value);
5945 if (network_playing && !network_player_action_received)
5949 printf("DEBUG: try to get network player actions in time\n");
5953 #if defined(PLATFORM_UNIX)
5954 /* last chance to get network player actions without main loop delay */
5958 if (game_status != GAME_MODE_PLAYING)
5961 if (!network_player_action_received)
5965 printf("DEBUG: failed to get network player actions in time\n");
5975 recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
5977 for (i=0; i<MAX_PLAYERS; i++)
5979 summarized_player_action |= stored_player[i].action;
5981 if (!network_playing)
5982 stored_player[i].effective_action = stored_player[i].action;
5985 #if defined(PLATFORM_UNIX)
5986 if (network_playing)
5987 SendToServer_MovePlayer(summarized_player_action);
5990 if (!options.network && !setup.team_mode)
5991 local_player->effective_action = summarized_player_action;
5993 for (i=0; i<MAX_PLAYERS; i++)
5995 int actual_player_action = stored_player[i].effective_action;
5997 if (stored_player[i].programmed_action)
5998 actual_player_action = stored_player[i].programmed_action;
6000 if (recorded_player_action)
6001 actual_player_action = recorded_player_action[i];
6003 PlayerActions(&stored_player[i], actual_player_action);
6004 ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
6007 network_player_action_received = FALSE;
6009 ScrollScreen(NULL, SCROLL_GO_ON);
6015 for (i=0; i<MAX_PLAYERS; i++)
6016 stored_player[i].Frame++;
6020 if (game.engine_version < VERSION_IDENT(2,2,0,7))
6022 for (i=0; i<MAX_PLAYERS; i++)
6024 struct PlayerInfo *player = &stored_player[i];
6028 if (player->active && player->is_pushing && player->is_moving &&
6031 ContinueMoving(x, y);
6033 /* continue moving after pushing (this is actually a bug) */
6034 if (!IS_MOVING(x, y))
6043 for (y=0; y<lev_fieldy; y++) for (x=0; x<lev_fieldx; x++)
6045 Changed[x][y] = CE_BITMASK_DEFAULT;
6046 ChangeEvent[x][y] = CE_BITMASK_DEFAULT;
6049 if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
6051 printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
6052 printf("GameActions(): This should never happen!\n");
6054 ChangePage[x][y] = -1;
6059 if (WasJustMoving[x][y] > 0)
6060 WasJustMoving[x][y]--;
6061 if (WasJustFalling[x][y] > 0)
6062 WasJustFalling[x][y]--;
6067 /* reset finished pushing action (not done in ContinueMoving() to allow
6068 continous pushing animation for elements with zero push delay) */
6069 if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
6071 ResetGfxAnimation(x, y);
6072 DrawLevelField(x, y);
6077 if (IS_BLOCKED(x, y))
6081 Blocked2Moving(x, y, &oldx, &oldy);
6082 if (!IS_MOVING(oldx, oldy))
6084 printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
6085 printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
6086 printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
6087 printf("GameActions(): This should never happen!\n");
6093 for (y=0; y<lev_fieldy; y++) for (x=0; x<lev_fieldx; x++)
6095 element = Feld[x][y];
6097 graphic = el_act_dir2img(element, GfxAction[x][y], MovDir[x][y]);
6099 graphic = el2img(element);
6105 printf("::: %d,%d: %d [%d]\n", x, y, element, FrameCounter);
6107 element = graphic = 0;
6111 if (graphic_info[graphic].anim_global_sync)
6112 GfxFrame[x][y] = FrameCounter;
6114 if (ANIM_MODE(graphic) == ANIM_RANDOM &&
6115 IS_NEXT_FRAME(GfxFrame[x][y], graphic))
6116 ResetRandomAnimationValue(x, y);
6118 SetRandomAnimationValue(x, y);
6121 PlaySoundLevelActionIfLoop(x, y, GfxAction[x][y]);
6124 if (IS_INACTIVE(element))
6126 if (IS_ANIMATED(graphic))
6127 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
6133 /* this may take place after moving, so 'element' may have changed */
6134 if (IS_CHANGING(x, y))
6137 ChangeElement(x, y, ChangePage[x][y] != -1 ? ChangePage[x][y] :
6138 element_info[element].event_page_nr[CE_DELAY]);
6140 ChangeElement(x, y, element_info[element].event_page_nr[CE_DELAY]);
6143 element = Feld[x][y];
6144 graphic = el_act_dir2img(element, GfxAction[x][y], MovDir[x][y]);
6148 if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
6153 graphic = el_act_dir2img(element, GfxAction[x][y], MovDir[x][y]);
6155 if (element == EL_MOLE)
6156 printf("::: %d, %d, %d [%d]\n",
6157 IS_ANIMATED(graphic), IS_MOVING(x, y), Stop[x][y],
6161 if (element == EL_YAMYAM)
6162 printf("::: %d, %d, %d\n",
6163 IS_ANIMATED(graphic), IS_MOVING(x, y), Stop[x][y]);
6167 if (IS_ANIMATED(graphic) &&
6171 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
6174 if (element == EL_MOLE)
6175 printf("::: %d, %d\n", graphic, GfxFrame[x][y]);
6179 if (IS_GEM(element) || element == EL_SP_INFOTRON)
6180 EdelsteinFunkeln(x, y);
6182 else if ((element == EL_ACID ||
6183 element == EL_EXIT_OPEN ||
6184 element == EL_SP_EXIT_OPEN ||
6185 element == EL_SP_TERMINAL ||
6186 element == EL_SP_TERMINAL_ACTIVE ||
6187 element == EL_EXTRA_TIME ||
6188 element == EL_SHIELD_NORMAL ||
6189 element == EL_SHIELD_DEADLY) &&
6190 IS_ANIMATED(graphic))
6191 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
6192 else if (IS_MOVING(x, y))
6193 ContinueMoving(x, y);
6194 else if (IS_ACTIVE_BOMB(element))
6195 CheckDynamite(x, y);
6197 else if (element == EL_EXPLOSION && !game.explosions_delayed)
6198 Explode(x, y, ExplodePhase[x][y], EX_NORMAL);
6200 else if (element == EL_AMOEBA_GROWING)
6201 AmoebeWaechst(x, y);
6202 else if (element == EL_AMOEBA_SHRINKING)
6203 AmoebaDisappearing(x, y);
6205 #if !USE_NEW_AMOEBA_CODE
6206 else if (IS_AMOEBALIVE(element))
6207 AmoebeAbleger(x, y);
6210 else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
6212 else if (element == EL_EXIT_CLOSED)
6214 else if (element == EL_SP_EXIT_CLOSED)
6216 else if (element == EL_EXPANDABLE_WALL_GROWING)
6218 else if (element == EL_EXPANDABLE_WALL ||
6219 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
6220 element == EL_EXPANDABLE_WALL_VERTICAL ||
6221 element == EL_EXPANDABLE_WALL_ANY)
6223 else if (element == EL_FLAMES)
6224 CheckForDragon(x, y);
6226 else if (IS_AUTO_CHANGING(element))
6227 ChangeElement(x, y);
6229 else if (element == EL_EXPLOSION)
6230 ; /* drawing of correct explosion animation is handled separately */
6231 else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
6232 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
6235 /* this may take place after moving, so 'element' may have changed */
6236 if (IS_AUTO_CHANGING(Feld[x][y]))
6237 ChangeElement(x, y);
6240 if (IS_BELT_ACTIVE(element))
6241 PlaySoundLevelAction(x, y, ACTION_ACTIVE);
6243 if (game.magic_wall_active)
6245 int jx = local_player->jx, jy = local_player->jy;
6247 /* play the element sound at the position nearest to the player */
6248 if ((element == EL_MAGIC_WALL_FULL ||
6249 element == EL_MAGIC_WALL_ACTIVE ||
6250 element == EL_MAGIC_WALL_EMPTYING ||
6251 element == EL_BD_MAGIC_WALL_FULL ||
6252 element == EL_BD_MAGIC_WALL_ACTIVE ||
6253 element == EL_BD_MAGIC_WALL_EMPTYING) &&
6254 ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
6262 #if USE_NEW_AMOEBA_CODE
6263 /* new experimental amoeba growth stuff */
6265 if (!(FrameCounter % 8))
6268 static unsigned long random = 1684108901;
6270 for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
6273 x = (random >> 10) % lev_fieldx;
6274 y = (random >> 20) % lev_fieldy;
6276 x = RND(lev_fieldx);
6277 y = RND(lev_fieldy);
6279 element = Feld[x][y];
6281 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
6282 if (!IS_PLAYER(x,y) &&
6283 (element == EL_EMPTY ||
6284 element == EL_SAND ||
6285 element == EL_QUICKSAND_EMPTY ||
6286 element == EL_ACID_SPLASH_LEFT ||
6287 element == EL_ACID_SPLASH_RIGHT))
6289 if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
6290 (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
6291 (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
6292 (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
6293 Feld[x][y] = EL_AMOEBA_DROP;
6296 random = random * 129 + 1;
6302 if (game.explosions_delayed)
6305 game.explosions_delayed = FALSE;
6307 for (y=0; y<lev_fieldy; y++) for (x=0; x<lev_fieldx; x++)
6309 element = Feld[x][y];
6311 if (ExplodeField[x][y])
6312 Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
6313 else if (element == EL_EXPLOSION)
6314 Explode(x, y, ExplodePhase[x][y], EX_NORMAL);
6316 ExplodeField[x][y] = EX_NO_EXPLOSION;
6319 game.explosions_delayed = TRUE;
6322 if (game.magic_wall_active)
6324 if (!(game.magic_wall_time_left % 4))
6326 int element = Feld[magic_wall_x][magic_wall_y];
6328 if (element == EL_BD_MAGIC_WALL_FULL ||
6329 element == EL_BD_MAGIC_WALL_ACTIVE ||
6330 element == EL_BD_MAGIC_WALL_EMPTYING)
6331 PlaySoundLevel(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
6333 PlaySoundLevel(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
6336 if (game.magic_wall_time_left > 0)
6338 game.magic_wall_time_left--;
6339 if (!game.magic_wall_time_left)
6341 for (y=0; y<lev_fieldy; y++) for (x=0; x<lev_fieldx; x++)
6343 element = Feld[x][y];
6345 if (element == EL_MAGIC_WALL_ACTIVE ||
6346 element == EL_MAGIC_WALL_FULL)
6348 Feld[x][y] = EL_MAGIC_WALL_DEAD;
6349 DrawLevelField(x, y);
6351 else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
6352 element == EL_BD_MAGIC_WALL_FULL)
6354 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
6355 DrawLevelField(x, y);
6359 game.magic_wall_active = FALSE;
6364 if (game.light_time_left > 0)
6366 game.light_time_left--;
6368 if (game.light_time_left == 0)
6369 RedrawAllLightSwitchesAndInvisibleElements();
6372 if (game.timegate_time_left > 0)
6374 game.timegate_time_left--;
6376 if (game.timegate_time_left == 0)
6377 CloseAllOpenTimegates();
6380 for (i=0; i<MAX_PLAYERS; i++)
6382 struct PlayerInfo *player = &stored_player[i];
6384 if (SHIELD_ON(player))
6386 if (player->shield_deadly_time_left)
6387 PlaySoundLevel(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
6388 else if (player->shield_normal_time_left)
6389 PlaySoundLevel(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
6393 if (TimeFrames >= FRAMES_PER_SECOND)
6398 for (i=0; i<MAX_PLAYERS; i++)
6400 struct PlayerInfo *player = &stored_player[i];
6402 if (SHIELD_ON(player))
6404 player->shield_normal_time_left--;
6406 if (player->shield_deadly_time_left > 0)
6407 player->shield_deadly_time_left--;
6411 if (tape.recording || tape.playing)
6412 DrawVideoDisplay(VIDEO_STATE_TIME_ON, TimePlayed);
6418 if (TimeLeft <= 10 && setup.time_limit)
6419 PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, SOUND_MIDDLE);
6421 DrawText(DX_TIME, DY_TIME, int2str(TimeLeft, 3), FONT_TEXT_2);
6423 if (!TimeLeft && setup.time_limit)
6424 for (i=0; i<MAX_PLAYERS; i++)
6425 KillHero(&stored_player[i]);
6427 else if (level.time == 0 && !AllPlayersGone) /* level without time limit */
6428 DrawText(DX_TIME, DY_TIME, int2str(TimePlayed, 3), FONT_TEXT_2);
6433 if (options.debug) /* calculate frames per second */
6435 static unsigned long fps_counter = 0;
6436 static int fps_frames = 0;
6437 unsigned long fps_delay_ms = Counter() - fps_counter;
6441 if (fps_delay_ms >= 500) /* calculate fps every 0.5 seconds */
6443 global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
6446 fps_counter = Counter();
6449 redraw_mask |= REDRAW_FPS;
6453 if (stored_player[0].jx != stored_player[0].last_jx ||
6454 stored_player[0].jy != stored_player[0].last_jy)
6455 printf("::: %d, %d, %d, %d, %d\n",
6456 stored_player[0].MovDir,
6457 stored_player[0].MovPos,
6458 stored_player[0].GfxPos,
6459 stored_player[0].Frame,
6460 stored_player[0].StepFrame);
6467 for (i=0; i<MAX_PLAYERS; i++)
6470 MOVE_DELAY_NORMAL_SPEED / stored_player[i].move_delay_value;
6472 stored_player[i].Frame += move_frames;
6474 if (stored_player[i].MovPos != 0)
6475 stored_player[i].StepFrame += move_frames;
6480 if (local_player->show_envelope != 0 && local_player->MovPos == 0)
6482 ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
6484 local_player->show_envelope = 0;
6489 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
6491 int min_x = x, min_y = y, max_x = x, max_y = y;
6494 for (i=0; i<MAX_PLAYERS; i++)
6496 int jx = stored_player[i].jx, jy = stored_player[i].jy;
6498 if (!stored_player[i].active || &stored_player[i] == player)
6501 min_x = MIN(min_x, jx);
6502 min_y = MIN(min_y, jy);
6503 max_x = MAX(max_x, jx);
6504 max_y = MAX(max_y, jy);
6507 return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
6510 static boolean AllPlayersInVisibleScreen()
6514 for (i=0; i<MAX_PLAYERS; i++)
6516 int jx = stored_player[i].jx, jy = stored_player[i].jy;
6518 if (!stored_player[i].active)
6521 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
6528 void ScrollLevel(int dx, int dy)
6530 int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
6533 BlitBitmap(drawto_field, drawto_field,
6534 FX + TILEX * (dx == -1) - softscroll_offset,
6535 FY + TILEY * (dy == -1) - softscroll_offset,
6536 SXSIZE - TILEX * (dx!=0) + 2 * softscroll_offset,
6537 SYSIZE - TILEY * (dy!=0) + 2 * softscroll_offset,
6538 FX + TILEX * (dx == 1) - softscroll_offset,
6539 FY + TILEY * (dy == 1) - softscroll_offset);
6543 x = (dx == 1 ? BX1 : BX2);
6544 for (y=BY1; y <= BY2; y++)
6545 DrawScreenField(x, y);
6550 y = (dy == 1 ? BY1 : BY2);
6551 for (x=BX1; x <= BX2; x++)
6552 DrawScreenField(x, y);
6555 redraw_mask |= REDRAW_FIELD;
6558 static void CheckGravityMovement(struct PlayerInfo *player)
6560 if (game.gravity && !player->programmed_action)
6562 int move_dir_vertical = player->action & (MV_UP | MV_DOWN);
6563 int move_dir_horizontal = player->action & (MV_LEFT | MV_RIGHT);
6565 (player->last_move_dir & (MV_LEFT | MV_RIGHT) ?
6566 (move_dir_vertical ? move_dir_vertical : move_dir_horizontal) :
6567 (move_dir_horizontal ? move_dir_horizontal : move_dir_vertical));
6568 int jx = player->jx, jy = player->jy;
6569 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
6570 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
6571 int new_jx = jx + dx, new_jy = jy + dy;
6572 boolean field_under_player_is_free =
6573 (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
6574 boolean player_is_moving_to_valid_field =
6575 (IN_LEV_FIELD(new_jx, new_jy) &&
6576 (Feld[new_jx][new_jy] == EL_SP_BASE ||
6577 Feld[new_jx][new_jy] == EL_SAND));
6578 /* !!! extend EL_SAND to anything diggable !!! */
6580 if (field_under_player_is_free &&
6581 !player_is_moving_to_valid_field &&
6582 !IS_WALKABLE_INSIDE(Feld[jx][jy]))
6583 player->programmed_action = MV_DOWN;
6589 -----------------------------------------------------------------------------
6590 dx, dy: direction (non-diagonal) to try to move the player to
6591 real_dx, real_dy: direction as read from input device (can be diagonal)
6594 boolean MovePlayerOneStep(struct PlayerInfo *player,
6595 int dx, int dy, int real_dx, int real_dy)
6598 static int change_sides[4][2] =
6600 /* enter side leave side */
6601 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* moving left */
6602 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* moving right */
6603 { CH_SIDE_BOTTOM, CH_SIDE_TOP }, /* moving up */
6604 { CH_SIDE_TOP, CH_SIDE_BOTTOM } /* moving down */
6606 int move_direction = (dx == -1 ? MV_LEFT :
6607 dx == +1 ? MV_RIGHT :
6609 dy == +1 ? MV_DOWN : MV_NO_MOVING);
6610 int enter_side = change_sides[MV_DIR_BIT(move_direction)][0];
6611 int leave_side = change_sides[MV_DIR_BIT(move_direction)][1];
6613 int jx = player->jx, jy = player->jy;
6614 int new_jx = jx + dx, new_jy = jy + dy;
6618 if (!player->active || (!dx && !dy))
6619 return MF_NO_ACTION;
6621 player->MovDir = (dx < 0 ? MV_LEFT :
6624 dy > 0 ? MV_DOWN : MV_NO_MOVING);
6626 if (!IN_LEV_FIELD(new_jx, new_jy))
6627 return MF_NO_ACTION;
6629 if (!options.network && !AllPlayersInSight(player, new_jx, new_jy))
6630 return MF_NO_ACTION;
6633 element = MovingOrBlocked2Element(new_jx, new_jy);
6635 element = MovingOrBlocked2ElementIfNotLeaving(new_jx, new_jy);
6638 if (DONT_RUN_INTO(element))
6640 if (element == EL_ACID && dx == 0 && dy == 1)
6643 Feld[jx][jy] = EL_PLAYER_1;
6644 InitMovingField(jx, jy, MV_DOWN);
6645 Store[jx][jy] = EL_ACID;
6646 ContinueMoving(jx, jy);
6650 TestIfHeroRunsIntoBadThing(jx, jy, player->MovDir);
6655 can_move = DigField(player, new_jx, new_jy, real_dx, real_dy, DF_DIG);
6656 if (can_move != MF_MOVING)
6659 /* check if DigField() has caused relocation of the player */
6660 if (player->jx != jx || player->jy != jy)
6661 return MF_NO_ACTION;
6663 StorePlayer[jx][jy] = 0;
6664 player->last_jx = jx;
6665 player->last_jy = jy;
6666 player->jx = new_jx;
6667 player->jy = new_jy;
6668 StorePlayer[new_jx][new_jy] = player->element_nr;
6671 (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
6673 ScrollPlayer(player, SCROLL_INIT);
6676 if (IS_CUSTOM_ELEMENT(Feld[jx][jy]))
6678 CheckTriggeredElementSideChange(jx, jy, Feld[jx][jy], leave_side,
6679 CE_OTHER_GETS_LEFT);
6680 CheckElementSideChange(jx, jy, Feld[jx][jy], leave_side,
6681 CE_LEFT_BY_PLAYER, -1);
6684 if (IS_CUSTOM_ELEMENT(Feld[new_jx][new_jy]))
6686 CheckTriggeredElementSideChange(new_jx, new_jy, Feld[new_jx][new_jy],
6687 enter_side, CE_OTHER_GETS_ENTERED);
6688 CheckElementSideChange(new_jx, new_jy, Feld[new_jx][new_jy], enter_side,
6689 CE_ENTERED_BY_PLAYER, -1);
6696 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
6698 int jx = player->jx, jy = player->jy;
6699 int old_jx = jx, old_jy = jy;
6700 int moved = MF_NO_ACTION;
6702 if (!player->active || (!dx && !dy))
6706 if (!FrameReached(&player->move_delay, player->move_delay_value) &&
6710 if (!FrameReached(&player->move_delay, player->move_delay_value) &&
6711 !(tape.playing && tape.file_version < FILE_VERSION_2_0))
6715 /* remove the last programmed player action */
6716 player->programmed_action = 0;
6720 /* should only happen if pre-1.2 tape recordings are played */
6721 /* this is only for backward compatibility */
6723 int original_move_delay_value = player->move_delay_value;
6726 printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%ld]\n",
6730 /* scroll remaining steps with finest movement resolution */
6731 player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
6733 while (player->MovPos)
6735 ScrollPlayer(player, SCROLL_GO_ON);
6736 ScrollScreen(NULL, SCROLL_GO_ON);
6742 player->move_delay_value = original_move_delay_value;
6745 if (player->last_move_dir & (MV_LEFT | MV_RIGHT))
6747 if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
6748 moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
6752 if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
6753 moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
6759 if (moved & MF_MOVING && !ScreenMovPos &&
6760 (player == local_player || !options.network))
6762 int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
6763 int offset = (setup.scroll_delay ? 3 : 0);
6765 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
6767 /* actual player has left the screen -- scroll in that direction */
6768 if (jx != old_jx) /* player has moved horizontally */
6769 scroll_x += (jx - old_jx);
6770 else /* player has moved vertically */
6771 scroll_y += (jy - old_jy);
6775 if (jx != old_jx) /* player has moved horizontally */
6777 if ((player->MovDir == MV_LEFT && scroll_x > jx - MIDPOSX + offset) ||
6778 (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
6779 scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
6781 /* don't scroll over playfield boundaries */
6782 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
6783 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
6785 /* don't scroll more than one field at a time */
6786 scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
6788 /* don't scroll against the player's moving direction */
6789 if ((player->MovDir == MV_LEFT && scroll_x > old_scroll_x) ||
6790 (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
6791 scroll_x = old_scroll_x;
6793 else /* player has moved vertically */
6795 if ((player->MovDir == MV_UP && scroll_y > jy - MIDPOSY + offset) ||
6796 (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
6797 scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
6799 /* don't scroll over playfield boundaries */
6800 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
6801 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
6803 /* don't scroll more than one field at a time */
6804 scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
6806 /* don't scroll against the player's moving direction */
6807 if ((player->MovDir == MV_UP && scroll_y > old_scroll_y) ||
6808 (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
6809 scroll_y = old_scroll_y;
6813 if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
6815 if (!options.network && !AllPlayersInVisibleScreen())
6817 scroll_x = old_scroll_x;
6818 scroll_y = old_scroll_y;
6822 ScrollScreen(player, SCROLL_INIT);
6823 ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
6830 InitPlayerGfxAnimation(player, ACTION_DEFAULT);
6832 if (!(moved & MF_MOVING) && !player->is_pushing)
6837 player->StepFrame = 0;
6839 if (moved & MF_MOVING)
6841 if (old_jx != jx && old_jy == jy)
6842 player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
6843 else if (old_jx == jx && old_jy != jy)
6844 player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
6846 DrawLevelField(jx, jy); /* for "crumbled sand" */
6848 player->last_move_dir = player->MovDir;
6849 player->is_moving = TRUE;
6851 player->is_snapping = FALSE;
6855 player->is_switching = FALSE;
6861 static int change_sides[4][2] =
6863 /* enter side leave side */
6864 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* moving left */
6865 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* moving right */
6866 { CH_SIDE_BOTTOM, CH_SIDE_TOP }, /* moving up */
6867 { CH_SIDE_TOP, CH_SIDE_BOTTOM } /* moving down */
6869 int move_direction = player->MovDir;
6870 int enter_side = change_sides[MV_DIR_BIT(move_direction)][0];
6871 int leave_side = change_sides[MV_DIR_BIT(move_direction)][1];
6874 if (IS_CUSTOM_ELEMENT(Feld[old_jx][old_jy]))
6876 CheckTriggeredElementSideChange(old_jx, old_jy, Feld[old_jx][old_jy],
6877 leave_side, CE_OTHER_GETS_LEFT);
6878 CheckElementSideChange(old_jx, old_jy, Feld[old_jx][old_jy],
6879 leave_side, CE_LEFT_BY_PLAYER, -1);
6882 if (IS_CUSTOM_ELEMENT(Feld[jx][jy]))
6884 CheckTriggeredElementSideChange(jx, jy, Feld[jx][jy],
6885 enter_side, CE_OTHER_GETS_ENTERED);
6886 CheckElementSideChange(jx, jy, Feld[jx][jy],
6887 enter_side, CE_ENTERED_BY_PLAYER, -1);
6898 CheckGravityMovement(player);
6901 player->last_move_dir = MV_NO_MOVING;
6903 player->is_moving = FALSE;
6906 if (game.engine_version < VERSION_IDENT(3,0,7,0))
6908 TestIfHeroTouchesBadThing(jx, jy);
6909 TestIfPlayerTouchesCustomElement(jx, jy);
6912 if (!player->active)
6918 void ScrollPlayer(struct PlayerInfo *player, int mode)
6920 int jx = player->jx, jy = player->jy;
6921 int last_jx = player->last_jx, last_jy = player->last_jy;
6922 int move_stepsize = TILEX / player->move_delay_value;
6924 if (!player->active || !player->MovPos)
6927 if (mode == SCROLL_INIT)
6929 player->actual_frame_counter = FrameCounter;
6930 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
6932 if (Feld[last_jx][last_jy] == EL_EMPTY)
6933 Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
6940 else if (!FrameReached(&player->actual_frame_counter, 1))
6943 player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
6944 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
6946 if (Feld[last_jx][last_jy] == EL_PLAYER_IS_LEAVING)
6947 Feld[last_jx][last_jy] = EL_EMPTY;
6949 /* before DrawPlayer() to draw correct player graphic for this case */
6950 if (player->MovPos == 0)
6951 CheckGravityMovement(player);
6954 DrawPlayer(player); /* needed here only to cleanup last field */
6957 if (player->MovPos == 0) /* player reached destination field */
6959 if (IS_PASSABLE(Feld[last_jx][last_jy]))
6961 /* continue with normal speed after quickly moving through gate */
6962 HALVE_PLAYER_SPEED(player);
6964 /* be able to make the next move without delay */
6965 player->move_delay = 0;
6968 player->last_jx = jx;
6969 player->last_jy = jy;
6971 if (Feld[jx][jy] == EL_EXIT_OPEN ||
6972 Feld[jx][jy] == EL_SP_EXIT_OPEN ||
6973 Feld[jx][jy] == EL_SP_EXIT_OPENING) /* <-- special case */
6975 DrawPlayer(player); /* needed here only to cleanup last field */
6978 if (local_player->friends_still_needed == 0 ||
6979 IS_SP_ELEMENT(Feld[jx][jy]))
6980 player->LevelSolved = player->GameOver = TRUE;
6983 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
6985 TestIfHeroTouchesBadThing(jx, jy);
6986 TestIfPlayerTouchesCustomElement(jx, jy);
6988 TestIfElementTouchesCustomElement(jx, jy); /* for empty space */
6991 if (!player->active)
6995 if (tape.single_step && tape.recording && !tape.pausing &&
6996 !player->programmed_action)
6997 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
7001 void ScrollScreen(struct PlayerInfo *player, int mode)
7003 static unsigned long screen_frame_counter = 0;
7005 if (mode == SCROLL_INIT)
7007 /* set scrolling step size according to actual player's moving speed */
7008 ScrollStepSize = TILEX / player->move_delay_value;
7010 screen_frame_counter = FrameCounter;
7011 ScreenMovDir = player->MovDir;
7012 ScreenMovPos = player->MovPos;
7013 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
7016 else if (!FrameReached(&screen_frame_counter, 1))
7021 ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
7022 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
7023 redraw_mask |= REDRAW_FIELD;
7026 ScreenMovDir = MV_NO_MOVING;
7029 void TestIfPlayerTouchesCustomElement(int x, int y)
7031 static int xy[4][2] =
7038 static int change_sides[4][2] =
7040 /* center side border side */
7041 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
7042 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
7043 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
7044 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
7046 static int touch_dir[4] =
7053 int center_element = Feld[x][y]; /* should always be non-moving! */
7058 int xx = x + xy[i][0];
7059 int yy = y + xy[i][1];
7060 int center_side = change_sides[i][0];
7061 int border_side = change_sides[i][1];
7064 if (!IN_LEV_FIELD(xx, yy))
7067 if (IS_PLAYER(x, y))
7069 if (game.engine_version < VERSION_IDENT(3,0,7,0))
7070 border_element = Feld[xx][yy]; /* may be moving! */
7071 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
7072 border_element = Feld[xx][yy];
7073 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
7074 border_element = MovingOrBlocked2Element(xx, yy);
7076 continue; /* center and border element do not touch */
7078 CheckTriggeredElementSideChange(xx, yy, border_element, border_side,
7079 CE_OTHER_GETS_TOUCHED);
7080 CheckElementSideChange(xx, yy, border_element, border_side,
7081 CE_TOUCHED_BY_PLAYER, -1);
7083 else if (IS_PLAYER(xx, yy))
7085 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
7087 struct PlayerInfo *player = PLAYERINFO(xx, yy);
7089 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
7090 continue; /* center and border element do not touch */
7093 CheckTriggeredElementSideChange(x, y, center_element, center_side,
7094 CE_OTHER_GETS_TOUCHED);
7095 CheckElementSideChange(x, y, center_element, center_side,
7096 CE_TOUCHED_BY_PLAYER, -1);
7103 void TestIfElementTouchesCustomElement(int x, int y)
7105 static int xy[4][2] =
7112 static int change_sides[4][2] =
7114 /* center side border side */
7115 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
7116 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
7117 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
7118 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
7120 static int touch_dir[4] =
7127 boolean change_center_element = FALSE;
7128 int center_element_change_page = 0;
7129 int center_element = Feld[x][y]; /* should always be non-moving! */
7134 int xx = x + xy[i][0];
7135 int yy = y + xy[i][1];
7136 int center_side = change_sides[i][0];
7137 int border_side = change_sides[i][1];
7140 if (!IN_LEV_FIELD(xx, yy))
7143 if (game.engine_version < VERSION_IDENT(3,0,7,0))
7144 border_element = Feld[xx][yy]; /* may be moving! */
7145 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
7146 border_element = Feld[xx][yy];
7147 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
7148 border_element = MovingOrBlocked2Element(xx, yy);
7150 continue; /* center and border element do not touch */
7152 /* check for change of center element (but change it only once) */
7153 if (IS_CUSTOM_ELEMENT(center_element) &&
7154 HAS_ANY_CHANGE_EVENT(center_element, CE_OTHER_IS_TOUCHING) &&
7155 !change_center_element)
7157 for (j=0; j < element_info[center_element].num_change_pages; j++)
7159 struct ElementChangeInfo *change =
7160 &element_info[center_element].change_page[j];
7162 if (change->can_change &&
7163 change->events & CH_EVENT_BIT(CE_OTHER_IS_TOUCHING) &&
7164 change->sides & border_side &&
7165 change->trigger_element == border_element)
7167 change_center_element = TRUE;
7168 center_element_change_page = j;
7175 /* check for change of border element */
7176 if (IS_CUSTOM_ELEMENT(border_element) &&
7177 HAS_ANY_CHANGE_EVENT(border_element, CE_OTHER_IS_TOUCHING))
7179 for (j=0; j < element_info[border_element].num_change_pages; j++)
7181 struct ElementChangeInfo *change =
7182 &element_info[border_element].change_page[j];
7184 if (change->can_change &&
7185 change->events & CH_EVENT_BIT(CE_OTHER_IS_TOUCHING) &&
7186 change->sides & center_side &&
7187 change->trigger_element == center_element)
7189 CheckElementSideChange(xx, yy, border_element, CH_SIDE_ANY,
7190 CE_OTHER_IS_TOUCHING, j);
7197 if (change_center_element)
7198 CheckElementSideChange(x, y, center_element, CH_SIDE_ANY,
7199 CE_OTHER_IS_TOUCHING, center_element_change_page);
7202 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
7204 int i, kill_x = -1, kill_y = -1;
7205 static int test_xy[4][2] =
7212 static int test_dir[4] =
7222 int test_x, test_y, test_move_dir, test_element;
7224 test_x = good_x + test_xy[i][0];
7225 test_y = good_y + test_xy[i][1];
7226 if (!IN_LEV_FIELD(test_x, test_y))
7230 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NO_MOVING);
7233 test_element = Feld[test_x][test_y];
7235 test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
7238 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
7239 2nd case: DONT_TOUCH style bad thing does not move away from good thing
7241 if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
7242 (DONT_TOUCH(test_element) && test_move_dir != test_dir[i]))
7250 if (kill_x != -1 || kill_y != -1)
7252 if (IS_PLAYER(good_x, good_y))
7254 struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
7256 if (player->shield_deadly_time_left > 0)
7257 Bang(kill_x, kill_y);
7258 else if (!PLAYER_PROTECTED(good_x, good_y))
7262 Bang(good_x, good_y);
7266 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
7268 int i, kill_x = -1, kill_y = -1;
7269 int bad_element = Feld[bad_x][bad_y];
7270 static int test_xy[4][2] =
7277 static int touch_dir[4] =
7284 static int test_dir[4] =
7292 if (bad_element == EL_EXPLOSION) /* skip just exploding bad things */
7297 int test_x, test_y, test_move_dir, test_element;
7299 test_x = bad_x + test_xy[i][0];
7300 test_y = bad_y + test_xy[i][1];
7301 if (!IN_LEV_FIELD(test_x, test_y))
7305 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NO_MOVING);
7307 test_element = Feld[test_x][test_y];
7309 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
7310 2nd case: DONT_TOUCH style bad thing does not move away from good thing
7312 if ((DONT_RUN_INTO(bad_element) && bad_move_dir == test_dir[i]) ||
7313 (DONT_TOUCH(bad_element) && test_move_dir != test_dir[i]))
7315 /* good thing is player or penguin that does not move away */
7316 if (IS_PLAYER(test_x, test_y))
7318 struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
7320 if (bad_element == EL_ROBOT && player->is_moving)
7321 continue; /* robot does not kill player if he is moving */
7323 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
7325 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
7326 continue; /* center and border element do not touch */
7333 else if (test_element == EL_PENGUIN)
7342 if (kill_x != -1 || kill_y != -1)
7344 if (IS_PLAYER(kill_x, kill_y))
7346 struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
7348 if (player->shield_deadly_time_left > 0)
7350 else if (!PLAYER_PROTECTED(kill_x, kill_y))
7354 Bang(kill_x, kill_y);
7358 void TestIfHeroTouchesBadThing(int x, int y)
7360 TestIfGoodThingHitsBadThing(x, y, MV_NO_MOVING);
7363 void TestIfHeroRunsIntoBadThing(int x, int y, int move_dir)
7365 TestIfGoodThingHitsBadThing(x, y, move_dir);
7368 void TestIfBadThingTouchesHero(int x, int y)
7370 TestIfBadThingHitsGoodThing(x, y, MV_NO_MOVING);
7373 void TestIfBadThingRunsIntoHero(int x, int y, int move_dir)
7375 TestIfBadThingHitsGoodThing(x, y, move_dir);
7378 void TestIfFriendTouchesBadThing(int x, int y)
7380 TestIfGoodThingHitsBadThing(x, y, MV_NO_MOVING);
7383 void TestIfBadThingTouchesFriend(int x, int y)
7385 TestIfBadThingHitsGoodThing(x, y, MV_NO_MOVING);
7388 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
7390 int i, kill_x = bad_x, kill_y = bad_y;
7391 static int xy[4][2] =
7403 x = bad_x + xy[i][0];
7404 y = bad_y + xy[i][1];
7405 if (!IN_LEV_FIELD(x, y))
7408 element = Feld[x][y];
7409 if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
7410 element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
7418 if (kill_x != bad_x || kill_y != bad_y)
7422 void KillHero(struct PlayerInfo *player)
7424 int jx = player->jx, jy = player->jy;
7426 if (!player->active)
7429 /* remove accessible field at the player's position */
7430 Feld[jx][jy] = EL_EMPTY;
7432 /* deactivate shield (else Bang()/Explode() would not work right) */
7433 player->shield_normal_time_left = 0;
7434 player->shield_deadly_time_left = 0;
7440 static void KillHeroUnlessProtected(int x, int y)
7442 if (!PLAYER_PROTECTED(x, y))
7443 KillHero(PLAYERINFO(x, y));
7446 void BuryHero(struct PlayerInfo *player)
7448 int jx = player->jx, jy = player->jy;
7450 if (!player->active)
7454 PlaySoundLevelElementAction(jx, jy, player->element_nr, ACTION_DYING);
7456 PlaySoundLevel(jx, jy, SND_CLASS_PLAYER_DYING);
7458 PlaySoundLevel(jx, jy, SND_GAME_LOSING);
7460 player->GameOver = TRUE;
7464 void RemoveHero(struct PlayerInfo *player)
7466 int jx = player->jx, jy = player->jy;
7467 int i, found = FALSE;
7469 player->present = FALSE;
7470 player->active = FALSE;
7472 if (!ExplodeField[jx][jy])
7473 StorePlayer[jx][jy] = 0;
7475 for (i=0; i<MAX_PLAYERS; i++)
7476 if (stored_player[i].active)
7480 AllPlayersGone = TRUE;
7487 =============================================================================
7488 checkDiagonalPushing()
7489 -----------------------------------------------------------------------------
7490 check if diagonal input device direction results in pushing of object
7491 (by checking if the alternative direction is walkable, diggable, ...)
7492 =============================================================================
7495 static boolean checkDiagonalPushing(struct PlayerInfo *player,
7496 int x, int y, int real_dx, int real_dy)
7498 int jx, jy, dx, dy, xx, yy;
7500 if (real_dx == 0 || real_dy == 0) /* no diagonal direction => push */
7503 /* diagonal direction: check alternative direction */
7508 xx = jx + (dx == 0 ? real_dx : 0);
7509 yy = jy + (dy == 0 ? real_dy : 0);
7511 return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
7515 =============================================================================
7517 -----------------------------------------------------------------------------
7518 x, y: field next to player (non-diagonal) to try to dig to
7519 real_dx, real_dy: direction as read from input device (can be diagonal)
7520 =============================================================================
7523 int DigField(struct PlayerInfo *player,
7524 int x, int y, int real_dx, int real_dy, int mode)
7526 static int change_sides[4] =
7528 CH_SIDE_RIGHT, /* moving left */
7529 CH_SIDE_LEFT, /* moving right */
7530 CH_SIDE_BOTTOM, /* moving up */
7531 CH_SIDE_TOP, /* moving down */
7533 boolean use_spring_bug = (game.engine_version < VERSION_IDENT(2,2,0,0));
7534 int jx = player->jx, jy = player->jy;
7535 int dx = x - jx, dy = y - jy;
7536 int nextx = x + dx, nexty = y + dy;
7537 int move_direction = (dx == -1 ? MV_LEFT :
7538 dx == +1 ? MV_RIGHT :
7540 dy == +1 ? MV_DOWN : MV_NO_MOVING);
7541 int dig_side = change_sides[MV_DIR_BIT(move_direction)];
7544 if (player->MovPos == 0)
7546 player->is_digging = FALSE;
7547 player->is_collecting = FALSE;
7550 if (player->MovPos == 0) /* last pushing move finished */
7551 player->is_pushing = FALSE;
7553 if (mode == DF_NO_PUSH) /* player just stopped pushing */
7555 player->is_switching = FALSE;
7556 player->push_delay = 0;
7558 return MF_NO_ACTION;
7561 if (IS_MOVING(x, y) || IS_PLAYER(x, y))
7562 return MF_NO_ACTION;
7565 if (IS_TUBE(Feld[jx][jy]) || IS_TUBE(Back[jx][jy]))
7567 if (IS_TUBE(Feld[jx][jy]) ||
7568 (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0)))
7572 int tube_element = (IS_TUBE(Feld[jx][jy]) ? Feld[jx][jy] : Back[jx][jy]);
7573 int tube_leave_directions[][2] =
7575 { EL_TUBE_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
7576 { EL_TUBE_VERTICAL, MV_UP | MV_DOWN },
7577 { EL_TUBE_HORIZONTAL, MV_LEFT | MV_RIGHT },
7578 { EL_TUBE_VERTICAL_LEFT, MV_LEFT | MV_UP | MV_DOWN },
7579 { EL_TUBE_VERTICAL_RIGHT, MV_RIGHT | MV_UP | MV_DOWN },
7580 { EL_TUBE_HORIZONTAL_UP, MV_LEFT | MV_RIGHT | MV_UP },
7581 { EL_TUBE_HORIZONTAL_DOWN, MV_LEFT | MV_RIGHT | MV_DOWN },
7582 { EL_TUBE_LEFT_UP, MV_LEFT | MV_UP },
7583 { EL_TUBE_LEFT_DOWN, MV_LEFT | MV_DOWN },
7584 { EL_TUBE_RIGHT_UP, MV_RIGHT | MV_UP },
7585 { EL_TUBE_RIGHT_DOWN, MV_RIGHT | MV_DOWN },
7586 { -1, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN }
7589 while (tube_leave_directions[i][0] != tube_element)
7592 if (tube_leave_directions[i][0] == -1) /* should not happen */
7596 if (!(tube_leave_directions[i][1] & move_direction))
7597 return MF_NO_ACTION; /* tube has no opening in this direction */
7600 element = Feld[x][y];
7602 if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
7603 game.engine_version >= VERSION_IDENT(2,2,0,0))
7604 return MF_NO_ACTION;
7608 case EL_SP_PORT_LEFT:
7609 case EL_SP_PORT_RIGHT:
7611 case EL_SP_PORT_DOWN:
7612 case EL_SP_PORT_HORIZONTAL:
7613 case EL_SP_PORT_VERTICAL:
7614 case EL_SP_PORT_ANY:
7615 case EL_SP_GRAVITY_PORT_LEFT:
7616 case EL_SP_GRAVITY_PORT_RIGHT:
7617 case EL_SP_GRAVITY_PORT_UP:
7618 case EL_SP_GRAVITY_PORT_DOWN:
7620 element != EL_SP_PORT_LEFT &&
7621 element != EL_SP_GRAVITY_PORT_LEFT &&
7622 element != EL_SP_PORT_HORIZONTAL &&
7623 element != EL_SP_PORT_ANY) ||
7625 element != EL_SP_PORT_RIGHT &&
7626 element != EL_SP_GRAVITY_PORT_RIGHT &&
7627 element != EL_SP_PORT_HORIZONTAL &&
7628 element != EL_SP_PORT_ANY) ||
7630 element != EL_SP_PORT_UP &&
7631 element != EL_SP_GRAVITY_PORT_UP &&
7632 element != EL_SP_PORT_VERTICAL &&
7633 element != EL_SP_PORT_ANY) ||
7635 element != EL_SP_PORT_DOWN &&
7636 element != EL_SP_GRAVITY_PORT_DOWN &&
7637 element != EL_SP_PORT_VERTICAL &&
7638 element != EL_SP_PORT_ANY) ||
7639 !IN_LEV_FIELD(nextx, nexty) ||
7640 !IS_FREE(nextx, nexty))
7641 return MF_NO_ACTION;
7643 if (element == EL_SP_GRAVITY_PORT_LEFT ||
7644 element == EL_SP_GRAVITY_PORT_RIGHT ||
7645 element == EL_SP_GRAVITY_PORT_UP ||
7646 element == EL_SP_GRAVITY_PORT_DOWN)
7647 game.gravity = !game.gravity;
7649 /* automatically move to the next field with double speed */
7650 player->programmed_action = move_direction;
7651 DOUBLE_PLAYER_SPEED(player);
7653 PlaySoundLevel(x, y, SND_CLASS_SP_PORT_PASSING);
7657 case EL_TUBE_VERTICAL:
7658 case EL_TUBE_HORIZONTAL:
7659 case EL_TUBE_VERTICAL_LEFT:
7660 case EL_TUBE_VERTICAL_RIGHT:
7661 case EL_TUBE_HORIZONTAL_UP:
7662 case EL_TUBE_HORIZONTAL_DOWN:
7663 case EL_TUBE_LEFT_UP:
7664 case EL_TUBE_LEFT_DOWN:
7665 case EL_TUBE_RIGHT_UP:
7666 case EL_TUBE_RIGHT_DOWN:
7669 int tube_enter_directions[][2] =
7671 { EL_TUBE_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
7672 { EL_TUBE_VERTICAL, MV_UP | MV_DOWN },
7673 { EL_TUBE_HORIZONTAL, MV_LEFT | MV_RIGHT },
7674 { EL_TUBE_VERTICAL_LEFT, MV_RIGHT | MV_UP | MV_DOWN },
7675 { EL_TUBE_VERTICAL_RIGHT, MV_LEFT | MV_UP | MV_DOWN },
7676 { EL_TUBE_HORIZONTAL_UP, MV_LEFT | MV_RIGHT | MV_DOWN },
7677 { EL_TUBE_HORIZONTAL_DOWN, MV_LEFT | MV_RIGHT | MV_UP },
7678 { EL_TUBE_LEFT_UP, MV_RIGHT | MV_DOWN },
7679 { EL_TUBE_LEFT_DOWN, MV_RIGHT | MV_UP },
7680 { EL_TUBE_RIGHT_UP, MV_LEFT | MV_DOWN },
7681 { EL_TUBE_RIGHT_DOWN, MV_LEFT | MV_UP },
7682 { -1, MV_NO_MOVING }
7685 while (tube_enter_directions[i][0] != element)
7688 if (tube_enter_directions[i][0] == -1) /* should not happen */
7692 if (!(tube_enter_directions[i][1] & move_direction))
7693 return MF_NO_ACTION; /* tube has no opening in this direction */
7695 PlaySoundLevel(x, y, SND_CLASS_TUBE_WALKING);
7701 if (IS_WALKABLE(element))
7703 int sound_action = ACTION_WALKING;
7705 if (element >= EL_GATE_1 && element <= EL_GATE_4)
7707 if (!player->key[element - EL_GATE_1])
7708 return MF_NO_ACTION;
7710 else if (element >= EL_GATE_1_GRAY && element <= EL_GATE_4_GRAY)
7712 if (!player->key[element - EL_GATE_1_GRAY])
7713 return MF_NO_ACTION;
7715 else if (element == EL_EXIT_OPEN ||
7716 element == EL_SP_EXIT_OPEN ||
7717 element == EL_SP_EXIT_OPENING)
7719 sound_action = ACTION_PASSING; /* player is passing exit */
7721 else if (element == EL_EMPTY)
7723 sound_action = ACTION_MOVING; /* nothing to walk on */
7726 /* play sound from background or player, whatever is available */
7727 if (element_info[element].sound[sound_action] != SND_UNDEFINED)
7728 PlaySoundLevelElementAction(x, y, element, sound_action);
7730 PlaySoundLevelElementAction(x, y, player->element_nr, sound_action);
7734 else if (IS_PASSABLE(element))
7736 if (!IN_LEV_FIELD(nextx, nexty) || !IS_FREE(nextx, nexty))
7737 return MF_NO_ACTION;
7740 if (CAN_MOVE(element)) /* only fixed elements can be passed! */
7741 return MF_NO_ACTION;
7744 if (element >= EL_EM_GATE_1 && element <= EL_EM_GATE_4)
7746 if (!player->key[element - EL_EM_GATE_1])
7747 return MF_NO_ACTION;
7749 else if (element >= EL_EM_GATE_1_GRAY && element <= EL_EM_GATE_4_GRAY)
7751 if (!player->key[element - EL_EM_GATE_1_GRAY])
7752 return MF_NO_ACTION;
7755 /* automatically move to the next field with double speed */
7756 player->programmed_action = move_direction;
7757 DOUBLE_PLAYER_SPEED(player);
7759 PlaySoundLevelAction(x, y, ACTION_PASSING);
7763 else if (IS_DIGGABLE(element))
7767 if (mode != DF_SNAP)
7770 GfxElement[x][y] = GFX_ELEMENT(element);
7773 (GFX_CRUMBLED(element) ? EL_SAND : GFX_ELEMENT(element));
7775 player->is_digging = TRUE;
7778 PlaySoundLevelElementAction(x, y, element, ACTION_DIGGING);
7780 CheckTriggeredElementChange(x, y, element, CE_OTHER_GETS_DIGGED);
7783 if (mode == DF_SNAP)
7784 TestIfElementTouchesCustomElement(x, y); /* for empty space */
7789 else if (IS_COLLECTIBLE(element))
7793 if (mode != DF_SNAP)
7795 GfxElement[x][y] = element;
7796 player->is_collecting = TRUE;
7799 if (element == EL_SPEED_PILL)
7800 player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
7801 else if (element == EL_EXTRA_TIME && level.time > 0)
7804 DrawText(DX_TIME, DY_TIME, int2str(TimeLeft, 3), FONT_TEXT_2);
7806 else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
7808 player->shield_normal_time_left += 10;
7809 if (element == EL_SHIELD_DEADLY)
7810 player->shield_deadly_time_left += 10;
7812 else if (element == EL_DYNAMITE || element == EL_SP_DISK_RED)
7814 if (player->inventory_size < MAX_INVENTORY_SIZE)
7815 player->inventory_element[player->inventory_size++] = element;
7817 DrawText(DX_DYNAMITE, DY_DYNAMITE,
7818 int2str(local_player->inventory_size, 3), FONT_TEXT_2);
7820 else if (element == EL_DYNABOMB_INCREASE_NUMBER)
7822 player->dynabomb_count++;
7823 player->dynabombs_left++;
7825 else if (element == EL_DYNABOMB_INCREASE_SIZE)
7827 player->dynabomb_size++;
7829 else if (element == EL_DYNABOMB_INCREASE_POWER)
7831 player->dynabomb_xl = TRUE;
7833 else if ((element >= EL_KEY_1 && element <= EL_KEY_4) ||
7834 (element >= EL_EM_KEY_1 && element <= EL_EM_KEY_4))
7836 int key_nr = (element >= EL_KEY_1 && element <= EL_KEY_4 ?
7837 element - EL_KEY_1 : element - EL_EM_KEY_1);
7839 player->key[key_nr] = TRUE;
7841 DrawMiniGraphicExt(drawto, DX_KEYS + key_nr * MINI_TILEX, DY_KEYS,
7842 el2edimg(EL_KEY_1 + key_nr));
7843 redraw_mask |= REDRAW_DOOR_1;
7845 else if (IS_ENVELOPE(element))
7848 player->show_envelope = element;
7850 ShowEnvelope(element - EL_ENVELOPE_1);
7853 else if (IS_DROPPABLE(element)) /* can be collected and dropped */
7857 for (i=0; i < element_info[element].collect_count; i++)
7858 if (player->inventory_size < MAX_INVENTORY_SIZE)
7859 player->inventory_element[player->inventory_size++] = element;
7861 DrawText(DX_DYNAMITE, DY_DYNAMITE,
7862 int2str(local_player->inventory_size, 3), FONT_TEXT_2);
7864 else if (element_info[element].collect_count > 0)
7866 local_player->gems_still_needed -=
7867 element_info[element].collect_count;
7868 if (local_player->gems_still_needed < 0)
7869 local_player->gems_still_needed = 0;
7871 DrawText(DX_EMERALDS, DY_EMERALDS,
7872 int2str(local_player->gems_still_needed, 3), FONT_TEXT_2);
7875 RaiseScoreElement(element);
7876 PlaySoundLevelElementAction(x, y, element, ACTION_COLLECTING);
7878 CheckTriggeredElementChange(x, y, element, CE_OTHER_GETS_COLLECTED);
7881 if (mode == DF_SNAP)
7882 TestIfElementTouchesCustomElement(x, y); /* for empty space */
7887 else if (IS_PUSHABLE(element))
7889 if (mode == DF_SNAP && element != EL_BD_ROCK)
7890 return MF_NO_ACTION;
7892 if (CAN_FALL(element) && dy)
7893 return MF_NO_ACTION;
7895 if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
7896 !(element == EL_SPRING && use_spring_bug))
7897 return MF_NO_ACTION;
7900 if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
7901 ((move_direction & MV_VERTICAL &&
7902 ((element_info[element].move_pattern & MV_LEFT &&
7903 IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
7904 (element_info[element].move_pattern & MV_RIGHT &&
7905 IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
7906 (move_direction & MV_HORIZONTAL &&
7907 ((element_info[element].move_pattern & MV_UP &&
7908 IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
7909 (element_info[element].move_pattern & MV_DOWN &&
7910 IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
7911 return MF_NO_ACTION;
7915 /* do not push elements already moving away faster than player */
7916 if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
7917 ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
7918 return MF_NO_ACTION;
7920 if (element == EL_SPRING && MovDir[x][y] != MV_NO_MOVING)
7921 return MF_NO_ACTION;
7925 if (game.engine_version >= VERSION_IDENT(3,0,7,1))
7927 if (player->push_delay_value == -1)
7928 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
7930 else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
7932 if (!player->is_pushing)
7933 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
7937 if (game.engine_version >= VERSION_IDENT(2,2,0,7) &&
7938 (game.engine_version < VERSION_IDENT(3,0,7,1) ||
7939 !player_is_pushing))
7940 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
7943 if (!player->is_pushing &&
7944 game.engine_version >= VERSION_IDENT(2,2,0,7))
7945 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
7949 printf("::: push delay: %ld [%d, %d] [%d]\n",
7950 player->push_delay_value, FrameCounter, game.engine_version,
7951 player->is_pushing);
7954 player->is_pushing = TRUE;
7956 if (!(IN_LEV_FIELD(nextx, nexty) &&
7957 (IS_FREE(nextx, nexty) ||
7958 (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY &&
7959 IS_SB_ELEMENT(element)))))
7960 return MF_NO_ACTION;
7962 if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
7963 return MF_NO_ACTION;
7965 if (player->push_delay == 0) /* new pushing; restart delay */
7966 player->push_delay = FrameCounter;
7968 if (!FrameReached(&player->push_delay, player->push_delay_value) &&
7969 !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
7970 element != EL_SPRING && element != EL_BALLOON)
7972 /* make sure that there is no move delay before next try to push */
7973 if (game.engine_version >= VERSION_IDENT(3,0,7,1))
7974 player->move_delay = INITIAL_MOVE_DELAY_OFF;
7976 return MF_NO_ACTION;
7980 printf("::: NOW PUSHING... [%d]\n", FrameCounter);
7983 if (IS_SB_ELEMENT(element))
7985 if (element == EL_SOKOBAN_FIELD_FULL)
7987 Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
7988 local_player->sokobanfields_still_needed++;
7991 if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
7993 Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
7994 local_player->sokobanfields_still_needed--;
7997 Feld[x][y] = EL_SOKOBAN_OBJECT;
7999 if (Back[x][y] == Back[nextx][nexty])
8000 PlaySoundLevelAction(x, y, ACTION_PUSHING);
8001 else if (Back[x][y] != 0)
8002 PlaySoundLevelElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
8005 PlaySoundLevelElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
8008 if (local_player->sokobanfields_still_needed == 0 &&
8009 game.emulation == EMU_SOKOBAN)
8011 player->LevelSolved = player->GameOver = TRUE;
8012 PlaySoundLevel(x, y, SND_GAME_SOKOBAN_SOLVING);
8016 PlaySoundLevelElementAction(x, y, element, ACTION_PUSHING);
8018 InitMovingField(x, y, move_direction);
8019 GfxAction[x][y] = ACTION_PUSHING;
8021 if (mode == DF_SNAP)
8022 ContinueMoving(x, y);
8024 MovPos[x][y] = (dx != 0 ? dx : dy);
8026 Pushed[x][y] = TRUE;
8027 Pushed[nextx][nexty] = TRUE;
8029 if (game.engine_version < VERSION_IDENT(2,2,0,7))
8030 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
8032 player->push_delay_value = -1; /* get new value later */
8034 CheckTriggeredElementSideChange(x, y, element, dig_side,
8035 CE_OTHER_GETS_PUSHED);
8036 CheckElementSideChange(x, y, element, dig_side,
8037 CE_PUSHED_BY_PLAYER, -1);
8041 else if (IS_SWITCHABLE(element))
8043 if (PLAYER_SWITCHING(player, x, y))
8046 player->is_switching = TRUE;
8047 player->switch_x = x;
8048 player->switch_y = y;
8050 PlaySoundLevelElementAction(x, y, element, ACTION_ACTIVATING);
8052 if (element == EL_ROBOT_WHEEL)
8054 Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
8058 DrawLevelField(x, y);
8060 else if (element == EL_SP_TERMINAL)
8064 for (yy=0; yy < lev_fieldy; yy++) for (xx=0; xx < lev_fieldx; xx++)
8066 if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
8068 else if (Feld[xx][yy] == EL_SP_TERMINAL)
8069 Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
8072 else if (IS_BELT_SWITCH(element))
8074 ToggleBeltSwitch(x, y);
8076 else if (element == EL_SWITCHGATE_SWITCH_UP ||
8077 element == EL_SWITCHGATE_SWITCH_DOWN)
8079 ToggleSwitchgateSwitch(x, y);
8081 else if (element == EL_LIGHT_SWITCH ||
8082 element == EL_LIGHT_SWITCH_ACTIVE)
8084 ToggleLightSwitch(x, y);
8087 PlaySoundLevel(x, y, element == EL_LIGHT_SWITCH ?
8088 SND_LIGHT_SWITCH_ACTIVATING :
8089 SND_LIGHT_SWITCH_DEACTIVATING);
8092 else if (element == EL_TIMEGATE_SWITCH)
8094 ActivateTimegateSwitch(x, y);
8096 else if (element == EL_BALLOON_SWITCH_LEFT ||
8097 element == EL_BALLOON_SWITCH_RIGHT ||
8098 element == EL_BALLOON_SWITCH_UP ||
8099 element == EL_BALLOON_SWITCH_DOWN ||
8100 element == EL_BALLOON_SWITCH_ANY)
8102 if (element == EL_BALLOON_SWITCH_ANY)
8103 game.balloon_dir = move_direction;
8105 game.balloon_dir = (element == EL_BALLOON_SWITCH_LEFT ? MV_LEFT :
8106 element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
8107 element == EL_BALLOON_SWITCH_UP ? MV_UP :
8108 element == EL_BALLOON_SWITCH_DOWN ? MV_DOWN :
8111 else if (element == EL_LAMP)
8113 Feld[x][y] = EL_LAMP_ACTIVE;
8114 local_player->lights_still_needed--;
8116 DrawLevelField(x, y);
8118 else if (element == EL_TIME_ORB_FULL)
8120 Feld[x][y] = EL_TIME_ORB_EMPTY;
8122 DrawText(DX_TIME, DY_TIME, int2str(TimeLeft, 3), FONT_TEXT_2);
8124 DrawLevelField(x, y);
8127 PlaySoundStereo(SND_TIME_ORB_FULL_COLLECTING, SOUND_MIDDLE);
8135 if (!PLAYER_SWITCHING(player, x, y))
8137 player->is_switching = TRUE;
8138 player->switch_x = x;
8139 player->switch_y = y;
8141 CheckTriggeredElementSideChange(x, y, element, dig_side,
8142 CE_OTHER_IS_SWITCHING);
8143 CheckElementSideChange(x, y, element, dig_side, CE_SWITCHED, -1);
8146 CheckTriggeredElementSideChange(x, y, element, dig_side,
8147 CE_OTHER_GETS_PRESSED);
8148 CheckElementSideChange(x, y, element, dig_side,
8149 CE_PRESSED_BY_PLAYER, -1);
8152 return MF_NO_ACTION;
8155 player->push_delay = 0;
8157 if (Feld[x][y] != element) /* really digged/collected something */
8158 player->is_collecting = !player->is_digging;
8163 boolean SnapField(struct PlayerInfo *player, int dx, int dy)
8165 int jx = player->jx, jy = player->jy;
8166 int x = jx + dx, y = jy + dy;
8167 int snap_direction = (dx == -1 ? MV_LEFT :
8168 dx == +1 ? MV_RIGHT :
8170 dy == +1 ? MV_DOWN : MV_NO_MOVING);
8172 if (player->MovPos && game.engine_version >= VERSION_IDENT(2,2,0,0))
8175 if (!player->active || !IN_LEV_FIELD(x, y))
8183 if (player->MovPos == 0)
8184 player->is_pushing = FALSE;
8186 player->is_snapping = FALSE;
8188 if (player->MovPos == 0)
8190 player->is_moving = FALSE;
8191 player->is_digging = FALSE;
8192 player->is_collecting = FALSE;
8198 if (player->is_snapping)
8201 player->MovDir = snap_direction;
8203 player->is_moving = FALSE;
8204 player->is_digging = FALSE;
8205 player->is_collecting = FALSE;
8207 if (DigField(player, x, y, 0, 0, DF_SNAP) == MF_NO_ACTION)
8210 player->is_snapping = TRUE;
8212 player->is_moving = FALSE;
8213 player->is_digging = FALSE;
8214 player->is_collecting = FALSE;
8216 DrawLevelField(x, y);
8222 boolean DropElement(struct PlayerInfo *player)
8224 int jx = player->jx, jy = player->jy;
8227 if (!player->active || player->MovPos)
8230 old_element = Feld[jx][jy];
8232 /* check if player has anything that can be dropped */
8233 if (player->inventory_size == 0 && player->dynabombs_left == 0)
8236 /* check if anything can be dropped at the current position */
8237 if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
8240 /* collected custom elements can only be dropped on empty fields */
8241 if (player->inventory_size > 0 &&
8242 IS_CUSTOM_ELEMENT(player->inventory_element[player->inventory_size - 1])
8243 && old_element != EL_EMPTY)
8246 if (old_element != EL_EMPTY)
8247 Back[jx][jy] = old_element; /* store old element on this field */
8249 MovDelay[jx][jy] = 96;
8251 ResetGfxAnimation(jx, jy);
8252 ResetRandomAnimationValue(jx, jy);
8254 if (player->inventory_size > 0)
8256 int new_element = player->inventory_element[--player->inventory_size];
8258 Feld[jx][jy] = (new_element == EL_DYNAMITE ? EL_DYNAMITE_ACTIVE :
8259 new_element == EL_SP_DISK_RED ? EL_SP_DISK_RED_ACTIVE :
8262 DrawText(DX_DYNAMITE, DY_DYNAMITE,
8263 int2str(local_player->inventory_size, 3), FONT_TEXT_2);
8265 if (IN_SCR_FIELD(SCREENX(jx), SCREENY(jy)))
8266 DrawGraphicThruMask(SCREENX(jx), SCREENY(jy), el2img(Feld[jx][jy]), 0);
8268 PlaySoundLevelAction(jx, jy, ACTION_DROPPING);
8270 CheckTriggeredElementChange(jx, jy, new_element, CE_OTHER_GETS_DROPPED);
8271 CheckElementChange(jx, jy, new_element, CE_DROPPED_BY_PLAYER);
8273 TestIfElementTouchesCustomElement(jx, jy);
8275 else /* player is dropping a dyna bomb */
8277 player->dynabombs_left--;
8280 EL_DYNABOMB_PLAYER_1_ACTIVE + (player->element_nr - EL_PLAYER_1);
8282 if (IN_SCR_FIELD(SCREENX(jx), SCREENY(jy)))
8283 DrawGraphicThruMask(SCREENX(jx), SCREENY(jy), el2img(Feld[jx][jy]), 0);
8285 PlaySoundLevelAction(jx, jy, ACTION_DROPPING);
8291 /* ------------------------------------------------------------------------- */
8292 /* game sound playing functions */
8293 /* ------------------------------------------------------------------------- */
8295 static int *loop_sound_frame = NULL;
8296 static int *loop_sound_volume = NULL;
8298 void InitPlaySoundLevel()
8300 int num_sounds = getSoundListSize();
8302 if (loop_sound_frame != NULL)
8303 free(loop_sound_frame);
8305 if (loop_sound_volume != NULL)
8306 free(loop_sound_volume);
8308 loop_sound_frame = checked_calloc(num_sounds * sizeof(int));
8309 loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
8312 static void PlaySoundLevel(int x, int y, int nr)
8314 int sx = SCREENX(x), sy = SCREENY(y);
8315 int volume, stereo_position;
8316 int max_distance = 8;
8317 int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
8319 if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
8320 (!setup.sound_loops && IS_LOOP_SOUND(nr)))
8323 if (!IN_LEV_FIELD(x, y) ||
8324 sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
8325 sy < -max_distance || sy >= SCR_FIELDY + max_distance)
8328 volume = SOUND_MAX_VOLUME;
8330 if (!IN_SCR_FIELD(sx, sy))
8332 int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
8333 int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
8335 volume -= volume * (dx > dy ? dx : dy) / max_distance;
8338 stereo_position = (SOUND_MAX_LEFT +
8339 (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
8340 (SCR_FIELDX + 2 * max_distance));
8342 if (IS_LOOP_SOUND(nr))
8344 /* This assures that quieter loop sounds do not overwrite louder ones,
8345 while restarting sound volume comparison with each new game frame. */
8347 if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
8350 loop_sound_volume[nr] = volume;
8351 loop_sound_frame[nr] = FrameCounter;
8354 PlaySoundExt(nr, volume, stereo_position, type);
8357 static void PlaySoundLevelNearest(int x, int y, int sound_action)
8359 PlaySoundLevel(x < LEVELX(BX1) ? LEVELX(BX1) :
8360 x > LEVELX(BX2) ? LEVELX(BX2) : x,
8361 y < LEVELY(BY1) ? LEVELY(BY1) :
8362 y > LEVELY(BY2) ? LEVELY(BY2) : y,
8366 static void PlaySoundLevelAction(int x, int y, int action)
8368 PlaySoundLevelElementAction(x, y, Feld[x][y], action);
8371 static void PlaySoundLevelElementAction(int x, int y, int element, int action)
8373 int sound_effect = element_info[element].sound[action];
8375 if (sound_effect != SND_UNDEFINED)
8376 PlaySoundLevel(x, y, sound_effect);
8379 static void PlaySoundLevelActionIfLoop(int x, int y, int action)
8381 int sound_effect = element_info[Feld[x][y]].sound[action];
8383 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
8384 PlaySoundLevel(x, y, sound_effect);
8387 static void StopSoundLevelActionIfLoop(int x, int y, int action)
8389 int sound_effect = element_info[Feld[x][y]].sound[action];
8391 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
8392 StopSoundExt(sound_effect, SND_CTRL_STOP_SOUND);
8395 void RaiseScore(int value)
8397 local_player->score += value;
8398 DrawText(DX_SCORE, DY_SCORE, int2str(local_player->score, 5), FONT_TEXT_2);
8401 void RaiseScoreElement(int element)
8407 case EL_EMERALD_YELLOW:
8408 case EL_EMERALD_RED:
8409 case EL_EMERALD_PURPLE:
8410 case EL_SP_INFOTRON:
8411 RaiseScore(level.score[SC_EMERALD]);
8414 RaiseScore(level.score[SC_DIAMOND]);
8417 RaiseScore(level.score[SC_CRYSTAL]);
8420 RaiseScore(level.score[SC_PEARL]);
8423 case EL_BD_BUTTERFLY:
8424 case EL_SP_ELECTRON:
8425 RaiseScore(level.score[SC_BUG]);
8429 case EL_SP_SNIKSNAK:
8430 RaiseScore(level.score[SC_SPACESHIP]);
8433 case EL_DARK_YAMYAM:
8434 RaiseScore(level.score[SC_YAMYAM]);
8437 RaiseScore(level.score[SC_ROBOT]);
8440 RaiseScore(level.score[SC_PACMAN]);
8443 RaiseScore(level.score[SC_NUT]);
8446 case EL_SP_DISK_RED:
8447 case EL_DYNABOMB_INCREASE_NUMBER:
8448 case EL_DYNABOMB_INCREASE_SIZE:
8449 case EL_DYNABOMB_INCREASE_POWER:
8450 RaiseScore(level.score[SC_DYNAMITE]);
8452 case EL_SHIELD_NORMAL:
8453 case EL_SHIELD_DEADLY:
8454 RaiseScore(level.score[SC_SHIELD]);
8457 RaiseScore(level.score[SC_TIME_BONUS]);
8463 RaiseScore(level.score[SC_KEY]);
8466 RaiseScore(element_info[element].collect_score);
8471 void RequestQuitGame(boolean ask_if_really_quit)
8473 if (AllPlayersGone ||
8474 !ask_if_really_quit ||
8475 level_editor_test_game ||
8476 Request("Do you really want to quit the game ?",
8477 REQ_ASK | REQ_STAY_CLOSED))
8479 #if defined(PLATFORM_UNIX)
8480 if (options.network)
8481 SendToServer_StopPlaying();
8485 game_status = GAME_MODE_MAIN;
8491 OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
8496 /* ---------- new game button stuff ---------------------------------------- */
8498 /* graphic position values for game buttons */
8499 #define GAME_BUTTON_XSIZE 30
8500 #define GAME_BUTTON_YSIZE 30
8501 #define GAME_BUTTON_XPOS 5
8502 #define GAME_BUTTON_YPOS 215
8503 #define SOUND_BUTTON_XPOS 5
8504 #define SOUND_BUTTON_YPOS (GAME_BUTTON_YPOS + GAME_BUTTON_YSIZE)
8506 #define GAME_BUTTON_STOP_XPOS (GAME_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
8507 #define GAME_BUTTON_PAUSE_XPOS (GAME_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
8508 #define GAME_BUTTON_PLAY_XPOS (GAME_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
8509 #define SOUND_BUTTON_MUSIC_XPOS (SOUND_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
8510 #define SOUND_BUTTON_LOOPS_XPOS (SOUND_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
8511 #define SOUND_BUTTON_SIMPLE_XPOS (SOUND_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
8518 } gamebutton_info[NUM_GAME_BUTTONS] =
8521 GAME_BUTTON_STOP_XPOS, GAME_BUTTON_YPOS,
8526 GAME_BUTTON_PAUSE_XPOS, GAME_BUTTON_YPOS,
8531 GAME_BUTTON_PLAY_XPOS, GAME_BUTTON_YPOS,
8536 SOUND_BUTTON_MUSIC_XPOS, SOUND_BUTTON_YPOS,
8537 SOUND_CTRL_ID_MUSIC,
8538 "background music on/off"
8541 SOUND_BUTTON_LOOPS_XPOS, SOUND_BUTTON_YPOS,
8542 SOUND_CTRL_ID_LOOPS,
8543 "sound loops on/off"
8546 SOUND_BUTTON_SIMPLE_XPOS, SOUND_BUTTON_YPOS,
8547 SOUND_CTRL_ID_SIMPLE,
8548 "normal sounds on/off"
8552 void CreateGameButtons()
8556 for (i=0; i<NUM_GAME_BUTTONS; i++)
8558 Bitmap *gd_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
8559 struct GadgetInfo *gi;
8562 unsigned long event_mask;
8563 int gd_xoffset, gd_yoffset;
8564 int gd_x1, gd_x2, gd_y1, gd_y2;
8567 gd_xoffset = gamebutton_info[i].x;
8568 gd_yoffset = gamebutton_info[i].y;
8569 gd_x1 = DOOR_GFX_PAGEX4 + gd_xoffset;
8570 gd_x2 = DOOR_GFX_PAGEX3 + gd_xoffset;
8572 if (id == GAME_CTRL_ID_STOP ||
8573 id == GAME_CTRL_ID_PAUSE ||
8574 id == GAME_CTRL_ID_PLAY)
8576 button_type = GD_TYPE_NORMAL_BUTTON;
8578 event_mask = GD_EVENT_RELEASED;
8579 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
8580 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
8584 button_type = GD_TYPE_CHECK_BUTTON;
8586 ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
8587 (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
8588 (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
8589 event_mask = GD_EVENT_PRESSED;
8590 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset;
8591 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
8594 gi = CreateGadget(GDI_CUSTOM_ID, id,
8595 GDI_INFO_TEXT, gamebutton_info[i].infotext,
8596 GDI_X, DX + gd_xoffset,
8597 GDI_Y, DY + gd_yoffset,
8598 GDI_WIDTH, GAME_BUTTON_XSIZE,
8599 GDI_HEIGHT, GAME_BUTTON_YSIZE,
8600 GDI_TYPE, button_type,
8601 GDI_STATE, GD_BUTTON_UNPRESSED,
8602 GDI_CHECKED, checked,
8603 GDI_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y1,
8604 GDI_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y1,
8605 GDI_ALT_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y2,
8606 GDI_ALT_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y2,
8607 GDI_EVENT_MASK, event_mask,
8608 GDI_CALLBACK_ACTION, HandleGameButtons,
8612 Error(ERR_EXIT, "cannot create gadget");
8614 game_gadget[id] = gi;
8618 void FreeGameButtons()
8622 for (i=0; i<NUM_GAME_BUTTONS; i++)
8623 FreeGadget(game_gadget[i]);
8626 static void MapGameButtons()
8630 for (i=0; i<NUM_GAME_BUTTONS; i++)
8631 MapGadget(game_gadget[i]);
8634 void UnmapGameButtons()
8638 for (i=0; i<NUM_GAME_BUTTONS; i++)
8639 UnmapGadget(game_gadget[i]);
8642 static void HandleGameButtons(struct GadgetInfo *gi)
8644 int id = gi->custom_id;
8646 if (game_status != GAME_MODE_PLAYING)
8651 case GAME_CTRL_ID_STOP:
8652 RequestQuitGame(TRUE);
8655 case GAME_CTRL_ID_PAUSE:
8656 if (options.network)
8658 #if defined(PLATFORM_UNIX)
8660 SendToServer_ContinuePlaying();
8662 SendToServer_PausePlaying();
8666 TapeTogglePause(TAPE_TOGGLE_MANUAL);
8669 case GAME_CTRL_ID_PLAY:
8672 #if defined(PLATFORM_UNIX)
8673 if (options.network)
8674 SendToServer_ContinuePlaying();
8678 tape.pausing = FALSE;
8679 DrawVideoDisplay(VIDEO_STATE_PAUSE_OFF,0);
8684 case SOUND_CTRL_ID_MUSIC:
8685 if (setup.sound_music)
8687 setup.sound_music = FALSE;
8690 else if (audio.music_available)
8692 setup.sound = setup.sound_music = TRUE;
8694 SetAudioMode(setup.sound);
8695 PlayMusic(level_nr);
8699 case SOUND_CTRL_ID_LOOPS:
8700 if (setup.sound_loops)
8701 setup.sound_loops = FALSE;
8702 else if (audio.loops_available)
8704 setup.sound = setup.sound_loops = TRUE;
8705 SetAudioMode(setup.sound);
8709 case SOUND_CTRL_ID_SIMPLE:
8710 if (setup.sound_simple)
8711 setup.sound_simple = FALSE;
8712 else if (audio.sound_available)
8714 setup.sound = setup.sound_simple = TRUE;
8715 SetAudioMode(setup.sound);