1 /***********************************************************
2 * Rocks'n'Diamonds -- McDuffin Strikes Back! *
3 *----------------------------------------------------------*
4 * (c) 1995-2002 Artsoft Entertainment *
6 * Detmolder Strasse 189 *
9 * e-mail: info@artsoft.org *
10 *----------------------------------------------------------*
12 ***********************************************************/
14 #include "libgame/libgame.h"
24 /* this switch controls how rocks move horizontally */
25 #define OLD_GAME_BEHAVIOUR FALSE
27 /* EXPERIMENTAL STUFF */
28 #define USE_NEW_AMOEBA_CODE FALSE
35 /* for MovePlayer() */
36 #define MF_NO_ACTION 0
40 /* for ScrollPlayer() */
42 #define SCROLL_GO_ON 1
45 #define EX_PHASE_START 0
46 #define EX_NO_EXPLOSION 0
51 /* special positions in the game control window (relative to control window) */
54 #define XX_EMERALDS 29
55 #define YY_EMERALDS 54
56 #define XX_DYNAMITE 29
57 #define YY_DYNAMITE 89
65 /* special positions in the game control window (relative to main window) */
66 #define DX_LEVEL (DX + XX_LEVEL)
67 #define DY_LEVEL (DY + YY_LEVEL)
68 #define DX_EMERALDS (DX + XX_EMERALDS)
69 #define DY_EMERALDS (DY + YY_EMERALDS)
70 #define DX_DYNAMITE (DX + XX_DYNAMITE)
71 #define DY_DYNAMITE (DY + YY_DYNAMITE)
72 #define DX_KEYS (DX + XX_KEYS)
73 #define DY_KEYS (DY + YY_KEYS)
74 #define DX_SCORE (DX + XX_SCORE)
75 #define DY_SCORE (DY + YY_SCORE)
76 #define DX_TIME (DX + XX_TIME)
77 #define DY_TIME (DY + YY_TIME)
79 /* values for initial player move delay (initial delay counter value) */
80 #define INITIAL_MOVE_DELAY_OFF -1
81 #define INITIAL_MOVE_DELAY_ON 0
83 /* values for player movement speed (which is in fact a delay value) */
84 #define MOVE_DELAY_NORMAL_SPEED 8
85 #define MOVE_DELAY_HIGH_SPEED 4
87 #define DOUBLE_MOVE_DELAY(x) (x = (x <= MOVE_DELAY_HIGH_SPEED ? x * 2 : x))
88 #define HALVE_MOVE_DELAY(x) (x = (x >= MOVE_DELAY_HIGH_SPEED ? x / 2 : x))
89 #define DOUBLE_PLAYER_SPEED(p) (HALVE_MOVE_DELAY((p)->move_delay_value))
90 #define HALVE_PLAYER_SPEED(p) (DOUBLE_MOVE_DELAY((p)->move_delay_value))
92 /* values for other actions */
93 #define MOVE_STEPSIZE_NORMAL (TILEX / MOVE_DELAY_NORMAL_SPEED)
95 #define INIT_GFX_RANDOM() (SimpleRND(1000000))
97 #define GET_NEW_PUSH_DELAY(e) ( (element_info[e].push_delay_fixed) + \
98 RND(element_info[e].push_delay_random))
99 #define GET_NEW_MOVE_DELAY(e) ( (element_info[e].move_delay_fixed) + \
100 RND(element_info[e].move_delay_random))
101 #define GET_MAX_MOVE_DELAY(e) ( (element_info[e].move_delay_fixed) + \
102 (element_info[e].move_delay_random))
104 #define ELEMENT_CAN_ENTER_FIELD_GENERIC(e, x, y, condition) \
105 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
107 (DONT_COLLIDE_WITH(e) && \
108 IS_FREE_OR_PLAYER(x, y))))
110 #define ELEMENT_CAN_ENTER_FIELD_GENERIC_2(x, y, condition) \
111 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
114 #define ELEMENT_CAN_ENTER_FIELD(e, x, y) \
115 ELEMENT_CAN_ENTER_FIELD_GENERIC(e, x, y, 0)
117 #define ELEMENT_CAN_ENTER_FIELD_OR_ACID(e, x, y) \
118 ELEMENT_CAN_ENTER_FIELD_GENERIC(e, x, y, (Feld[x][y] == EL_ACID))
120 #define ELEMENT_CAN_ENTER_FIELD_OR_ACID_2(x, y) \
121 ELEMENT_CAN_ENTER_FIELD_GENERIC_2(x, y, (Feld[x][y] == EL_ACID))
123 #define ENEMY_CAN_ENTER_FIELD(x, y) (IN_LEV_FIELD(x, y) && IS_FREE(x, y))
125 #define YAMYAM_CAN_ENTER_FIELD(x, y) \
126 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) || \
127 Feld[x][y] == EL_DIAMOND))
129 #define DARK_YAMYAM_CAN_ENTER_FIELD(x, y) \
130 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) || \
131 IS_FOOD_DARK_YAMYAM(Feld[x][y])))
133 #define PACMAN_CAN_ENTER_FIELD(x, y) \
134 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) || \
135 IS_AMOEBOID(Feld[x][y])))
137 #define PIG_CAN_ENTER_FIELD(x, y) \
138 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
139 IS_FOOD_PIG(Feld[x][y])))
141 #define PENGUIN_CAN_ENTER_FIELD(x, y) \
142 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
143 IS_FOOD_PENGUIN(Feld[x][y]) || \
144 Feld[x][y] == EL_EXIT_OPEN || \
145 Feld[x][y] == EL_ACID))
147 #define MOLE_CAN_ENTER_FIELD(x, y, condition) \
148 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || (condition)))
150 #define IN_LEV_FIELD_AND_IS_FREE(x, y) (IN_LEV_FIELD(x, y) && IS_FREE(x, y))
151 #define IN_LEV_FIELD_AND_NOT_FREE(x, y) (IN_LEV_FIELD(x, y) && !IS_FREE(x, y))
153 /* game button identifiers */
154 #define GAME_CTRL_ID_STOP 0
155 #define GAME_CTRL_ID_PAUSE 1
156 #define GAME_CTRL_ID_PLAY 2
157 #define SOUND_CTRL_ID_MUSIC 3
158 #define SOUND_CTRL_ID_LOOPS 4
159 #define SOUND_CTRL_ID_SIMPLE 5
161 #define NUM_GAME_BUTTONS 6
164 /* forward declaration for internal use */
166 static boolean MovePlayerOneStep(struct PlayerInfo *, int, int, int, int);
167 static boolean MovePlayer(struct PlayerInfo *, int, int);
168 static void ScrollPlayer(struct PlayerInfo *, int);
169 static void ScrollScreen(struct PlayerInfo *, int);
171 static void InitBeltMovement(void);
172 static void CloseAllOpenTimegates(void);
173 static void CheckGravityMovement(struct PlayerInfo *);
174 static void KillHeroUnlessProtected(int, int);
176 static void TestIfPlayerTouchesCustomElement(int, int);
177 static void TestIfElementTouchesCustomElement(int, int);
179 static void ChangeElement(int, int, int);
180 static boolean CheckTriggeredElementSideChange(int, int, int, int, int);
181 static boolean CheckTriggeredElementChange(int, int, int, int);
182 static boolean CheckElementSideChange(int, int, int, int, int, int);
183 static boolean CheckElementChange(int, int, int, int);
185 static void PlaySoundLevel(int, int, int);
186 static void PlaySoundLevelNearest(int, int, int);
187 static void PlaySoundLevelAction(int, int, int);
188 static void PlaySoundLevelElementAction(int, int, int, int);
189 static void PlaySoundLevelActionIfLoop(int, int, int);
190 static void StopSoundLevelActionIfLoop(int, int, int);
192 static void MapGameButtons();
193 static void HandleGameButtons(struct GadgetInfo *);
195 static struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS];
198 /* ------------------------------------------------------------------------- */
199 /* definition of elements that automatically change to other elements after */
200 /* a specified time, eventually calling a function when changing */
201 /* ------------------------------------------------------------------------- */
203 /* forward declaration for changer functions */
204 static void InitBuggyBase(int x, int y);
205 static void WarnBuggyBase(int x, int y);
207 static void InitTrap(int x, int y);
208 static void ActivateTrap(int x, int y);
209 static void ChangeActiveTrap(int x, int y);
211 static void InitRobotWheel(int x, int y);
212 static void RunRobotWheel(int x, int y);
213 static void StopRobotWheel(int x, int y);
215 static void InitTimegateWheel(int x, int y);
216 static void RunTimegateWheel(int x, int y);
218 struct ChangingElementInfo
223 void (*pre_change_function)(int x, int y);
224 void (*change_function)(int x, int y);
225 void (*post_change_function)(int x, int y);
228 static struct ChangingElementInfo change_delay_list[] =
279 EL_SWITCHGATE_OPENING,
287 EL_SWITCHGATE_CLOSING,
288 EL_SWITCHGATE_CLOSED,
320 EL_ACID_SPLASH_RIGHT,
329 EL_SP_BUGGY_BASE_ACTIVATING,
336 EL_SP_BUGGY_BASE_ACTIVATING,
337 EL_SP_BUGGY_BASE_ACTIVE,
344 EL_SP_BUGGY_BASE_ACTIVE,
368 EL_ROBOT_WHEEL_ACTIVE,
376 EL_TIMEGATE_SWITCH_ACTIVE,
397 int push_delay_fixed, push_delay_random;
402 { EL_BALLOON, 0, 0 },
404 { EL_SOKOBAN_OBJECT, 2, 0 },
405 { EL_SOKOBAN_FIELD_FULL, 2, 0 },
406 { EL_SATELLITE, 2, 0 },
407 { EL_SP_DISK_YELLOW, 2, 0 },
409 { EL_UNDEFINED, 0, 0 },
417 move_stepsize_list[] =
419 { EL_AMOEBA_DROP, 2 },
420 { EL_AMOEBA_DROPPING, 2 },
421 { EL_QUICKSAND_FILLING, 1 },
422 { EL_QUICKSAND_EMPTYING, 1 },
423 { EL_MAGIC_WALL_FILLING, 2 },
424 { EL_BD_MAGIC_WALL_FILLING, 2 },
425 { EL_MAGIC_WALL_EMPTYING, 2 },
426 { EL_BD_MAGIC_WALL_EMPTYING, 2 },
436 collect_count_list[] =
439 { EL_BD_DIAMOND, 1 },
440 { EL_EMERALD_YELLOW, 1 },
441 { EL_EMERALD_RED, 1 },
442 { EL_EMERALD_PURPLE, 1 },
444 { EL_SP_INFOTRON, 1 },
451 static unsigned long trigger_events[MAX_NUM_ELEMENTS];
453 #define IS_AUTO_CHANGING(e) (element_info[e].change_events & \
454 CH_EVENT_BIT(CE_DELAY))
455 #define IS_JUST_CHANGING(x, y) (ChangeDelay[x][y] != 0)
456 #define IS_CHANGING(x, y) (IS_AUTO_CHANGING(Feld[x][y]) || \
457 IS_JUST_CHANGING(x, y))
459 #define CE_PAGE(e, ce) (element_info[e].event_page[ce])
462 void GetPlayerConfig()
464 if (!audio.sound_available)
465 setup.sound_simple = FALSE;
467 if (!audio.loops_available)
468 setup.sound_loops = FALSE;
470 if (!audio.music_available)
471 setup.sound_music = FALSE;
473 if (!video.fullscreen_available)
474 setup.fullscreen = FALSE;
476 setup.sound = (setup.sound_simple || setup.sound_loops || setup.sound_music);
478 SetAudioMode(setup.sound);
482 static int getBeltNrFromBeltElement(int element)
484 return (element < EL_CONVEYOR_BELT_2_LEFT ? 0 :
485 element < EL_CONVEYOR_BELT_3_LEFT ? 1 :
486 element < EL_CONVEYOR_BELT_4_LEFT ? 2 : 3);
489 static int getBeltNrFromBeltActiveElement(int element)
491 return (element < EL_CONVEYOR_BELT_2_LEFT_ACTIVE ? 0 :
492 element < EL_CONVEYOR_BELT_3_LEFT_ACTIVE ? 1 :
493 element < EL_CONVEYOR_BELT_4_LEFT_ACTIVE ? 2 : 3);
496 static int getBeltNrFromBeltSwitchElement(int element)
498 return (element < EL_CONVEYOR_BELT_2_SWITCH_LEFT ? 0 :
499 element < EL_CONVEYOR_BELT_3_SWITCH_LEFT ? 1 :
500 element < EL_CONVEYOR_BELT_4_SWITCH_LEFT ? 2 : 3);
503 static int getBeltDirNrFromBeltSwitchElement(int element)
505 static int belt_base_element[4] =
507 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
508 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
509 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
510 EL_CONVEYOR_BELT_4_SWITCH_LEFT
513 int belt_nr = getBeltNrFromBeltSwitchElement(element);
514 int belt_dir_nr = element - belt_base_element[belt_nr];
516 return (belt_dir_nr % 3);
519 static int getBeltDirFromBeltSwitchElement(int element)
521 static int belt_move_dir[3] =
528 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(element);
530 return belt_move_dir[belt_dir_nr];
533 static void InitPlayerField(int x, int y, int element, boolean init_game)
535 if (element == EL_SP_MURPHY)
539 if (stored_player[0].present)
541 Feld[x][y] = EL_SP_MURPHY_CLONE;
547 stored_player[0].use_murphy_graphic = TRUE;
550 Feld[x][y] = EL_PLAYER_1;
556 struct PlayerInfo *player = &stored_player[Feld[x][y] - EL_PLAYER_1];
557 int jx = player->jx, jy = player->jy;
559 player->present = TRUE;
561 if (!options.network || player->connected)
563 player->active = TRUE;
565 /* remove potentially duplicate players */
566 if (StorePlayer[jx][jy] == Feld[x][y])
567 StorePlayer[jx][jy] = 0;
569 StorePlayer[x][y] = Feld[x][y];
573 printf("Player %d activated.\n", player->element_nr);
574 printf("[Local player is %d and currently %s.]\n",
575 local_player->element_nr,
576 local_player->active ? "active" : "not active");
580 Feld[x][y] = EL_EMPTY;
581 player->jx = player->last_jx = x;
582 player->jy = player->last_jy = y;
586 static void InitField(int x, int y, boolean init_game)
588 int element = Feld[x][y];
597 InitPlayerField(x, y, element, init_game);
601 if (x < lev_fieldx-1 && Feld[x+1][y] == EL_ACID)
602 Feld[x][y] = EL_ACID_POOL_TOPLEFT;
603 else if (x > 0 && Feld[x-1][y] == EL_ACID)
604 Feld[x][y] = EL_ACID_POOL_TOPRIGHT;
605 else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPLEFT)
606 Feld[x][y] = EL_ACID_POOL_BOTTOMLEFT;
607 else if (y > 0 && Feld[x][y-1] == EL_ACID)
608 Feld[x][y] = EL_ACID_POOL_BOTTOM;
609 else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPRIGHT)
610 Feld[x][y] = EL_ACID_POOL_BOTTOMRIGHT;
618 case EL_SPACESHIP_RIGHT:
619 case EL_SPACESHIP_UP:
620 case EL_SPACESHIP_LEFT:
621 case EL_SPACESHIP_DOWN:
623 case EL_BD_BUTTERFLY_RIGHT:
624 case EL_BD_BUTTERFLY_UP:
625 case EL_BD_BUTTERFLY_LEFT:
626 case EL_BD_BUTTERFLY_DOWN:
627 case EL_BD_BUTTERFLY:
628 case EL_BD_FIREFLY_RIGHT:
629 case EL_BD_FIREFLY_UP:
630 case EL_BD_FIREFLY_LEFT:
631 case EL_BD_FIREFLY_DOWN:
633 case EL_PACMAN_RIGHT:
657 if (y == lev_fieldy - 1)
659 Feld[x][y] = EL_AMOEBA_GROWING;
660 Store[x][y] = EL_AMOEBA_WET;
664 case EL_DYNAMITE_ACTIVE:
669 local_player->lights_still_needed++;
672 case EL_SOKOBAN_FIELD_EMPTY:
673 local_player->sokobanfields_still_needed++;
677 local_player->friends_still_needed++;
682 GfxDir[x][y] = MovDir[x][y] = 1 << RND(4);
687 Feld[x][y] = EL_EMPTY;
692 case EL_EM_KEY_1_FILE:
693 Feld[x][y] = EL_EM_KEY_1;
695 case EL_EM_KEY_2_FILE:
696 Feld[x][y] = EL_EM_KEY_2;
698 case EL_EM_KEY_3_FILE:
699 Feld[x][y] = EL_EM_KEY_3;
701 case EL_EM_KEY_4_FILE:
702 Feld[x][y] = EL_EM_KEY_4;
706 case EL_CONVEYOR_BELT_1_SWITCH_LEFT:
707 case EL_CONVEYOR_BELT_1_SWITCH_MIDDLE:
708 case EL_CONVEYOR_BELT_1_SWITCH_RIGHT:
709 case EL_CONVEYOR_BELT_2_SWITCH_LEFT:
710 case EL_CONVEYOR_BELT_2_SWITCH_MIDDLE:
711 case EL_CONVEYOR_BELT_2_SWITCH_RIGHT:
712 case EL_CONVEYOR_BELT_3_SWITCH_LEFT:
713 case EL_CONVEYOR_BELT_3_SWITCH_MIDDLE:
714 case EL_CONVEYOR_BELT_3_SWITCH_RIGHT:
715 case EL_CONVEYOR_BELT_4_SWITCH_LEFT:
716 case EL_CONVEYOR_BELT_4_SWITCH_MIDDLE:
717 case EL_CONVEYOR_BELT_4_SWITCH_RIGHT:
720 int belt_nr = getBeltNrFromBeltSwitchElement(Feld[x][y]);
721 int belt_dir = getBeltDirFromBeltSwitchElement(Feld[x][y]);
722 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(Feld[x][y]);
724 if (game.belt_dir_nr[belt_nr] == 3) /* initial value */
726 game.belt_dir[belt_nr] = belt_dir;
727 game.belt_dir_nr[belt_nr] = belt_dir_nr;
729 else /* more than one switch -- set it like the first switch */
731 Feld[x][y] = Feld[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
736 case EL_SWITCHGATE_SWITCH_DOWN: /* always start with same switch pos */
738 Feld[x][y] = EL_SWITCHGATE_SWITCH_UP;
741 case EL_LIGHT_SWITCH_ACTIVE:
743 game.light_time_left = level.time_light * FRAMES_PER_SECOND;
747 if (IS_CUSTOM_ELEMENT(element) && CAN_MOVE(element))
753 void DrawGameDoorValues()
757 for (i=0; i<MAX_PLAYERS; i++)
759 if (stored_player[i].key[j])
760 DrawMiniGraphicExt(drawto, DX_KEYS + j * MINI_TILEX, DY_KEYS,
761 el2edimg(EL_KEY_1 + j));
763 DrawText(DX + XX_EMERALDS, DY + YY_EMERALDS,
764 int2str(local_player->gems_still_needed, 3), FONT_TEXT_2);
765 DrawText(DX + XX_DYNAMITE, DY + YY_DYNAMITE,
766 int2str(local_player->inventory_size, 3), FONT_TEXT_2);
767 DrawText(DX + XX_SCORE, DY + YY_SCORE,
768 int2str(local_player->score, 5), FONT_TEXT_2);
769 DrawText(DX + XX_TIME, DY + YY_TIME,
770 int2str(TimeLeft, 3), FONT_TEXT_2);
775 =============================================================================
777 -----------------------------------------------------------------------------
778 initialize game engine due to level / tape version number
779 =============================================================================
782 static void InitGameEngine()
786 /* set game engine from tape file when re-playing, else from level file */
787 game.engine_version = (tape.playing ? tape.engine_version :
790 /* dynamically adjust element properties according to game engine version */
791 InitElementPropertiesEngine(game.engine_version);
794 printf("level %d: level version == %06d\n", level_nr, level.game_version);
795 printf(" tape version == %06d [%s] [file: %06d]\n",
796 tape.engine_version, (tape.playing ? "PLAYING" : "RECORDING"),
798 printf(" => game.engine_version == %06d\n", game.engine_version);
801 /* ---------- initialize player's initial move delay --------------------- */
803 /* dynamically adjust player properties according to game engine version */
804 game.initial_move_delay =
805 (game.engine_version <= VERSION_IDENT(2,0,1,0) ? INITIAL_MOVE_DELAY_ON :
806 INITIAL_MOVE_DELAY_OFF);
808 /* dynamically adjust player properties according to level information */
809 game.initial_move_delay_value =
810 (level.double_speed ? MOVE_DELAY_HIGH_SPEED : MOVE_DELAY_NORMAL_SPEED);
812 /* ---------- initialize player's initial push delay --------------------- */
814 /* dynamically adjust player properties according to game engine version */
815 game.initial_push_delay_value =
816 (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
818 /* ---------- initialize changing elements ------------------------------- */
820 /* initialize changing elements information */
821 for (i=0; i < MAX_NUM_ELEMENTS; i++)
823 struct ElementInfo *ei = &element_info[i];
825 /* this pointer might have been changed in the level editor */
826 ei->change = &ei->change_page[0];
828 if (!IS_CUSTOM_ELEMENT(i))
830 ei->change->target_element = EL_EMPTY_SPACE;
831 ei->change->delay_fixed = 0;
832 ei->change->delay_random = 0;
833 ei->change->delay_frames = 1;
836 ei->change_events = CE_BITMASK_DEFAULT;
837 for (j=0; j < NUM_CHANGE_EVENTS; j++)
839 ei->event_page_nr[j] = 0;
840 ei->event_page[j] = &ei->change_page[0];
844 /* add changing elements from pre-defined list */
845 for (i=0; change_delay_list[i].element != EL_UNDEFINED; i++)
847 struct ChangingElementInfo *ch_delay = &change_delay_list[i];
848 struct ElementInfo *ei = &element_info[ch_delay->element];
850 ei->change->target_element = ch_delay->target_element;
851 ei->change->delay_fixed = ch_delay->change_delay;
853 ei->change->pre_change_function = ch_delay->pre_change_function;
854 ei->change->change_function = ch_delay->change_function;
855 ei->change->post_change_function = ch_delay->post_change_function;
857 ei->change_events |= CH_EVENT_BIT(CE_DELAY);
861 /* add change events from custom element configuration */
862 for (i=0; i < NUM_CUSTOM_ELEMENTS; i++)
864 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
866 for (j=0; j < ei->num_change_pages; j++)
868 if (!ei->change_page[j].can_change)
871 for (k=0; k < NUM_CHANGE_EVENTS; k++)
873 /* only add event page for the first page found with this event */
874 if (ei->change_page[j].events & CH_EVENT_BIT(k) &&
875 !(ei->change_events & CH_EVENT_BIT(k)))
877 ei->change_events |= CH_EVENT_BIT(k);
878 ei->event_page_nr[k] = j;
879 ei->event_page[k] = &ei->change_page[j];
887 /* add change events from custom element configuration */
888 for (i=0; i < NUM_CUSTOM_ELEMENTS; i++)
890 int element = EL_CUSTOM_START + i;
892 /* only add custom elements that change after fixed/random frame delay */
893 if (CAN_CHANGE(element) && HAS_CHANGE_EVENT(element, CE_DELAY))
894 element_info[element].change_events |= CH_EVENT_BIT(CE_DELAY);
898 /* ---------- initialize trigger events ---------------------------------- */
900 /* initialize trigger events information */
901 for (i=0; i<MAX_NUM_ELEMENTS; i++)
902 trigger_events[i] = EP_BITMASK_DEFAULT;
905 /* add trigger events from element change event properties */
906 for (i=0; i<MAX_NUM_ELEMENTS; i++)
908 struct ElementInfo *ei = &element_info[i];
910 for (j=0; j < ei->num_change_pages; j++)
912 if (!ei->change_page[j].can_change)
915 if (ei->change_page[j].events & CH_EVENT_BIT(CE_BY_OTHER_ACTION))
917 int trigger_element = ei->change_page[j].trigger_element;
919 trigger_events[trigger_element] |= ei->change_page[j].events;
924 /* add trigger events from element change event properties */
925 for (i=0; i<MAX_NUM_ELEMENTS; i++)
926 if (HAS_CHANGE_EVENT(i, CE_BY_OTHER_ACTION))
927 trigger_events[element_info[i].change->trigger_element] |=
928 element_info[i].change->events;
931 /* ---------- initialize push delay -------------------------------------- */
933 /* initialize push delay values to default */
934 for (i=0; i<MAX_NUM_ELEMENTS; i++)
936 if (!IS_CUSTOM_ELEMENT(i))
938 element_info[i].push_delay_fixed = game.default_push_delay_fixed;
939 element_info[i].push_delay_random = game.default_push_delay_random;
943 /* set push delay value for certain elements from pre-defined list */
944 for (i=0; push_delay_list[i].element != EL_UNDEFINED; i++)
946 int e = push_delay_list[i].element;
948 element_info[e].push_delay_fixed = push_delay_list[i].push_delay_fixed;
949 element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
952 /* ---------- initialize move stepsize ----------------------------------- */
954 /* initialize move stepsize values to default */
955 for (i=0; i<MAX_NUM_ELEMENTS; i++)
956 if (!IS_CUSTOM_ELEMENT(i))
957 element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
959 /* set move stepsize value for certain elements from pre-defined list */
960 for (i=0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
962 int e = move_stepsize_list[i].element;
964 element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
967 /* ---------- initialize gem count --------------------------------------- */
969 /* initialize gem count values for each element */
970 for (i=0; i<MAX_NUM_ELEMENTS; i++)
971 if (!IS_CUSTOM_ELEMENT(i))
972 element_info[i].collect_count = 0;
974 /* add gem count values for all elements from pre-defined list */
975 for (i=0; collect_count_list[i].element != EL_UNDEFINED; i++)
976 element_info[collect_count_list[i].element].collect_count =
977 collect_count_list[i].count;
982 =============================================================================
984 -----------------------------------------------------------------------------
985 initialize and start new game
986 =============================================================================
991 boolean emulate_bd = TRUE; /* unless non-BOULDERDASH elements found */
992 boolean emulate_sb = TRUE; /* unless non-SOKOBAN elements found */
993 boolean emulate_sp = TRUE; /* unless non-SUPAPLEX elements found */
1000 #if USE_NEW_AMOEBA_CODE
1001 printf("Using new amoeba code.\n");
1003 printf("Using old amoeba code.\n");
1008 /* don't play tapes over network */
1009 network_playing = (options.network && !tape.playing);
1011 for (i=0; i<MAX_PLAYERS; i++)
1013 struct PlayerInfo *player = &stored_player[i];
1015 player->index_nr = i;
1016 player->element_nr = EL_PLAYER_1 + i;
1018 player->present = FALSE;
1019 player->active = FALSE;
1022 player->effective_action = 0;
1023 player->programmed_action = 0;
1026 player->gems_still_needed = level.gems_needed;
1027 player->sokobanfields_still_needed = 0;
1028 player->lights_still_needed = 0;
1029 player->friends_still_needed = 0;
1032 player->key[j] = FALSE;
1034 player->dynabomb_count = 0;
1035 player->dynabomb_size = 1;
1036 player->dynabombs_left = 0;
1037 player->dynabomb_xl = FALSE;
1039 player->MovDir = MV_NO_MOVING;
1042 player->GfxDir = MV_NO_MOVING;
1043 player->GfxAction = ACTION_DEFAULT;
1045 player->StepFrame = 0;
1047 player->use_murphy_graphic = FALSE;
1049 player->actual_frame_counter = 0;
1051 player->last_move_dir = MV_NO_MOVING;
1053 player->is_waiting = FALSE;
1054 player->is_moving = FALSE;
1055 player->is_digging = FALSE;
1056 player->is_snapping = FALSE;
1057 player->is_collecting = FALSE;
1058 player->is_pushing = FALSE;
1059 player->is_switching = FALSE;
1061 player->switch_x = -1;
1062 player->switch_y = -1;
1064 player->show_envelope = 0;
1066 player->move_delay = game.initial_move_delay;
1067 player->move_delay_value = game.initial_move_delay_value;
1069 player->push_delay = 0;
1070 player->push_delay_value = game.initial_push_delay_value;
1072 player->last_jx = player->last_jy = 0;
1073 player->jx = player->jy = 0;
1075 player->shield_normal_time_left = 0;
1076 player->shield_deadly_time_left = 0;
1078 player->inventory_size = 0;
1080 DigField(player, 0, 0, 0, 0, DF_NO_PUSH);
1081 SnapField(player, 0, 0);
1083 player->LevelSolved = FALSE;
1084 player->GameOver = FALSE;
1087 network_player_action_received = FALSE;
1089 #if defined(PLATFORM_UNIX)
1090 /* initial null action */
1091 if (network_playing)
1092 SendToServer_MovePlayer(MV_NO_MOVING);
1100 TimeLeft = level.time;
1102 ScreenMovDir = MV_NO_MOVING;
1106 ScrollStepSize = 0; /* will be correctly initialized by ScrollScreen() */
1108 AllPlayersGone = FALSE;
1110 game.yamyam_content_nr = 0;
1111 game.magic_wall_active = FALSE;
1112 game.magic_wall_time_left = 0;
1113 game.light_time_left = 0;
1114 game.timegate_time_left = 0;
1115 game.switchgate_pos = 0;
1116 game.balloon_dir = MV_NO_MOVING;
1117 game.gravity = level.initial_gravity;
1118 game.explosions_delayed = TRUE;
1120 game.envelope_active = FALSE;
1124 game.belt_dir[i] = MV_NO_MOVING;
1125 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
1128 for (i=0; i<MAX_NUM_AMOEBA; i++)
1129 AmoebaCnt[i] = AmoebaCnt2[i] = 0;
1131 for (x=0; x<lev_fieldx; x++)
1133 for (y=0; y<lev_fieldy; y++)
1135 Feld[x][y] = level.field[x][y];
1136 MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
1137 ChangeDelay[x][y] = 0;
1138 ChangePage[x][y] = -1;
1139 Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
1141 WasJustMoving[x][y] = 0;
1142 WasJustFalling[x][y] = 0;
1144 Pushed[x][y] = FALSE;
1146 Changed[x][y] = CE_BITMASK_DEFAULT;
1147 ChangeEvent[x][y] = CE_BITMASK_DEFAULT;
1149 ExplodePhase[x][y] = 0;
1150 ExplodeField[x][y] = EX_NO_EXPLOSION;
1153 GfxRandom[x][y] = INIT_GFX_RANDOM();
1154 GfxElement[x][y] = EL_UNDEFINED;
1155 GfxAction[x][y] = ACTION_DEFAULT;
1156 GfxDir[x][y] = MV_NO_MOVING;
1160 for(y=0; y<lev_fieldy; y++)
1162 for(x=0; x<lev_fieldx; x++)
1164 if (emulate_bd && !IS_BD_ELEMENT(Feld[x][y]))
1166 if (emulate_sb && !IS_SB_ELEMENT(Feld[x][y]))
1168 if (emulate_sp && !IS_SP_ELEMENT(Feld[x][y]))
1171 InitField(x, y, TRUE);
1177 game.emulation = (emulate_bd ? EMU_BOULDERDASH :
1178 emulate_sb ? EMU_SOKOBAN :
1179 emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
1181 /* correct non-moving belts to start moving left */
1183 if (game.belt_dir[i] == MV_NO_MOVING)
1184 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
1186 /* check if any connected player was not found in playfield */
1187 for (i=0; i<MAX_PLAYERS; i++)
1189 struct PlayerInfo *player = &stored_player[i];
1191 if (player->connected && !player->present)
1193 for (j=0; j<MAX_PLAYERS; j++)
1195 struct PlayerInfo *some_player = &stored_player[j];
1196 int jx = some_player->jx, jy = some_player->jy;
1198 /* assign first free player found that is present in the playfield */
1199 if (some_player->present && !some_player->connected)
1201 player->present = TRUE;
1202 player->active = TRUE;
1203 some_player->present = FALSE;
1205 StorePlayer[jx][jy] = player->element_nr;
1206 player->jx = player->last_jx = jx;
1207 player->jy = player->last_jy = jy;
1217 /* when playing a tape, eliminate all players who do not participate */
1219 for (i=0; i<MAX_PLAYERS; i++)
1221 if (stored_player[i].active && !tape.player_participates[i])
1223 struct PlayerInfo *player = &stored_player[i];
1224 int jx = player->jx, jy = player->jy;
1226 player->active = FALSE;
1227 StorePlayer[jx][jy] = 0;
1228 Feld[jx][jy] = EL_EMPTY;
1232 else if (!options.network && !setup.team_mode) /* && !tape.playing */
1234 /* when in single player mode, eliminate all but the first active player */
1236 for (i=0; i<MAX_PLAYERS; i++)
1238 if (stored_player[i].active)
1240 for (j=i+1; j<MAX_PLAYERS; j++)
1242 if (stored_player[j].active)
1244 struct PlayerInfo *player = &stored_player[j];
1245 int jx = player->jx, jy = player->jy;
1247 player->active = FALSE;
1248 StorePlayer[jx][jy] = 0;
1249 Feld[jx][jy] = EL_EMPTY;
1256 /* when recording the game, store which players take part in the game */
1259 for (i=0; i<MAX_PLAYERS; i++)
1260 if (stored_player[i].active)
1261 tape.player_participates[i] = TRUE;
1266 for (i=0; i<MAX_PLAYERS; i++)
1268 struct PlayerInfo *player = &stored_player[i];
1270 printf("Player %d: present == %d, connected == %d, active == %d.\n",
1275 if (local_player == player)
1276 printf("Player %d is local player.\n", i+1);
1280 if (BorderElement == EL_EMPTY)
1283 SBX_Right = lev_fieldx - SCR_FIELDX;
1285 SBY_Lower = lev_fieldy - SCR_FIELDY;
1290 SBX_Right = lev_fieldx - SCR_FIELDX + 1;
1292 SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
1295 if (lev_fieldx + (SBX_Left == -1 ? 2 : 0) <= SCR_FIELDX)
1296 SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
1298 if (lev_fieldy + (SBY_Upper == -1 ? 2 : 0) <= SCR_FIELDY)
1299 SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
1301 /* if local player not found, look for custom element that might create
1302 the player (make some assumptions about the right custom element) */
1303 if (!local_player->present)
1305 int start_x = 0, start_y = 0;
1306 int found_rating = 0;
1307 int found_element = EL_UNDEFINED;
1309 for(y=0; y < lev_fieldy; y++) for(x=0; x < lev_fieldx; x++)
1311 int element = Feld[x][y];
1316 if (!IS_CUSTOM_ELEMENT(element))
1319 if (CAN_CHANGE(element))
1321 for (i=0; i < element_info[element].num_change_pages; i++)
1323 content = element_info[element].change_page[i].target_element;
1324 is_player = ELEM_IS_PLAYER(content);
1326 if (is_player && (found_rating < 3 || element < found_element))
1332 found_element = element;
1337 for(yy=0; yy < 3; yy++) for(xx=0; xx < 3; xx++)
1339 content = element_info[element].content[xx][yy];
1340 is_player = ELEM_IS_PLAYER(content);
1342 if (is_player && (found_rating < 2 || element < found_element))
1344 start_x = x + xx - 1;
1345 start_y = y + yy - 1;
1348 found_element = element;
1351 if (!CAN_CHANGE(element))
1354 for (i=0; i < element_info[element].num_change_pages; i++)
1356 content = element_info[element].change_page[i].content[xx][yy];
1357 is_player = ELEM_IS_PLAYER(content);
1359 if (is_player && (found_rating < 1 || element < found_element))
1361 start_x = x + xx - 1;
1362 start_y = y + yy - 1;
1365 found_element = element;
1371 scroll_x = (start_x < SBX_Left + MIDPOSX ? SBX_Left :
1372 start_x > SBX_Right + MIDPOSX ? SBX_Right :
1375 scroll_y = (start_y < SBY_Upper + MIDPOSY ? SBY_Upper :
1376 start_y > SBY_Lower + MIDPOSY ? SBY_Lower :
1382 scroll_x = (local_player->jx < SBX_Left + MIDPOSX ? SBX_Left :
1383 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
1384 local_player->jx - MIDPOSX);
1386 scroll_y = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
1387 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
1388 local_player->jy - MIDPOSY);
1390 scroll_x = SBX_Left;
1391 scroll_y = SBY_Upper;
1392 if (local_player->jx >= SBX_Left + MIDPOSX)
1393 scroll_x = (local_player->jx <= SBX_Right + MIDPOSX ?
1394 local_player->jx - MIDPOSX :
1396 if (local_player->jy >= SBY_Upper + MIDPOSY)
1397 scroll_y = (local_player->jy <= SBY_Lower + MIDPOSY ?
1398 local_player->jy - MIDPOSY :
1403 CloseDoor(DOOR_CLOSE_1);
1408 /* after drawing the level, correct some elements */
1409 if (game.timegate_time_left == 0)
1410 CloseAllOpenTimegates();
1412 if (setup.soft_scrolling)
1413 BlitBitmap(fieldbuffer, backbuffer, FX, FY, SXSIZE, SYSIZE, SX, SY);
1415 redraw_mask |= REDRAW_FROM_BACKBUFFER;
1418 /* copy default game door content to main double buffer */
1419 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
1420 DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
1423 DrawText(DX + XX_LEVEL, DY + YY_LEVEL, int2str(level_nr, 2), FONT_TEXT_2);
1426 DrawTextExt(drawto, DX + XX_EMERALDS, DY + YY_EMERALDS,
1427 int2str(level_nr, 3), FONT_LEVEL_NUMBER, BLIT_OPAQUE);
1428 BlitBitmap(drawto, drawto,
1429 DX + XX_EMERALDS, DY + YY_EMERALDS + 1,
1430 getFontWidth(FONT_LEVEL_NUMBER) * 3,
1431 getFontHeight(FONT_LEVEL_NUMBER) - 1,
1432 DX + XX_LEVEL - 1, DY + YY_LEVEL + 1);
1435 DrawGameDoorValues();
1439 game_gadget[SOUND_CTRL_ID_MUSIC]->checked = setup.sound_music;
1440 game_gadget[SOUND_CTRL_ID_LOOPS]->checked = setup.sound_loops;
1441 game_gadget[SOUND_CTRL_ID_SIMPLE]->checked = setup.sound_simple;
1445 /* copy actual game door content to door double buffer for OpenDoor() */
1446 BlitBitmap(drawto, bitmap_db_door,
1447 DX, DY, DXSIZE, DYSIZE, DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1);
1449 OpenDoor(DOOR_OPEN_ALL);
1451 PlaySoundStereo(SND_GAME_STARTING, SOUND_MIDDLE);
1452 if (setup.sound_music)
1453 PlayMusic(level_nr);
1455 KeyboardAutoRepeatOffUnlessAutoplay();
1460 printf("Player %d %sactive.\n",
1461 i + 1, (stored_player[i].active ? "" : "not "));
1465 void InitMovDir(int x, int y)
1467 int i, element = Feld[x][y];
1468 static int xy[4][2] =
1475 static int direction[3][4] =
1477 { MV_RIGHT, MV_UP, MV_LEFT, MV_DOWN },
1478 { MV_LEFT, MV_DOWN, MV_RIGHT, MV_UP },
1479 { MV_LEFT, MV_RIGHT, MV_UP, MV_DOWN }
1488 Feld[x][y] = EL_BUG;
1489 MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
1492 case EL_SPACESHIP_RIGHT:
1493 case EL_SPACESHIP_UP:
1494 case EL_SPACESHIP_LEFT:
1495 case EL_SPACESHIP_DOWN:
1496 Feld[x][y] = EL_SPACESHIP;
1497 MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
1500 case EL_BD_BUTTERFLY_RIGHT:
1501 case EL_BD_BUTTERFLY_UP:
1502 case EL_BD_BUTTERFLY_LEFT:
1503 case EL_BD_BUTTERFLY_DOWN:
1504 Feld[x][y] = EL_BD_BUTTERFLY;
1505 MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
1508 case EL_BD_FIREFLY_RIGHT:
1509 case EL_BD_FIREFLY_UP:
1510 case EL_BD_FIREFLY_LEFT:
1511 case EL_BD_FIREFLY_DOWN:
1512 Feld[x][y] = EL_BD_FIREFLY;
1513 MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
1516 case EL_PACMAN_RIGHT:
1518 case EL_PACMAN_LEFT:
1519 case EL_PACMAN_DOWN:
1520 Feld[x][y] = EL_PACMAN;
1521 MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
1524 case EL_SP_SNIKSNAK:
1525 MovDir[x][y] = MV_UP;
1528 case EL_SP_ELECTRON:
1529 MovDir[x][y] = MV_LEFT;
1536 Feld[x][y] = EL_MOLE;
1537 MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
1541 if (IS_CUSTOM_ELEMENT(element))
1543 if (element_info[element].move_direction_initial != MV_NO_MOVING)
1544 MovDir[x][y] = element_info[element].move_direction_initial;
1545 else if (element_info[element].move_pattern == MV_ALL_DIRECTIONS ||
1546 element_info[element].move_pattern == MV_TURNING_LEFT ||
1547 element_info[element].move_pattern == MV_TURNING_RIGHT)
1548 MovDir[x][y] = 1 << RND(4);
1549 else if (element_info[element].move_pattern == MV_HORIZONTAL)
1550 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
1551 else if (element_info[element].move_pattern == MV_VERTICAL)
1552 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
1553 else if (element_info[element].move_pattern & MV_ANY_DIRECTION)
1554 MovDir[x][y] = element_info[element].move_pattern;
1555 else if (element_info[element].move_pattern == MV_ALONG_LEFT_SIDE ||
1556 element_info[element].move_pattern == MV_ALONG_RIGHT_SIDE)
1560 int x1 = x + xy[i][0];
1561 int y1 = y + xy[i][1];
1563 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
1565 if (element_info[element].move_pattern == MV_ALONG_RIGHT_SIDE)
1566 MovDir[x][y] = direction[0][i];
1568 MovDir[x][y] = direction[1][i];
1577 MovDir[x][y] = 1 << RND(4);
1579 if (element != EL_BUG &&
1580 element != EL_SPACESHIP &&
1581 element != EL_BD_BUTTERFLY &&
1582 element != EL_BD_FIREFLY)
1587 int x1 = x + xy[i][0];
1588 int y1 = y + xy[i][1];
1590 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
1592 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
1594 MovDir[x][y] = direction[0][i];
1597 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
1598 element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
1600 MovDir[x][y] = direction[1][i];
1609 GfxDir[x][y] = MovDir[x][y];
1612 void InitAmoebaNr(int x, int y)
1615 int group_nr = AmoebeNachbarNr(x, y);
1619 for (i=1; i<MAX_NUM_AMOEBA; i++)
1621 if (AmoebaCnt[i] == 0)
1629 AmoebaNr[x][y] = group_nr;
1630 AmoebaCnt[group_nr]++;
1631 AmoebaCnt2[group_nr]++;
1637 boolean raise_level = FALSE;
1639 if (local_player->MovPos)
1643 if (tape.auto_play) /* tape might already be stopped here */
1644 tape.auto_play_level_solved = TRUE;
1646 if (tape.playing && tape.auto_play)
1647 tape.auto_play_level_solved = TRUE;
1650 local_player->LevelSolved = FALSE;
1652 PlaySoundStereo(SND_GAME_WINNING, SOUND_MIDDLE);
1656 if (!tape.playing && setup.sound_loops)
1657 PlaySoundExt(SND_GAME_LEVELTIME_BONUS, SOUND_MAX_VOLUME, SOUND_MIDDLE,
1658 SND_CTRL_PLAY_LOOP);
1660 while (TimeLeft > 0)
1662 if (!tape.playing && !setup.sound_loops)
1663 PlaySoundStereo(SND_GAME_LEVELTIME_BONUS, SOUND_MIDDLE);
1664 if (TimeLeft > 0 && !(TimeLeft % 10))
1665 RaiseScore(level.score[SC_TIME_BONUS]);
1666 if (TimeLeft > 100 && !(TimeLeft % 10))
1670 DrawText(DX_TIME, DY_TIME, int2str(TimeLeft, 3), FONT_TEXT_2);
1677 if (!tape.playing && setup.sound_loops)
1678 StopSound(SND_GAME_LEVELTIME_BONUS);
1680 else if (level.time == 0) /* level without time limit */
1682 if (!tape.playing && setup.sound_loops)
1683 PlaySoundExt(SND_GAME_LEVELTIME_BONUS, SOUND_MAX_VOLUME, SOUND_MIDDLE,
1684 SND_CTRL_PLAY_LOOP);
1686 while (TimePlayed < 999)
1688 if (!tape.playing && !setup.sound_loops)
1689 PlaySoundStereo(SND_GAME_LEVELTIME_BONUS, SOUND_MIDDLE);
1690 if (TimePlayed < 999 && !(TimePlayed % 10))
1691 RaiseScore(level.score[SC_TIME_BONUS]);
1692 if (TimePlayed < 900 && !(TimePlayed % 10))
1696 DrawText(DX_TIME, DY_TIME, int2str(TimePlayed, 3), FONT_TEXT_2);
1703 if (!tape.playing && setup.sound_loops)
1704 StopSound(SND_GAME_LEVELTIME_BONUS);
1707 /* close exit door after last player */
1708 if ((Feld[ExitX][ExitY] == EL_EXIT_OPEN ||
1709 Feld[ExitX][ExitY] == EL_SP_EXIT_OPEN) && AllPlayersGone)
1711 int element = Feld[ExitX][ExitY];
1713 Feld[ExitX][ExitY] = (element == EL_EXIT_OPEN ? EL_EXIT_CLOSING :
1714 EL_SP_EXIT_CLOSING);
1716 PlaySoundLevelElementAction(ExitX, ExitY, element, ACTION_CLOSING);
1719 /* Hero disappears */
1720 DrawLevelField(ExitX, ExitY);
1726 CloseDoor(DOOR_CLOSE_1);
1731 SaveTape(tape.level_nr); /* Ask to save tape */
1734 if (level_nr == leveldir_current->handicap_level)
1736 leveldir_current->handicap_level++;
1737 SaveLevelSetup_SeriesInfo();
1740 if (level_editor_test_game)
1741 local_player->score = -1; /* no highscore when playing from editor */
1742 else if (level_nr < leveldir_current->last_level)
1743 raise_level = TRUE; /* advance to next level */
1745 if ((hi_pos = NewHiScore()) >= 0)
1747 game_status = GAME_MODE_SCORES;
1748 DrawHallOfFame(hi_pos);
1757 game_status = GAME_MODE_MAIN;
1774 LoadScore(level_nr);
1776 if (strcmp(setup.player_name, EMPTY_PLAYER_NAME) == 0 ||
1777 local_player->score < highscore[MAX_SCORE_ENTRIES - 1].Score)
1780 for (k=0; k<MAX_SCORE_ENTRIES; k++)
1782 if (local_player->score > highscore[k].Score)
1784 /* player has made it to the hall of fame */
1786 if (k < MAX_SCORE_ENTRIES - 1)
1788 int m = MAX_SCORE_ENTRIES - 1;
1791 for (l=k; l<MAX_SCORE_ENTRIES; l++)
1792 if (!strcmp(setup.player_name, highscore[l].Name))
1794 if (m == k) /* player's new highscore overwrites his old one */
1800 strcpy(highscore[l].Name, highscore[l - 1].Name);
1801 highscore[l].Score = highscore[l - 1].Score;
1808 strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
1809 highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
1810 highscore[k].Score = local_player->score;
1816 else if (!strncmp(setup.player_name, highscore[k].Name,
1817 MAX_PLAYER_NAME_LEN))
1818 break; /* player already there with a higher score */
1824 SaveScore(level_nr);
1829 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
1831 if (player->GfxAction != action || player->GfxDir != dir)
1834 printf("Player frame reset! (%d => %d, %d => %d)\n",
1835 player->GfxAction, action, player->GfxDir, dir);
1838 player->GfxAction = action;
1839 player->GfxDir = dir;
1841 player->StepFrame = 0;
1845 static void ResetRandomAnimationValue(int x, int y)
1847 GfxRandom[x][y] = INIT_GFX_RANDOM();
1850 static void ResetGfxAnimation(int x, int y)
1853 GfxAction[x][y] = ACTION_DEFAULT;
1854 GfxDir[x][y] = MovDir[x][y];
1857 void InitMovingField(int x, int y, int direction)
1859 int element = Feld[x][y];
1860 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
1861 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
1865 if (!WasJustMoving[x][y] || direction != MovDir[x][y])
1866 ResetGfxAnimation(x, y);
1868 MovDir[newx][newy] = MovDir[x][y] = direction;
1869 GfxDir[x][y] = direction;
1871 if (Feld[newx][newy] == EL_EMPTY)
1872 Feld[newx][newy] = EL_BLOCKED;
1874 if (direction == MV_DOWN && CAN_FALL(element))
1875 GfxAction[x][y] = ACTION_FALLING;
1877 GfxAction[x][y] = ACTION_MOVING;
1879 GfxFrame[newx][newy] = GfxFrame[x][y];
1880 GfxRandom[newx][newy] = GfxRandom[x][y];
1881 GfxAction[newx][newy] = GfxAction[x][y];
1882 GfxDir[newx][newy] = GfxDir[x][y];
1885 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
1887 int direction = MovDir[x][y];
1888 int newx = x + (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
1889 int newy = y + (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
1895 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
1897 int oldx = x, oldy = y;
1898 int direction = MovDir[x][y];
1900 if (direction == MV_LEFT)
1902 else if (direction == MV_RIGHT)
1904 else if (direction == MV_UP)
1906 else if (direction == MV_DOWN)
1909 *comes_from_x = oldx;
1910 *comes_from_y = oldy;
1913 int MovingOrBlocked2Element(int x, int y)
1915 int element = Feld[x][y];
1917 if (element == EL_BLOCKED)
1921 Blocked2Moving(x, y, &oldx, &oldy);
1922 return Feld[oldx][oldy];
1928 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
1930 /* like MovingOrBlocked2Element(), but if element is moving
1931 and (x,y) is the field the moving element is just leaving,
1932 return EL_BLOCKED instead of the element value */
1933 int element = Feld[x][y];
1935 if (IS_MOVING(x, y))
1937 if (element == EL_BLOCKED)
1941 Blocked2Moving(x, y, &oldx, &oldy);
1942 return Feld[oldx][oldy];
1951 static void RemoveField(int x, int y)
1953 Feld[x][y] = EL_EMPTY;
1960 ChangeDelay[x][y] = 0;
1961 ChangePage[x][y] = -1;
1962 Pushed[x][y] = FALSE;
1964 GfxElement[x][y] = EL_UNDEFINED;
1965 GfxAction[x][y] = ACTION_DEFAULT;
1966 GfxDir[x][y] = MV_NO_MOVING;
1969 void RemoveMovingField(int x, int y)
1971 int oldx = x, oldy = y, newx = x, newy = y;
1972 int element = Feld[x][y];
1973 int next_element = EL_UNDEFINED;
1975 if (element != EL_BLOCKED && !IS_MOVING(x, y))
1978 if (IS_MOVING(x, y))
1980 Moving2Blocked(x, y, &newx, &newy);
1981 if (Feld[newx][newy] != EL_BLOCKED)
1984 else if (element == EL_BLOCKED)
1986 Blocked2Moving(x, y, &oldx, &oldy);
1987 if (!IS_MOVING(oldx, oldy))
1991 if (element == EL_BLOCKED &&
1992 (Feld[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
1993 Feld[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
1994 Feld[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
1995 Feld[oldx][oldy] == EL_AMOEBA_DROPPING))
1996 next_element = get_next_element(Feld[oldx][oldy]);
1998 RemoveField(oldx, oldy);
1999 RemoveField(newx, newy);
2001 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
2003 if (next_element != EL_UNDEFINED)
2004 Feld[oldx][oldy] = next_element;
2006 DrawLevelField(oldx, oldy);
2007 DrawLevelField(newx, newy);
2010 void DrawDynamite(int x, int y)
2012 int sx = SCREENX(x), sy = SCREENY(y);
2013 int graphic = el2img(Feld[x][y]);
2016 if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
2019 if (IS_WALKABLE_INSIDE(Back[x][y]))
2023 DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
2024 else if (Store[x][y])
2025 DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
2027 frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
2030 if (Back[x][y] || Store[x][y])
2031 DrawGraphicThruMask(sx, sy, graphic, frame);
2033 DrawGraphic(sx, sy, graphic, frame);
2035 if (game.emulation == EMU_SUPAPLEX)
2036 DrawGraphic(sx, sy, IMG_SP_DISK_RED, frame);
2037 else if (Store[x][y])
2038 DrawGraphicThruMask(sx, sy, graphic, frame);
2040 DrawGraphic(sx, sy, graphic, frame);
2044 void CheckDynamite(int x, int y)
2046 if (MovDelay[x][y] != 0) /* dynamite is still waiting to explode */
2050 if (MovDelay[x][y] != 0)
2053 PlaySoundLevelActionIfLoop(x, y, ACTION_ACTIVE);
2060 StopSoundLevelActionIfLoop(x, y, ACTION_ACTIVE);
2062 if (Feld[x][y] == EL_DYNAMITE_ACTIVE ||
2063 Feld[x][y] == EL_SP_DISK_RED_ACTIVE)
2064 StopSound(SND_DYNAMITE_ACTIVE);
2066 StopSound(SND_DYNABOMB_ACTIVE);
2072 void RelocatePlayer(int x, int y, int element)
2074 struct PlayerInfo *player = &stored_player[element - EL_PLAYER_1];
2077 RemoveField(x, y); /* temporarily remove newly placed player */
2078 DrawLevelField(x, y);
2081 if (player->present)
2083 while (player->MovPos)
2085 ScrollPlayer(player, SCROLL_GO_ON);
2086 ScrollScreen(NULL, SCROLL_GO_ON);
2092 Delay(GAME_FRAME_DELAY);
2095 DrawPlayer(player); /* needed here only to cleanup last field */
2096 DrawLevelField(player->jx, player->jy); /* remove player graphic */
2098 player->is_moving = FALSE;
2101 Feld[x][y] = element;
2102 InitPlayerField(x, y, element, TRUE);
2104 if (player == local_player)
2106 int scroll_xx = -999, scroll_yy = -999;
2108 while (scroll_xx != scroll_x || scroll_yy != scroll_y)
2111 int fx = FX, fy = FY;
2113 scroll_xx = (local_player->jx < SBX_Left + MIDPOSX ? SBX_Left :
2114 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
2115 local_player->jx - MIDPOSX);
2117 scroll_yy = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
2118 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
2119 local_player->jy - MIDPOSY);
2121 dx = (scroll_xx < scroll_x ? +1 : scroll_xx > scroll_x ? -1 : 0);
2122 dy = (scroll_yy < scroll_y ? +1 : scroll_yy > scroll_y ? -1 : 0);
2127 fx += dx * TILEX / 2;
2128 fy += dy * TILEY / 2;
2130 ScrollLevel(dx, dy);
2133 /* scroll in two steps of half tile size to make things smoother */
2134 BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
2136 Delay(GAME_FRAME_DELAY);
2138 /* scroll second step to align at full tile size */
2140 Delay(GAME_FRAME_DELAY);
2145 void Explode(int ex, int ey, int phase, int mode)
2149 int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
2150 int last_phase = num_phase * delay;
2151 int half_phase = (num_phase / 2) * delay;
2152 int first_phase_after_start = EX_PHASE_START + 1;
2154 if (game.explosions_delayed)
2156 ExplodeField[ex][ey] = mode;
2160 if (phase == EX_PHASE_START) /* initialize 'Store[][]' field */
2162 int center_element = Feld[ex][ey];
2165 /* --- This is only really needed (and now handled) in "Impact()". --- */
2166 /* do not explode moving elements that left the explode field in time */
2167 if (game.engine_version >= VERSION_IDENT(2,2,0,7) &&
2168 center_element == EL_EMPTY && (mode == EX_NORMAL || mode == EX_CENTER))
2172 if (mode == EX_NORMAL || mode == EX_CENTER)
2173 PlaySoundLevelAction(ex, ey, ACTION_EXPLODING);
2175 /* remove things displayed in background while burning dynamite */
2176 if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
2179 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
2181 /* put moving element to center field (and let it explode there) */
2182 center_element = MovingOrBlocked2Element(ex, ey);
2183 RemoveMovingField(ex, ey);
2184 Feld[ex][ey] = center_element;
2187 for (y = ey - 1; y <= ey + 1; y++) for(x = ex - 1; x <= ex + 1; x++)
2189 int xx = x - ex + 1;
2190 int yy = y - ey + 1;
2193 if (!IN_LEV_FIELD(x, y) ||
2194 ((mode != EX_NORMAL || center_element == EL_AMOEBA_TO_DIAMOND) &&
2195 (x != ex || y != ey)))
2198 element = Feld[x][y];
2200 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
2202 element = MovingOrBlocked2Element(x, y);
2204 if (!IS_EXPLOSION_PROOF(element))
2205 RemoveMovingField(x, y);
2211 if (IS_EXPLOSION_PROOF(element))
2214 /* indestructible elements can only explode in center (but not flames) */
2215 if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey)) ||
2216 element == EL_FLAMES)
2221 if ((IS_INDESTRUCTIBLE(element) &&
2222 (game.engine_version < VERSION_IDENT(2,2,0,0) ||
2223 (!IS_WALKABLE_OVER(element) && !IS_WALKABLE_UNDER(element)))) ||
2224 element == EL_FLAMES)
2228 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
2230 if (IS_ACTIVE_BOMB(element))
2232 /* re-activate things under the bomb like gate or penguin */
2233 Feld[x][y] = (Store[x][y] ? Store[x][y] : EL_EMPTY);
2240 /* save walkable background elements while explosion on same tile */
2242 if (IS_INDESTRUCTIBLE(element))
2243 Back[x][y] = element;
2245 if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element))
2246 Back[x][y] = element;
2249 /* ignite explodable elements reached by other explosion */
2250 if (element == EL_EXPLOSION)
2251 element = Store2[x][y];
2254 if (AmoebaNr[x][y] &&
2255 (element == EL_AMOEBA_FULL ||
2256 element == EL_BD_AMOEBA ||
2257 element == EL_AMOEBA_GROWING))
2259 AmoebaCnt[AmoebaNr[x][y]]--;
2260 AmoebaCnt2[AmoebaNr[x][y]]--;
2266 if (IS_PLAYER(ex, ey) && !PLAYER_PROTECTED(ex, ey))
2268 switch(StorePlayer[ex][ey])
2271 Store[x][y] = EL_EMERALD_RED;
2274 Store[x][y] = EL_EMERALD;
2277 Store[x][y] = EL_EMERALD_PURPLE;
2281 Store[x][y] = EL_EMERALD_YELLOW;
2285 if (game.emulation == EMU_SUPAPLEX)
2286 Store[x][y] = EL_EMPTY;
2288 else if (center_element == EL_MOLE)
2289 Store[x][y] = EL_EMERALD_RED;
2290 else if (center_element == EL_PENGUIN)
2291 Store[x][y] = EL_EMERALD_PURPLE;
2292 else if (center_element == EL_BUG)
2293 Store[x][y] = ((x == ex && y == ey) ? EL_DIAMOND : EL_EMERALD);
2294 else if (center_element == EL_BD_BUTTERFLY)
2295 Store[x][y] = EL_BD_DIAMOND;
2296 else if (center_element == EL_SP_ELECTRON)
2297 Store[x][y] = EL_SP_INFOTRON;
2298 else if (center_element == EL_AMOEBA_TO_DIAMOND)
2299 Store[x][y] = level.amoeba_content;
2300 else if (center_element == EL_YAMYAM)
2301 Store[x][y] = level.yamyam_content[game.yamyam_content_nr][xx][yy];
2302 else if (IS_CUSTOM_ELEMENT(center_element) &&
2303 element_info[center_element].content[xx][yy] != EL_EMPTY)
2304 Store[x][y] = element_info[center_element].content[xx][yy];
2305 else if (element == EL_WALL_EMERALD)
2306 Store[x][y] = EL_EMERALD;
2307 else if (element == EL_WALL_DIAMOND)
2308 Store[x][y] = EL_DIAMOND;
2309 else if (element == EL_WALL_BD_DIAMOND)
2310 Store[x][y] = EL_BD_DIAMOND;
2311 else if (element == EL_WALL_EMERALD_YELLOW)
2312 Store[x][y] = EL_EMERALD_YELLOW;
2313 else if (element == EL_WALL_EMERALD_RED)
2314 Store[x][y] = EL_EMERALD_RED;
2315 else if (element == EL_WALL_EMERALD_PURPLE)
2316 Store[x][y] = EL_EMERALD_PURPLE;
2317 else if (element == EL_WALL_PEARL)
2318 Store[x][y] = EL_PEARL;
2319 else if (element == EL_WALL_CRYSTAL)
2320 Store[x][y] = EL_CRYSTAL;
2321 else if (IS_CUSTOM_ELEMENT(element) && !CAN_EXPLODE(element))
2322 Store[x][y] = element_info[element].content[1][1];
2324 Store[x][y] = EL_EMPTY;
2326 if (x != ex || y != ey ||
2327 center_element == EL_AMOEBA_TO_DIAMOND || mode == EX_BORDER)
2328 Store2[x][y] = element;
2331 if (AmoebaNr[x][y] &&
2332 (element == EL_AMOEBA_FULL ||
2333 element == EL_BD_AMOEBA ||
2334 element == EL_AMOEBA_GROWING))
2336 AmoebaCnt[AmoebaNr[x][y]]--;
2337 AmoebaCnt2[AmoebaNr[x][y]]--;
2343 MovDir[x][y] = MovPos[x][y] = 0;
2344 GfxDir[x][y] = MovDir[x][y];
2349 Feld[x][y] = EL_EXPLOSION;
2351 GfxElement[x][y] = center_element;
2353 GfxElement[x][y] = EL_UNDEFINED;
2356 ExplodePhase[x][y] = 1;
2360 if (center_element == EL_YAMYAM)
2361 game.yamyam_content_nr =
2362 (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
2373 ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
2377 /* activate this even in non-DEBUG version until cause for crash in
2378 getGraphicAnimationFrame() (see below) is found and eliminated */
2382 if (GfxElement[x][y] == EL_UNDEFINED)
2385 printf("Explode(): x = %d, y = %d: GfxElement == EL_UNDEFINED\n", x, y);
2386 printf("Explode(): This should never happen!\n");
2389 GfxElement[x][y] = EL_EMPTY;
2393 if (phase == first_phase_after_start)
2395 int element = Store2[x][y];
2397 if (element == EL_BLACK_ORB)
2399 Feld[x][y] = Store2[x][y];
2404 else if (phase == half_phase)
2406 int element = Store2[x][y];
2408 if (IS_PLAYER(x, y))
2409 KillHeroUnlessProtected(x, y);
2410 else if (CAN_EXPLODE_BY_FIRE(element))
2412 Feld[x][y] = Store2[x][y];
2416 else if (element == EL_AMOEBA_TO_DIAMOND)
2417 AmoebeUmwandeln(x, y);
2420 if (phase == last_phase)
2424 element = Feld[x][y] = Store[x][y];
2425 Store[x][y] = Store2[x][y] = 0;
2426 GfxElement[x][y] = EL_UNDEFINED;
2428 if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
2429 element = Feld[x][y] = Back[x][y];
2432 MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
2433 GfxDir[x][y] = MV_NO_MOVING;
2434 ChangeDelay[x][y] = 0;
2435 ChangePage[x][y] = -1;
2437 InitField(x, y, FALSE);
2438 if (CAN_MOVE(element))
2440 DrawLevelField(x, y);
2442 TestIfElementTouchesCustomElement(x, y);
2444 if (GFX_CRUMBLED(element))
2445 DrawLevelFieldCrumbledSandNeighbours(x, y);
2447 if (IS_PLAYER(x, y) && !PLAYERINFO(x,y)->present)
2448 StorePlayer[x][y] = 0;
2450 if (ELEM_IS_PLAYER(element))
2451 RelocatePlayer(x, y, element);
2453 else if (phase >= delay && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
2456 int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
2458 int stored = Store[x][y];
2459 int graphic = (game.emulation != EMU_SUPAPLEX ? IMG_EXPLOSION :
2460 stored == EL_SP_INFOTRON ? IMG_SP_EXPLOSION_INFOTRON :
2463 int frame = getGraphicAnimationFrame(graphic, phase - delay);
2466 DrawLevelFieldCrumbledSand(x, y);
2468 if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
2470 DrawLevelElement(x, y, Back[x][y]);
2471 DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
2473 else if (IS_WALKABLE_UNDER(Back[x][y]))
2475 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
2476 DrawLevelElementThruMask(x, y, Back[x][y]);
2478 else if (!IS_WALKABLE_INSIDE(Back[x][y]))
2479 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
2483 void DynaExplode(int ex, int ey)
2486 int dynabomb_size = 1;
2487 boolean dynabomb_xl = FALSE;
2488 struct PlayerInfo *player;
2489 static int xy[4][2] =
2497 if (IS_ACTIVE_BOMB(Feld[ex][ey]))
2499 player = &stored_player[Feld[ex][ey] - EL_DYNABOMB_PLAYER_1_ACTIVE];
2500 dynabomb_size = player->dynabomb_size;
2501 dynabomb_xl = player->dynabomb_xl;
2502 player->dynabombs_left++;
2505 Explode(ex, ey, EX_PHASE_START, EX_CENTER);
2509 for (j=1; j<=dynabomb_size; j++)
2511 int x = ex + j * xy[i % 4][0];
2512 int y = ey + j * xy[i % 4][1];
2515 if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
2518 element = Feld[x][y];
2520 /* do not restart explosions of fields with active bombs */
2521 if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
2524 Explode(x, y, EX_PHASE_START, EX_BORDER);
2526 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
2527 if (element != EL_EMPTY &&
2528 element != EL_SAND &&
2529 element != EL_EXPLOSION &&
2536 void Bang(int x, int y)
2539 int element = MovingOrBlocked2Element(x, y);
2541 int element = Feld[x][y];
2545 if (IS_PLAYER(x, y) && !PLAYER_PROTECTED(x, y))
2547 if (IS_PLAYER(x, y))
2550 struct PlayerInfo *player = PLAYERINFO(x, y);
2552 element = Feld[x][y] = (player->use_murphy_graphic ? EL_SP_MURPHY :
2553 player->element_nr);
2558 PlaySoundLevelAction(x, y, ACTION_EXPLODING);
2560 if (game.emulation == EMU_SUPAPLEX)
2561 PlaySoundLevel(x, y, SND_SP_ELEMENT_EXPLODING);
2563 PlaySoundLevel(x, y, SND_ELEMENT_EXPLODING);
2568 if (IS_PLAYER(x, y)) /* remove objects that might cause smaller explosion */
2576 case EL_BD_BUTTERFLY:
2579 case EL_DARK_YAMYAM:
2583 RaiseScoreElement(element);
2584 Explode(x, y, EX_PHASE_START, EX_NORMAL);
2586 case EL_DYNABOMB_PLAYER_1_ACTIVE:
2587 case EL_DYNABOMB_PLAYER_2_ACTIVE:
2588 case EL_DYNABOMB_PLAYER_3_ACTIVE:
2589 case EL_DYNABOMB_PLAYER_4_ACTIVE:
2590 case EL_DYNABOMB_INCREASE_NUMBER:
2591 case EL_DYNABOMB_INCREASE_SIZE:
2592 case EL_DYNABOMB_INCREASE_POWER:
2597 case EL_LAMP_ACTIVE:
2598 if (IS_PLAYER(x, y))
2599 Explode(x, y, EX_PHASE_START, EX_NORMAL);
2601 Explode(x, y, EX_PHASE_START, EX_CENTER);
2604 if (CAN_EXPLODE_1X1(element))
2605 Explode(x, y, EX_PHASE_START, EX_CENTER);
2607 Explode(x, y, EX_PHASE_START, EX_NORMAL);
2611 CheckTriggeredElementChange(x, y, element, CE_OTHER_IS_EXPLODING);
2614 void SplashAcid(int x, int y)
2616 int element = Feld[x][y];
2618 if (element != EL_ACID_SPLASH_LEFT &&
2619 element != EL_ACID_SPLASH_RIGHT)
2621 PlaySoundLevel(x, y, SND_ACID_SPLASHING);
2623 if (IN_LEV_FIELD(x-1, y) && IS_FREE(x-1, y) &&
2624 (!IN_LEV_FIELD(x-1, y-1) ||
2625 !CAN_FALL(MovingOrBlocked2Element(x-1, y-1))))
2626 Feld[x-1][y] = EL_ACID_SPLASH_LEFT;
2628 if (IN_LEV_FIELD(x+1, y) && IS_FREE(x+1, y) &&
2629 (!IN_LEV_FIELD(x+1, y-1) ||
2630 !CAN_FALL(MovingOrBlocked2Element(x+1, y-1))))
2631 Feld[x+1][y] = EL_ACID_SPLASH_RIGHT;
2635 static void InitBeltMovement()
2637 static int belt_base_element[4] =
2639 EL_CONVEYOR_BELT_1_LEFT,
2640 EL_CONVEYOR_BELT_2_LEFT,
2641 EL_CONVEYOR_BELT_3_LEFT,
2642 EL_CONVEYOR_BELT_4_LEFT
2644 static int belt_base_active_element[4] =
2646 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
2647 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
2648 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
2649 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
2654 /* set frame order for belt animation graphic according to belt direction */
2661 int element = belt_base_active_element[belt_nr] + j;
2662 int graphic = el2img(element);
2664 if (game.belt_dir[i] == MV_LEFT)
2665 graphic_info[graphic].anim_mode &= ~ANIM_REVERSE;
2667 graphic_info[graphic].anim_mode |= ANIM_REVERSE;
2671 for(y=0; y<lev_fieldy; y++)
2673 for(x=0; x<lev_fieldx; x++)
2675 int element = Feld[x][y];
2679 if (IS_BELT(element) && game.belt_dir[i] != MV_NO_MOVING)
2681 int e_belt_nr = getBeltNrFromBeltElement(element);
2684 if (e_belt_nr == belt_nr)
2686 int belt_part = Feld[x][y] - belt_base_element[belt_nr];
2688 Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
2696 static void ToggleBeltSwitch(int x, int y)
2698 static int belt_base_element[4] =
2700 EL_CONVEYOR_BELT_1_LEFT,
2701 EL_CONVEYOR_BELT_2_LEFT,
2702 EL_CONVEYOR_BELT_3_LEFT,
2703 EL_CONVEYOR_BELT_4_LEFT
2705 static int belt_base_active_element[4] =
2707 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
2708 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
2709 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
2710 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
2712 static int belt_base_switch_element[4] =
2714 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
2715 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
2716 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
2717 EL_CONVEYOR_BELT_4_SWITCH_LEFT
2719 static int belt_move_dir[4] =
2727 int element = Feld[x][y];
2728 int belt_nr = getBeltNrFromBeltSwitchElement(element);
2729 int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
2730 int belt_dir = belt_move_dir[belt_dir_nr];
2733 if (!IS_BELT_SWITCH(element))
2736 game.belt_dir_nr[belt_nr] = belt_dir_nr;
2737 game.belt_dir[belt_nr] = belt_dir;
2739 if (belt_dir_nr == 3)
2742 /* set frame order for belt animation graphic according to belt direction */
2745 int element = belt_base_active_element[belt_nr] + i;
2746 int graphic = el2img(element);
2748 if (belt_dir == MV_LEFT)
2749 graphic_info[graphic].anim_mode &= ~ANIM_REVERSE;
2751 graphic_info[graphic].anim_mode |= ANIM_REVERSE;
2754 for (yy=0; yy<lev_fieldy; yy++)
2756 for (xx=0; xx<lev_fieldx; xx++)
2758 int element = Feld[xx][yy];
2760 if (IS_BELT_SWITCH(element))
2762 int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
2764 if (e_belt_nr == belt_nr)
2766 Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
2767 DrawLevelField(xx, yy);
2770 else if (IS_BELT(element) && belt_dir != MV_NO_MOVING)
2772 int e_belt_nr = getBeltNrFromBeltElement(element);
2774 if (e_belt_nr == belt_nr)
2776 int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
2778 Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
2779 DrawLevelField(xx, yy);
2782 else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NO_MOVING)
2784 int e_belt_nr = getBeltNrFromBeltActiveElement(element);
2786 if (e_belt_nr == belt_nr)
2788 int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
2790 Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
2791 DrawLevelField(xx, yy);
2798 static void ToggleSwitchgateSwitch(int x, int y)
2802 game.switchgate_pos = !game.switchgate_pos;
2804 for (yy=0; yy<lev_fieldy; yy++)
2806 for (xx=0; xx<lev_fieldx; xx++)
2808 int element = Feld[xx][yy];
2810 if (element == EL_SWITCHGATE_SWITCH_UP ||
2811 element == EL_SWITCHGATE_SWITCH_DOWN)
2813 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
2814 DrawLevelField(xx, yy);
2816 else if (element == EL_SWITCHGATE_OPEN ||
2817 element == EL_SWITCHGATE_OPENING)
2819 Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
2821 PlaySoundLevelAction(xx, yy, ACTION_CLOSING);
2823 PlaySoundLevel(xx, yy, SND_SWITCHGATE_CLOSING);
2826 else if (element == EL_SWITCHGATE_CLOSED ||
2827 element == EL_SWITCHGATE_CLOSING)
2829 Feld[xx][yy] = EL_SWITCHGATE_OPENING;
2831 PlaySoundLevelAction(xx, yy, ACTION_OPENING);
2833 PlaySoundLevel(xx, yy, SND_SWITCHGATE_OPENING);
2840 static int getInvisibleActiveFromInvisibleElement(int element)
2842 return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
2843 element == EL_INVISIBLE_WALL ? EL_INVISIBLE_WALL_ACTIVE :
2844 element == EL_INVISIBLE_SAND ? EL_INVISIBLE_SAND_ACTIVE :
2848 static int getInvisibleFromInvisibleActiveElement(int element)
2850 return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
2851 element == EL_INVISIBLE_WALL_ACTIVE ? EL_INVISIBLE_WALL :
2852 element == EL_INVISIBLE_SAND_ACTIVE ? EL_INVISIBLE_SAND :
2856 static void RedrawAllLightSwitchesAndInvisibleElements()
2860 for (y=0; y<lev_fieldy; y++)
2862 for (x=0; x<lev_fieldx; x++)
2864 int element = Feld[x][y];
2866 if (element == EL_LIGHT_SWITCH &&
2867 game.light_time_left > 0)
2869 Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
2870 DrawLevelField(x, y);
2872 else if (element == EL_LIGHT_SWITCH_ACTIVE &&
2873 game.light_time_left == 0)
2875 Feld[x][y] = EL_LIGHT_SWITCH;
2876 DrawLevelField(x, y);
2878 else if (element == EL_INVISIBLE_STEELWALL ||
2879 element == EL_INVISIBLE_WALL ||
2880 element == EL_INVISIBLE_SAND)
2882 if (game.light_time_left > 0)
2883 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
2885 DrawLevelField(x, y);
2887 else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
2888 element == EL_INVISIBLE_WALL_ACTIVE ||
2889 element == EL_INVISIBLE_SAND_ACTIVE)
2891 if (game.light_time_left == 0)
2892 Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
2894 DrawLevelField(x, y);
2900 static void ToggleLightSwitch(int x, int y)
2902 int element = Feld[x][y];
2904 game.light_time_left =
2905 (element == EL_LIGHT_SWITCH ?
2906 level.time_light * FRAMES_PER_SECOND : 0);
2908 RedrawAllLightSwitchesAndInvisibleElements();
2911 static void ActivateTimegateSwitch(int x, int y)
2915 game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
2917 for (yy=0; yy<lev_fieldy; yy++)
2919 for (xx=0; xx<lev_fieldx; xx++)
2921 int element = Feld[xx][yy];
2923 if (element == EL_TIMEGATE_CLOSED ||
2924 element == EL_TIMEGATE_CLOSING)
2926 Feld[xx][yy] = EL_TIMEGATE_OPENING;
2927 PlaySoundLevel(xx, yy, SND_TIMEGATE_OPENING);
2931 else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
2933 Feld[xx][yy] = EL_TIMEGATE_SWITCH;
2934 DrawLevelField(xx, yy);
2941 Feld[x][y] = EL_TIMEGATE_SWITCH_ACTIVE;
2944 inline static int getElementMoveStepsize(int x, int y)
2946 int element = Feld[x][y];
2947 int direction = MovDir[x][y];
2948 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
2949 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
2950 int horiz_move = (dx != 0);
2951 int sign = (horiz_move ? dx : dy);
2952 int step = sign * element_info[element].move_stepsize;
2954 /* special values for move stepsize for spring and things on conveyor belt */
2957 if (CAN_FALL(element) &&
2958 y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
2959 step = sign * MOVE_STEPSIZE_NORMAL / 2;
2960 else if (element == EL_SPRING)
2961 step = sign * MOVE_STEPSIZE_NORMAL * 2;
2967 void Impact(int x, int y)
2969 boolean lastline = (y == lev_fieldy-1);
2970 boolean object_hit = FALSE;
2971 boolean impact = (lastline || object_hit);
2972 int element = Feld[x][y];
2973 int smashed = EL_UNDEFINED;
2975 if (!lastline) /* check if element below was hit */
2977 if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
2980 object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
2981 MovDir[x][y + 1] != MV_DOWN ||
2982 MovPos[x][y + 1] <= TILEY / 2));
2984 /* do not smash moving elements that left the smashed field in time */
2985 if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
2986 ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
2990 smashed = MovingOrBlocked2Element(x, y + 1);
2992 impact = (lastline || object_hit);
2995 if (!lastline && smashed == EL_ACID) /* element falls into acid */
3001 /* only reset graphic animation if graphic really changes after impact */
3003 el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
3005 ResetGfxAnimation(x, y);
3006 DrawLevelField(x, y);
3009 if (impact && CAN_EXPLODE_IMPACT(element))
3014 else if (impact && element == EL_PEARL)
3016 Feld[x][y] = EL_PEARL_BREAKING;
3017 PlaySoundLevel(x, y, SND_PEARL_BREAKING);
3020 else if (impact && CheckElementChange(x, y, element, CE_IMPACT))
3022 PlaySoundLevelElementAction(x, y, element, ACTION_IMPACT);
3027 if (impact && element == EL_AMOEBA_DROP)
3029 if (object_hit && IS_PLAYER(x, y + 1))
3030 KillHeroUnlessProtected(x, y + 1);
3031 else if (object_hit && smashed == EL_PENGUIN)
3035 Feld[x][y] = EL_AMOEBA_GROWING;
3036 Store[x][y] = EL_AMOEBA_WET;
3038 ResetRandomAnimationValue(x, y);
3043 if (object_hit) /* check which object was hit */
3045 if (CAN_PASS_MAGIC_WALL(element) &&
3046 (smashed == EL_MAGIC_WALL ||
3047 smashed == EL_BD_MAGIC_WALL))
3050 int activated_magic_wall =
3051 (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
3052 EL_BD_MAGIC_WALL_ACTIVE);
3054 /* activate magic wall / mill */
3055 for (yy=0; yy<lev_fieldy; yy++)
3056 for (xx=0; xx<lev_fieldx; xx++)
3057 if (Feld[xx][yy] == smashed)
3058 Feld[xx][yy] = activated_magic_wall;
3060 game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
3061 game.magic_wall_active = TRUE;
3063 PlaySoundLevel(x, y, (smashed == EL_MAGIC_WALL ?
3064 SND_MAGIC_WALL_ACTIVATING :
3065 SND_BD_MAGIC_WALL_ACTIVATING));
3068 if (IS_PLAYER(x, y + 1))
3070 if (CAN_SMASH_PLAYER(element))
3072 KillHeroUnlessProtected(x, y + 1);
3076 else if (smashed == EL_PENGUIN)
3078 if (CAN_SMASH_PLAYER(element))
3084 else if (element == EL_BD_DIAMOND)
3086 if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
3092 else if ((element == EL_SP_INFOTRON ||
3093 element == EL_SP_ZONK) &&
3094 (smashed == EL_SP_SNIKSNAK ||
3095 smashed == EL_SP_ELECTRON ||
3096 smashed == EL_SP_DISK_ORANGE))
3102 else if (CAN_SMASH_ENEMIES(element) && IS_CLASSIC_ENEMY(smashed))
3108 else if (CAN_SMASH_EVERYTHING(element))
3110 if (IS_CLASSIC_ENEMY(smashed) ||
3111 CAN_EXPLODE_SMASHED(smashed))
3116 else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
3118 if (smashed == EL_LAMP ||
3119 smashed == EL_LAMP_ACTIVE)
3124 else if (smashed == EL_NUT)
3126 Feld[x][y + 1] = EL_NUT_BREAKING;
3127 PlaySoundLevel(x, y, SND_NUT_BREAKING);
3128 RaiseScoreElement(EL_NUT);
3131 else if (smashed == EL_PEARL)
3133 Feld[x][y + 1] = EL_PEARL_BREAKING;
3134 PlaySoundLevel(x, y, SND_PEARL_BREAKING);
3137 else if (smashed == EL_DIAMOND)
3139 Feld[x][y + 1] = EL_DIAMOND_BREAKING;
3140 PlaySoundLevel(x, y, SND_DIAMOND_BREAKING);
3143 else if (IS_BELT_SWITCH(smashed))
3145 ToggleBeltSwitch(x, y + 1);
3147 else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
3148 smashed == EL_SWITCHGATE_SWITCH_DOWN)
3150 ToggleSwitchgateSwitch(x, y + 1);
3152 else if (smashed == EL_LIGHT_SWITCH ||
3153 smashed == EL_LIGHT_SWITCH_ACTIVE)
3155 ToggleLightSwitch(x, y + 1);
3159 CheckElementChange(x, y + 1, smashed, CE_SMASHED);
3161 CheckTriggeredElementSideChange(x, y + 1, smashed, CH_SIDE_TOP,
3162 CE_OTHER_IS_SWITCHING);
3163 CheckElementSideChange(x, y + 1, smashed, CH_SIDE_TOP,
3169 CheckElementChange(x, y + 1, smashed, CE_SMASHED);
3174 /* play sound of magic wall / mill */
3176 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
3177 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE))
3179 if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
3180 PlaySoundLevel(x, y, SND_MAGIC_WALL_FILLING);
3181 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
3182 PlaySoundLevel(x, y, SND_BD_MAGIC_WALL_FILLING);
3187 /* play sound of object that hits the ground */
3188 if (lastline || object_hit)
3189 PlaySoundLevelElementAction(x, y, element, ACTION_IMPACT);
3192 inline static void TurnRoundExt(int x, int y)
3204 { 0, 0 }, { 0, 0 }, { 0, 0 },
3209 int left, right, back;
3213 { MV_DOWN, MV_UP, MV_RIGHT },
3214 { MV_UP, MV_DOWN, MV_LEFT },
3216 { MV_LEFT, MV_RIGHT, MV_DOWN },
3220 { MV_RIGHT, MV_LEFT, MV_UP }
3223 int element = Feld[x][y];
3224 int old_move_dir = MovDir[x][y];
3225 int left_dir = turn[old_move_dir].left;
3226 int right_dir = turn[old_move_dir].right;
3227 int back_dir = turn[old_move_dir].back;
3229 int left_dx = move_xy[left_dir].x, left_dy = move_xy[left_dir].y;
3230 int right_dx = move_xy[right_dir].x, right_dy = move_xy[right_dir].y;
3231 int move_dx = move_xy[old_move_dir].x, move_dy = move_xy[old_move_dir].y;
3232 int back_dx = move_xy[back_dir].x, back_dy = move_xy[back_dir].y;
3234 int left_x = x + left_dx, left_y = y + left_dy;
3235 int right_x = x + right_dx, right_y = y + right_dy;
3236 int move_x = x + move_dx, move_y = y + move_dy;
3240 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
3242 TestIfBadThingTouchesOtherBadThing(x, y);
3244 if (ENEMY_CAN_ENTER_FIELD(right_x, right_y))
3245 MovDir[x][y] = right_dir;
3246 else if (!ENEMY_CAN_ENTER_FIELD(move_x, move_y))
3247 MovDir[x][y] = left_dir;
3249 if (element == EL_BUG && MovDir[x][y] != old_move_dir)
3251 else if (element == EL_BD_BUTTERFLY) /* && MovDir[x][y] == left_dir) */
3254 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
3255 element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
3257 TestIfBadThingTouchesOtherBadThing(x, y);
3259 if (ENEMY_CAN_ENTER_FIELD(left_x, left_y))
3260 MovDir[x][y] = left_dir;
3261 else if (!ENEMY_CAN_ENTER_FIELD(move_x, move_y))
3262 MovDir[x][y] = right_dir;
3264 if ((element == EL_SPACESHIP ||
3265 element == EL_SP_SNIKSNAK ||
3266 element == EL_SP_ELECTRON)
3267 && MovDir[x][y] != old_move_dir)
3269 else if (element == EL_BD_FIREFLY) /* && MovDir[x][y] == right_dir) */
3272 else if (element == EL_YAMYAM)
3274 boolean can_turn_left = YAMYAM_CAN_ENTER_FIELD(left_x, left_y);
3275 boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(right_x, right_y);
3277 if (can_turn_left && can_turn_right)
3278 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
3279 else if (can_turn_left)
3280 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
3281 else if (can_turn_right)
3282 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
3284 MovDir[x][y] = back_dir;
3286 MovDelay[x][y] = 16 + 16 * RND(3);
3288 else if (element == EL_DARK_YAMYAM)
3290 boolean can_turn_left = DARK_YAMYAM_CAN_ENTER_FIELD(left_x, left_y);
3291 boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(right_x, right_y);
3293 if (can_turn_left && can_turn_right)
3294 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
3295 else if (can_turn_left)
3296 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
3297 else if (can_turn_right)
3298 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
3300 MovDir[x][y] = back_dir;
3302 MovDelay[x][y] = 16 + 16 * RND(3);
3304 else if (element == EL_PACMAN)
3306 boolean can_turn_left = PACMAN_CAN_ENTER_FIELD(left_x, left_y);
3307 boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(right_x, right_y);
3309 if (can_turn_left && can_turn_right)
3310 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
3311 else if (can_turn_left)
3312 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
3313 else if (can_turn_right)
3314 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
3316 MovDir[x][y] = back_dir;
3318 MovDelay[x][y] = 6 + RND(40);
3320 else if (element == EL_PIG)
3322 boolean can_turn_left = PIG_CAN_ENTER_FIELD(left_x, left_y);
3323 boolean can_turn_right = PIG_CAN_ENTER_FIELD(right_x, right_y);
3324 boolean can_move_on = PIG_CAN_ENTER_FIELD(move_x, move_y);
3325 boolean should_turn_left, should_turn_right, should_move_on;
3327 int rnd = RND(rnd_value);
3329 should_turn_left = (can_turn_left &&
3331 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
3332 y + back_dy + left_dy)));
3333 should_turn_right = (can_turn_right &&
3335 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
3336 y + back_dy + right_dy)));
3337 should_move_on = (can_move_on &&
3340 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
3341 y + move_dy + left_dy) ||
3342 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
3343 y + move_dy + right_dy)));
3345 if (should_turn_left || should_turn_right || should_move_on)
3347 if (should_turn_left && should_turn_right && should_move_on)
3348 MovDir[x][y] = (rnd < rnd_value / 3 ? left_dir :
3349 rnd < 2 * rnd_value / 3 ? right_dir :
3351 else if (should_turn_left && should_turn_right)
3352 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
3353 else if (should_turn_left && should_move_on)
3354 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
3355 else if (should_turn_right && should_move_on)
3356 MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
3357 else if (should_turn_left)
3358 MovDir[x][y] = left_dir;
3359 else if (should_turn_right)
3360 MovDir[x][y] = right_dir;
3361 else if (should_move_on)
3362 MovDir[x][y] = old_move_dir;
3364 else if (can_move_on && rnd > rnd_value / 8)
3365 MovDir[x][y] = old_move_dir;
3366 else if (can_turn_left && can_turn_right)
3367 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
3368 else if (can_turn_left && rnd > rnd_value / 8)
3369 MovDir[x][y] = left_dir;
3370 else if (can_turn_right && rnd > rnd_value/8)
3371 MovDir[x][y] = right_dir;
3373 MovDir[x][y] = back_dir;
3375 xx = x + move_xy[MovDir[x][y]].x;
3376 yy = y + move_xy[MovDir[x][y]].y;
3378 if (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy]))
3379 MovDir[x][y] = old_move_dir;
3383 else if (element == EL_DRAGON)
3385 boolean can_turn_left = IN_LEV_FIELD_AND_IS_FREE(left_x, left_y);
3386 boolean can_turn_right = IN_LEV_FIELD_AND_IS_FREE(right_x, right_y);
3387 boolean can_move_on = IN_LEV_FIELD_AND_IS_FREE(move_x, move_y);
3389 int rnd = RND(rnd_value);
3391 if (can_move_on && rnd > rnd_value / 8)
3392 MovDir[x][y] = old_move_dir;
3393 else if (can_turn_left && can_turn_right)
3394 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
3395 else if (can_turn_left && rnd > rnd_value / 8)
3396 MovDir[x][y] = left_dir;
3397 else if (can_turn_right && rnd > rnd_value / 8)
3398 MovDir[x][y] = right_dir;
3400 MovDir[x][y] = back_dir;
3402 xx = x + move_xy[MovDir[x][y]].x;
3403 yy = y + move_xy[MovDir[x][y]].y;
3405 if (!IS_FREE(xx, yy))
3406 MovDir[x][y] = old_move_dir;
3410 else if (element == EL_MOLE)
3412 boolean can_move_on =
3413 (MOLE_CAN_ENTER_FIELD(move_x, move_y,
3414 IS_AMOEBOID(Feld[move_x][move_y]) ||
3415 Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
3418 boolean can_turn_left =
3419 (MOLE_CAN_ENTER_FIELD(left_x, left_y,
3420 IS_AMOEBOID(Feld[left_x][left_y])));
3422 boolean can_turn_right =
3423 (MOLE_CAN_ENTER_FIELD(right_x, right_y,
3424 IS_AMOEBOID(Feld[right_x][right_y])));
3426 if (can_turn_left && can_turn_right)
3427 MovDir[x][y] = (RND(2) ? left_dir : right_dir);
3428 else if (can_turn_left)
3429 MovDir[x][y] = left_dir;
3431 MovDir[x][y] = right_dir;
3434 if (MovDir[x][y] != old_move_dir)
3437 else if (element == EL_BALLOON)
3439 MovDir[x][y] = game.balloon_dir;
3442 else if (element == EL_SPRING)
3444 if (MovDir[x][y] & MV_HORIZONTAL &&
3445 (!IN_LEV_FIELD_AND_IS_FREE(move_x, move_y) ||
3446 IN_LEV_FIELD_AND_IS_FREE(x, y + 1)))
3447 MovDir[x][y] = MV_NO_MOVING;
3451 else if (element == EL_ROBOT ||
3452 element == EL_SATELLITE ||
3453 element == EL_PENGUIN)
3455 int attr_x = -1, attr_y = -1;
3466 for (i=0; i<MAX_PLAYERS; i++)
3468 struct PlayerInfo *player = &stored_player[i];
3469 int jx = player->jx, jy = player->jy;
3471 if (!player->active)
3475 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
3483 if (element == EL_ROBOT && ZX >= 0 && ZY >= 0)
3489 if (element == EL_PENGUIN)
3492 static int xy[4][2] =
3502 int ex = x + xy[i % 4][0];
3503 int ey = y + xy[i % 4][1];
3505 if (IN_LEV_FIELD(ex, ey) && Feld[ex][ey] == EL_EXIT_OPEN)
3514 MovDir[x][y] = MV_NO_MOVING;
3516 MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
3517 else if (attr_x > x)
3518 MovDir[x][y] |= (AllPlayersGone ? MV_LEFT : MV_RIGHT);
3520 MovDir[x][y] |= (AllPlayersGone ? MV_DOWN : MV_UP);
3521 else if (attr_y > y)
3522 MovDir[x][y] |= (AllPlayersGone ? MV_UP : MV_DOWN);
3524 if (element == EL_ROBOT)
3528 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
3529 MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
3530 Moving2Blocked(x, y, &newx, &newy);
3532 if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
3533 MovDelay[x][y] = 8 + 8 * !RND(3);
3535 MovDelay[x][y] = 16;
3537 else if (element == EL_PENGUIN)
3543 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
3545 boolean first_horiz = RND(2);
3546 int new_move_dir = MovDir[x][y];
3549 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
3550 Moving2Blocked(x, y, &newx, &newy);
3552 if (PENGUIN_CAN_ENTER_FIELD(newx, newy))
3556 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
3557 Moving2Blocked(x, y, &newx, &newy);
3559 if (PENGUIN_CAN_ENTER_FIELD(newx, newy))
3562 MovDir[x][y] = old_move_dir;
3566 else /* (element == EL_SATELLITE) */
3572 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
3574 boolean first_horiz = RND(2);
3575 int new_move_dir = MovDir[x][y];
3578 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
3579 Moving2Blocked(x, y, &newx, &newy);
3581 if (ELEMENT_CAN_ENTER_FIELD_OR_ACID_2(newx, newy))
3585 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
3586 Moving2Blocked(x, y, &newx, &newy);
3588 if (ELEMENT_CAN_ENTER_FIELD_OR_ACID_2(newx, newy))
3591 MovDir[x][y] = old_move_dir;
3596 else if (element_info[element].move_pattern == MV_ALL_DIRECTIONS ||
3597 element_info[element].move_pattern == MV_TURNING_LEFT ||
3598 element_info[element].move_pattern == MV_TURNING_RIGHT)
3600 boolean can_turn_left = ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
3601 boolean can_turn_right = ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
3603 if (element_info[element].move_pattern == MV_TURNING_LEFT)
3604 MovDir[x][y] = left_dir;
3605 else if (element_info[element].move_pattern == MV_TURNING_RIGHT)
3606 MovDir[x][y] = right_dir;
3607 else if (can_turn_left && can_turn_right)
3608 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
3609 else if (can_turn_left)
3610 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
3611 else if (can_turn_right)
3612 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
3614 MovDir[x][y] = back_dir;
3616 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
3618 else if (element_info[element].move_pattern == MV_HORIZONTAL ||
3619 element_info[element].move_pattern == MV_VERTICAL)
3621 if (element_info[element].move_pattern & old_move_dir)
3622 MovDir[x][y] = back_dir;
3623 else if (element_info[element].move_pattern == MV_HORIZONTAL)
3624 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
3625 else if (element_info[element].move_pattern == MV_VERTICAL)
3626 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
3628 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
3630 else if (element_info[element].move_pattern & MV_ANY_DIRECTION)
3632 MovDir[x][y] = element_info[element].move_pattern;
3633 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
3635 else if (element_info[element].move_pattern == MV_ALONG_LEFT_SIDE)
3637 if (ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
3638 MovDir[x][y] = left_dir;
3639 else if (!ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
3640 MovDir[x][y] = right_dir;
3642 if (MovDir[x][y] != old_move_dir)
3643 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
3645 else if (element_info[element].move_pattern == MV_ALONG_RIGHT_SIDE)
3647 if (ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
3648 MovDir[x][y] = right_dir;
3649 else if (!ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
3650 MovDir[x][y] = left_dir;
3652 if (MovDir[x][y] != old_move_dir)
3653 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
3655 else if (element_info[element].move_pattern == MV_TOWARDS_PLAYER ||
3656 element_info[element].move_pattern == MV_AWAY_FROM_PLAYER)
3658 int attr_x = -1, attr_y = -1;
3661 (element_info[element].move_pattern == MV_AWAY_FROM_PLAYER);
3672 for (i=0; i<MAX_PLAYERS; i++)
3674 struct PlayerInfo *player = &stored_player[i];
3675 int jx = player->jx, jy = player->jy;
3677 if (!player->active)
3681 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
3689 MovDir[x][y] = MV_NO_MOVING;
3691 MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
3692 else if (attr_x > x)
3693 MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
3695 MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
3696 else if (attr_y > y)
3697 MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
3699 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
3701 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
3703 boolean first_horiz = RND(2);
3704 int new_move_dir = MovDir[x][y];
3707 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
3708 Moving2Blocked(x, y, &newx, &newy);
3710 if (ELEMENT_CAN_ENTER_FIELD_OR_ACID(element, newx, newy))
3714 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
3715 Moving2Blocked(x, y, &newx, &newy);
3717 if (ELEMENT_CAN_ENTER_FIELD_OR_ACID(element, newx, newy))
3720 MovDir[x][y] = old_move_dir;
3723 else if (element_info[element].move_pattern == MV_WHEN_PUSHED)
3725 if (!IN_LEV_FIELD_AND_IS_FREE(move_x, move_y))
3726 MovDir[x][y] = MV_NO_MOVING;
3732 static void TurnRound(int x, int y)
3734 int direction = MovDir[x][y];
3737 GfxDir[x][y] = MovDir[x][y];
3743 GfxDir[x][y] = MovDir[x][y];
3746 if (direction != MovDir[x][y])
3751 GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_BIT(direction);
3754 GfxAction[x][y] = ACTION_WAITING;
3758 static boolean JustBeingPushed(int x, int y)
3762 for (i=0; i<MAX_PLAYERS; i++)
3764 struct PlayerInfo *player = &stored_player[i];
3766 if (player->active && player->is_pushing && player->MovPos)
3768 int next_jx = player->jx + (player->jx - player->last_jx);
3769 int next_jy = player->jy + (player->jy - player->last_jy);
3771 if (x == next_jx && y == next_jy)
3779 void StartMoving(int x, int y)
3781 boolean use_spring_bug = (game.engine_version < VERSION_IDENT(2,2,0,0));
3782 boolean started_moving = FALSE; /* some elements can fall _and_ move */
3783 int element = Feld[x][y];
3789 if (MovDelay[x][y] == 0)
3790 GfxAction[x][y] = ACTION_DEFAULT;
3792 /* !!! this should be handled more generic (not only for mole) !!! */
3793 if (element != EL_MOLE && GfxAction[x][y] != ACTION_DIGGING)
3794 GfxAction[x][y] = ACTION_DEFAULT;
3797 if (CAN_FALL(element) && y < lev_fieldy - 1)
3799 if ((x > 0 && IS_PLAYER(x - 1, y)) ||
3800 (x < lev_fieldx-1 && IS_PLAYER(x + 1, y)))
3801 if (JustBeingPushed(x, y))
3804 if (element == EL_QUICKSAND_FULL)
3806 if (IS_FREE(x, y + 1))
3808 InitMovingField(x, y, MV_DOWN);
3809 started_moving = TRUE;
3811 Feld[x][y] = EL_QUICKSAND_EMPTYING;
3812 Store[x][y] = EL_ROCK;
3814 PlaySoundLevelAction(x, y, ACTION_EMPTYING);
3816 PlaySoundLevel(x, y, SND_QUICKSAND_EMPTYING);
3819 else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
3821 if (!MovDelay[x][y])
3822 MovDelay[x][y] = TILEY + 1;
3831 Feld[x][y] = EL_QUICKSAND_EMPTY;
3832 Feld[x][y + 1] = EL_QUICKSAND_FULL;
3833 Store[x][y + 1] = Store[x][y];
3836 PlaySoundLevelAction(x, y, ACTION_FILLING);
3838 PlaySoundLevel(x, y, SND_QUICKSAND_FILLING);
3842 else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
3843 Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
3845 InitMovingField(x, y, MV_DOWN);
3846 started_moving = TRUE;
3848 Feld[x][y] = EL_QUICKSAND_FILLING;
3849 Store[x][y] = element;
3851 PlaySoundLevelAction(x, y, ACTION_FILLING);
3853 PlaySoundLevel(x, y, SND_QUICKSAND_FILLING);
3856 else if (element == EL_MAGIC_WALL_FULL)
3858 if (IS_FREE(x, y + 1))
3860 InitMovingField(x, y, MV_DOWN);
3861 started_moving = TRUE;
3863 Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
3864 Store[x][y] = EL_CHANGED(Store[x][y]);
3866 else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
3868 if (!MovDelay[x][y])
3869 MovDelay[x][y] = TILEY/4 + 1;
3878 Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
3879 Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
3880 Store[x][y + 1] = EL_CHANGED(Store[x][y]);
3884 else if (element == EL_BD_MAGIC_WALL_FULL)
3886 if (IS_FREE(x, y + 1))
3888 InitMovingField(x, y, MV_DOWN);
3889 started_moving = TRUE;
3891 Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
3892 Store[x][y] = EL_CHANGED2(Store[x][y]);
3894 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
3896 if (!MovDelay[x][y])
3897 MovDelay[x][y] = TILEY/4 + 1;
3906 Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
3907 Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
3908 Store[x][y + 1] = EL_CHANGED2(Store[x][y]);
3912 else if (CAN_PASS_MAGIC_WALL(element) &&
3913 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
3914 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE))
3916 InitMovingField(x, y, MV_DOWN);
3917 started_moving = TRUE;
3920 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
3921 EL_BD_MAGIC_WALL_FILLING);
3922 Store[x][y] = element;
3925 else if (CAN_SMASH(element) && Feld[x][y + 1] == EL_ACID)
3927 else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
3932 InitMovingField(x, y, MV_DOWN);
3933 started_moving = TRUE;
3935 Store[x][y] = EL_ACID;
3937 /* !!! TEST !!! better use "_FALLING" etc. !!! */
3938 GfxAction[x][y + 1] = ACTION_ACTIVE;
3942 else if ((game.engine_version < VERSION_IDENT(2,2,0,7) &&
3943 CAN_SMASH(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
3944 (Feld[x][y + 1] == EL_BLOCKED)) ||
3945 (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
3946 CAN_SMASH(element) && WasJustFalling[x][y] &&
3947 (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))))
3951 else if (game.engine_version < VERSION_IDENT(2,2,0,7) &&
3952 CAN_SMASH(element) && Feld[x][y + 1] == EL_BLOCKED &&
3953 WasJustMoving[x][y] && !Pushed[x][y + 1])
3955 else if (CAN_SMASH(element) && Feld[x][y + 1] == EL_BLOCKED &&
3956 WasJustMoving[x][y])
3961 /* this is needed for a special case not covered by calling "Impact()"
3962 from "ContinueMoving()": if an element moves to a tile directly below
3963 another element which was just falling on that tile (which was empty
3964 in the previous frame), the falling element above would just stop
3965 instead of smashing the element below (in previous version, the above
3966 element was just checked for "moving" instead of "falling", resulting
3967 in incorrect smashes caused by horizontal movement of the above
3968 element; also, the case of the player being the element to smash was
3969 simply not covered here... :-/ ) */
3973 else if (IS_FREE(x, y + 1) && element == EL_SPRING && use_spring_bug)
3975 if (MovDir[x][y] == MV_NO_MOVING)
3977 InitMovingField(x, y, MV_DOWN);
3978 started_moving = TRUE;
3981 else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
3983 if (WasJustFalling[x][y]) /* prevent animation from being restarted */
3984 MovDir[x][y] = MV_DOWN;
3986 InitMovingField(x, y, MV_DOWN);
3987 started_moving = TRUE;
3989 else if (element == EL_AMOEBA_DROP)
3991 Feld[x][y] = EL_AMOEBA_GROWING;
3992 Store[x][y] = EL_AMOEBA_WET;
3994 /* Store[x][y + 1] must be zero, because:
3995 (EL_QUICKSAND_FULL -> EL_ROCK): Store[x][y + 1] == EL_QUICKSAND_EMPTY
3998 #if OLD_GAME_BEHAVIOUR
3999 else if (IS_SLIPPERY(Feld[x][y + 1]) && !Store[x][y + 1])
4001 else if (IS_SLIPPERY(Feld[x][y + 1]) && !Store[x][y + 1] &&
4002 !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
4003 element != EL_DX_SUPABOMB)
4006 else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
4007 (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
4008 !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
4009 element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
4012 boolean can_fall_left = (x > 0 && IS_FREE(x - 1, y) &&
4013 (IS_FREE(x - 1, y + 1) ||
4014 Feld[x - 1][y + 1] == EL_ACID));
4015 boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
4016 (IS_FREE(x + 1, y + 1) ||
4017 Feld[x + 1][y + 1] == EL_ACID));
4018 boolean can_fall_any = (can_fall_left || can_fall_right);
4019 boolean can_fall_both = (can_fall_left && can_fall_right);
4021 if (can_fall_any && IS_CUSTOM_ELEMENT(Feld[x][y + 1]))
4023 int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
4025 if (slippery_type == SLIPPERY_ONLY_LEFT)
4026 can_fall_right = FALSE;
4027 else if (slippery_type == SLIPPERY_ONLY_RIGHT)
4028 can_fall_left = FALSE;
4029 else if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
4030 can_fall_right = FALSE;
4031 else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
4032 can_fall_left = FALSE;
4034 can_fall_any = (can_fall_left || can_fall_right);
4035 can_fall_both = (can_fall_left && can_fall_right);
4040 if (can_fall_both &&
4041 (game.emulation != EMU_BOULDERDASH &&
4042 element != EL_BD_ROCK && element != EL_BD_DIAMOND))
4043 can_fall_left = !(can_fall_right = RND(2));
4045 InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
4046 started_moving = TRUE;
4049 else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
4051 boolean left_is_free = (x > 0 && IS_FREE(x - 1, y));
4052 boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
4053 int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
4054 int belt_dir = game.belt_dir[belt_nr];
4056 if ((belt_dir == MV_LEFT && left_is_free) ||
4057 (belt_dir == MV_RIGHT && right_is_free))
4059 InitMovingField(x, y, belt_dir);
4060 started_moving = TRUE;
4062 GfxAction[x][y] = ACTION_DEFAULT;
4067 /* not "else if" because of elements that can fall and move (EL_SPRING) */
4068 if (CAN_MOVE(element) && !started_moving)
4073 if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
4076 if ((element == EL_SATELLITE ||
4077 element == EL_BALLOON ||
4078 element == EL_SPRING)
4079 && JustBeingPushed(x, y))
4085 if (element == EL_SPRING && MovDir[x][y] == MV_DOWN)
4086 Feld[x][y + 1] = EL_EMPTY; /* was set to EL_BLOCKED above */
4088 if (element == EL_SPRING && MovDir[x][y] != MV_NO_MOVING)
4090 Moving2Blocked(x, y, &newx, &newy);
4091 if (Feld[newx][newy] == EL_BLOCKED)
4092 Feld[newx][newy] = EL_EMPTY; /* was set to EL_BLOCKED above */
4097 if (!MovDelay[x][y]) /* start new movement phase */
4099 /* all objects that can change their move direction after each step
4100 (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall */
4102 if (element != EL_YAMYAM &&
4103 element != EL_DARK_YAMYAM &&
4104 element != EL_PACMAN &&
4105 !(element_info[element].move_pattern & MV_ANY_DIRECTION) &&
4106 element_info[element].move_pattern != MV_TURNING_LEFT &&
4107 element_info[element].move_pattern != MV_TURNING_RIGHT)
4111 if (MovDelay[x][y] && (element == EL_BUG ||
4112 element == EL_SPACESHIP ||
4113 element == EL_SP_SNIKSNAK ||
4114 element == EL_SP_ELECTRON ||
4115 element == EL_MOLE))
4116 DrawLevelField(x, y);
4120 if (MovDelay[x][y]) /* wait some time before next movement */
4125 if (element == EL_YAMYAM)
4128 el_act_dir2img(EL_YAMYAM, ACTION_WAITING, MV_LEFT));
4129 DrawLevelElementAnimation(x, y, element);
4133 if (MovDelay[x][y]) /* element still has to wait some time */
4136 /* !!! PLACE THIS SOMEWHERE AFTER "TurnRound()" !!! */
4137 ResetGfxAnimation(x, y);
4141 if (GfxAction[x][y] != ACTION_WAITING)
4142 printf("::: %d: %d != ACTION_WAITING\n", element, GfxAction[x][y]);
4144 GfxAction[x][y] = ACTION_WAITING;
4148 if (element == EL_ROBOT ||
4150 element == EL_PACMAN ||
4152 element == EL_YAMYAM ||
4153 element == EL_DARK_YAMYAM)
4156 DrawLevelElementAnimation(x, y, element);
4158 DrawLevelElementAnimationIfNeeded(x, y, element);
4160 PlaySoundLevelAction(x, y, ACTION_WAITING);
4162 else if (element == EL_SP_ELECTRON)
4163 DrawLevelElementAnimationIfNeeded(x, y, element);
4164 else if (element == EL_DRAGON)
4167 int dir = MovDir[x][y];
4168 int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
4169 int dy = (dir == MV_UP ? -1 : dir == MV_DOWN ? +1 : 0);
4170 int graphic = (dir == MV_LEFT ? IMG_FLAMES_1_LEFT :
4171 dir == MV_RIGHT ? IMG_FLAMES_1_RIGHT :
4172 dir == MV_UP ? IMG_FLAMES_1_UP :
4173 dir == MV_DOWN ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
4174 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
4177 printf("::: %d, %d\n", GfxAction[x][y], GfxFrame[x][y]);
4180 GfxAction[x][y] = ACTION_ATTACKING;
4182 if (IS_PLAYER(x, y))
4183 DrawPlayerField(x, y);
4185 DrawLevelField(x, y);
4187 PlaySoundLevelActionIfLoop(x, y, ACTION_ATTACKING);
4189 for (i=1; i <= 3; i++)
4191 int xx = x + i * dx;
4192 int yy = y + i * dy;
4193 int sx = SCREENX(xx);
4194 int sy = SCREENY(yy);
4195 int flame_graphic = graphic + (i - 1);
4197 if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
4202 int flamed = MovingOrBlocked2Element(xx, yy);
4204 if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_FIRE(flamed))
4207 RemoveMovingField(xx, yy);
4209 Feld[xx][yy] = EL_FLAMES;
4210 if (IN_SCR_FIELD(sx, sy))
4212 DrawLevelFieldCrumbledSand(xx, yy);
4213 DrawGraphic(sx, sy, flame_graphic, frame);
4218 if (Feld[xx][yy] == EL_FLAMES)
4219 Feld[xx][yy] = EL_EMPTY;
4220 DrawLevelField(xx, yy);
4225 if (MovDelay[x][y]) /* element still has to wait some time */
4227 PlaySoundLevelAction(x, y, ACTION_WAITING);
4233 /* special case of "moving" animation of waiting elements (FIX THIS !!!);
4234 for all other elements GfxAction will be set by InitMovingField() */
4235 if (element == EL_BD_BUTTERFLY || element == EL_BD_FIREFLY)
4236 GfxAction[x][y] = ACTION_MOVING;
4240 /* now make next step */
4242 Moving2Blocked(x, y, &newx, &newy); /* get next screen position */
4244 if (DONT_COLLIDE_WITH(element) &&
4245 IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
4246 !PLAYER_PROTECTED(newx, newy))
4249 TestIfBadThingRunsIntoHero(x, y, MovDir[x][y]);
4252 /* player killed by element which is deadly when colliding with */
4254 KillHero(PLAYERINFO(newx, newy));
4259 else if ((element == EL_PENGUIN ||
4260 element == EL_ROBOT ||
4261 element == EL_SATELLITE ||
4262 element == EL_BALLOON ||
4263 IS_CUSTOM_ELEMENT(element)) &&
4264 IN_LEV_FIELD(newx, newy) &&
4265 MovDir[x][y] == MV_DOWN && Feld[newx][newy] == EL_ACID)
4268 Store[x][y] = EL_ACID;
4270 else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
4272 if (Feld[newx][newy] == EL_EXIT_OPEN)
4274 Feld[x][y] = EL_EMPTY;
4275 DrawLevelField(x, y);
4277 PlaySoundLevel(newx, newy, SND_PENGUIN_PASSING);
4278 if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
4279 DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
4281 local_player->friends_still_needed--;
4282 if (!local_player->friends_still_needed &&
4283 !local_player->GameOver && AllPlayersGone)
4284 local_player->LevelSolved = local_player->GameOver = TRUE;
4288 else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
4290 if (DigField(local_player, newx, newy, 0, 0, DF_DIG) == MF_MOVING)
4291 DrawLevelField(newx, newy);
4293 GfxDir[x][y] = MovDir[x][y] = MV_NO_MOVING;
4295 else if (!IS_FREE(newx, newy))
4297 GfxAction[x][y] = ACTION_WAITING;
4299 if (IS_PLAYER(x, y))
4300 DrawPlayerField(x, y);
4302 DrawLevelField(x, y);
4306 else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
4308 if (IS_FOOD_PIG(Feld[newx][newy]))
4310 if (IS_MOVING(newx, newy))
4311 RemoveMovingField(newx, newy);
4314 Feld[newx][newy] = EL_EMPTY;
4315 DrawLevelField(newx, newy);
4318 PlaySoundLevel(x, y, SND_PIG_DIGGING);
4320 else if (!IS_FREE(newx, newy))
4322 if (IS_PLAYER(x, y))
4323 DrawPlayerField(x, y);
4325 DrawLevelField(x, y);
4329 else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
4331 if (!IS_FREE(newx, newy))
4333 if (IS_PLAYER(x, y))
4334 DrawPlayerField(x, y);
4336 DrawLevelField(x, y);
4342 boolean wanna_flame = !RND(10);
4343 int dx = newx - x, dy = newy - y;
4344 int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
4345 int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
4346 int element1 = (IN_LEV_FIELD(newx1, newy1) ?
4347 MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
4348 int element2 = (IN_LEV_FIELD(newx2, newy2) ?
4349 MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
4352 IS_CLASSIC_ENEMY(element1) ||
4353 IS_CLASSIC_ENEMY(element2)) &&
4354 element1 != EL_DRAGON && element2 != EL_DRAGON &&
4355 element1 != EL_FLAMES && element2 != EL_FLAMES)
4358 ResetGfxAnimation(x, y);
4359 GfxAction[x][y] = ACTION_ATTACKING;
4362 if (IS_PLAYER(x, y))
4363 DrawPlayerField(x, y);
4365 DrawLevelField(x, y);
4367 PlaySoundLevel(x, y, SND_DRAGON_ATTACKING);
4369 MovDelay[x][y] = 50;
4371 Feld[newx][newy] = EL_FLAMES;
4372 if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
4373 Feld[newx1][newy1] = EL_FLAMES;
4374 if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
4375 Feld[newx2][newy2] = EL_FLAMES;
4381 else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
4382 Feld[newx][newy] == EL_DIAMOND)
4384 if (IS_MOVING(newx, newy))
4385 RemoveMovingField(newx, newy);
4388 Feld[newx][newy] = EL_EMPTY;
4389 DrawLevelField(newx, newy);
4392 PlaySoundLevel(x, y, SND_YAMYAM_DIGGING);
4394 else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
4395 IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
4397 if (AmoebaNr[newx][newy])
4399 AmoebaCnt2[AmoebaNr[newx][newy]]--;
4400 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
4401 Feld[newx][newy] == EL_BD_AMOEBA)
4402 AmoebaCnt[AmoebaNr[newx][newy]]--;
4405 if (IS_MOVING(newx, newy))
4406 RemoveMovingField(newx, newy);
4409 Feld[newx][newy] = EL_EMPTY;
4410 DrawLevelField(newx, newy);
4413 PlaySoundLevel(x, y, SND_DARK_YAMYAM_DIGGING);
4415 else if ((element == EL_PACMAN || element == EL_MOLE)
4416 && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
4418 if (AmoebaNr[newx][newy])
4420 AmoebaCnt2[AmoebaNr[newx][newy]]--;
4421 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
4422 Feld[newx][newy] == EL_BD_AMOEBA)
4423 AmoebaCnt[AmoebaNr[newx][newy]]--;
4426 if (element == EL_MOLE)
4428 Feld[newx][newy] = EL_AMOEBA_SHRINKING;
4429 PlaySoundLevel(x, y, SND_MOLE_DIGGING);
4431 ResetGfxAnimation(x, y);
4432 GfxAction[x][y] = ACTION_DIGGING;
4433 DrawLevelField(x, y);
4435 MovDelay[newx][newy] = 0; /* start amoeba shrinking delay */
4436 return; /* wait for shrinking amoeba */
4438 else /* element == EL_PACMAN */
4440 Feld[newx][newy] = EL_EMPTY;
4441 DrawLevelField(newx, newy);
4442 PlaySoundLevel(x, y, SND_PACMAN_DIGGING);
4445 else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
4446 (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
4447 (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
4449 /* wait for shrinking amoeba to completely disappear */
4452 else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
4454 /* object was running against a wall */
4459 if (GFX_ELEMENT(element) != EL_SAND) /* !!! FIX THIS (crumble) !!! */
4460 DrawLevelElementAnimation(x, y, element);
4462 if (element == EL_BUG ||
4463 element == EL_SPACESHIP ||
4464 element == EL_SP_SNIKSNAK)
4465 DrawLevelField(x, y);
4466 else if (element == EL_MOLE)
4467 DrawLevelField(x, y);
4468 else if (element == EL_BD_BUTTERFLY ||
4469 element == EL_BD_FIREFLY)
4470 DrawLevelElementAnimationIfNeeded(x, y, element);
4471 else if (element == EL_SATELLITE)
4472 DrawLevelElementAnimationIfNeeded(x, y, element);
4473 else if (element == EL_SP_ELECTRON)
4474 DrawLevelElementAnimationIfNeeded(x, y, element);
4477 if (DONT_TOUCH(element))
4478 TestIfBadThingTouchesHero(x, y);
4481 PlaySoundLevelAction(x, y, ACTION_WAITING);
4487 InitMovingField(x, y, MovDir[x][y]);
4489 PlaySoundLevelAction(x, y, ACTION_MOVING);
4493 ContinueMoving(x, y);
4496 void ContinueMoving(int x, int y)
4498 int element = Feld[x][y];
4499 int direction = MovDir[x][y];
4500 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
4501 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
4502 int newx = x + dx, newy = y + dy;
4503 int nextx = newx + dx, nexty = newy + dy;
4504 boolean pushed = Pushed[x][y];
4506 MovPos[x][y] += getElementMoveStepsize(x, y);
4508 if (pushed) /* special case: moving object pushed by player */
4509 MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
4511 if (ABS(MovPos[x][y]) < TILEX)
4513 DrawLevelField(x, y);
4515 return; /* element is still moving */
4518 /* element reached destination field */
4520 Feld[x][y] = EL_EMPTY;
4521 Feld[newx][newy] = element;
4522 MovPos[x][y] = 0; /* force "not moving" for "crumbled sand" */
4524 if (element == EL_MOLE)
4526 Feld[x][y] = EL_SAND;
4528 DrawLevelFieldCrumbledSandNeighbours(x, y);
4530 else if (element == EL_QUICKSAND_FILLING)
4532 element = Feld[newx][newy] = get_next_element(element);
4533 Store[newx][newy] = Store[x][y];
4535 else if (element == EL_QUICKSAND_EMPTYING)
4537 Feld[x][y] = get_next_element(element);
4538 element = Feld[newx][newy] = Store[x][y];
4540 else if (element == EL_MAGIC_WALL_FILLING)
4542 element = Feld[newx][newy] = get_next_element(element);
4543 if (!game.magic_wall_active)
4544 element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
4545 Store[newx][newy] = Store[x][y];
4547 else if (element == EL_MAGIC_WALL_EMPTYING)
4549 Feld[x][y] = get_next_element(element);
4550 if (!game.magic_wall_active)
4551 Feld[x][y] = EL_MAGIC_WALL_DEAD;
4552 element = Feld[newx][newy] = Store[x][y];
4554 else if (element == EL_BD_MAGIC_WALL_FILLING)
4556 element = Feld[newx][newy] = get_next_element(element);
4557 if (!game.magic_wall_active)
4558 element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
4559 Store[newx][newy] = Store[x][y];
4561 else if (element == EL_BD_MAGIC_WALL_EMPTYING)
4563 Feld[x][y] = get_next_element(element);
4564 if (!game.magic_wall_active)
4565 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
4566 element = Feld[newx][newy] = Store[x][y];
4568 else if (element == EL_AMOEBA_DROPPING)
4570 Feld[x][y] = get_next_element(element);
4571 element = Feld[newx][newy] = Store[x][y];
4573 else if (element == EL_SOKOBAN_OBJECT)
4576 Feld[x][y] = Back[x][y];
4578 if (Back[newx][newy])
4579 Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
4581 Back[x][y] = Back[newx][newy] = 0;
4583 else if (Store[x][y] == EL_ACID)
4585 element = Feld[newx][newy] = EL_ACID;
4589 MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
4590 MovDelay[newx][newy] = 0;
4592 /* copy element change control values to new field */
4593 ChangeDelay[newx][newy] = ChangeDelay[x][y];
4594 ChangePage[newx][newy] = ChangePage[x][y];
4595 Changed[newx][newy] = Changed[x][y];
4596 ChangeEvent[newx][newy] = ChangeEvent[x][y];
4598 ChangeDelay[x][y] = 0;
4599 ChangePage[x][y] = -1;
4600 Changed[x][y] = CE_BITMASK_DEFAULT;
4601 ChangeEvent[x][y] = CE_BITMASK_DEFAULT;
4603 /* copy animation control values to new field */
4604 GfxFrame[newx][newy] = GfxFrame[x][y];
4605 GfxRandom[newx][newy] = GfxRandom[x][y]; /* keep same random value */
4606 GfxAction[newx][newy] = GfxAction[x][y]; /* keep action one frame */
4607 GfxDir[newx][newy] = GfxDir[x][y]; /* keep element direction */
4609 Pushed[x][y] = Pushed[newx][newy] = FALSE;
4611 ResetGfxAnimation(x, y); /* reset animation values for old field */
4614 /* 2.1.1 (does not work correctly for spring) */
4615 if (!CAN_MOVE(element))
4616 MovDir[newx][newy] = 0;
4620 /* (does not work for falling objects that slide horizontally) */
4621 if (CAN_FALL(element) && MovDir[newx][newy] == MV_DOWN)
4622 MovDir[newx][newy] = 0;
4625 if (!CAN_MOVE(element) ||
4626 (element == EL_SPRING && MovDir[newx][newy] == MV_DOWN))
4627 MovDir[newx][newy] = 0;
4630 if (!CAN_MOVE(element) ||
4631 (CAN_FALL(element) && direction == MV_DOWN))
4632 GfxDir[x][y] = MovDir[newx][newy] = 0;
4637 DrawLevelField(x, y);
4638 DrawLevelField(newx, newy);
4640 Stop[newx][newy] = TRUE; /* ignore this element until the next frame */
4642 /* prevent pushed element from moving on in pushed direction */
4643 if (pushed && CAN_MOVE(element) &&
4644 element_info[element].move_pattern & MV_ANY_DIRECTION &&
4645 !(element_info[element].move_pattern & direction))
4646 TurnRound(newx, newy);
4648 if (!pushed) /* special case: moving object pushed by player */
4650 WasJustMoving[newx][newy] = 3;
4652 if (CAN_FALL(element) && direction == MV_DOWN)
4653 WasJustFalling[newx][newy] = 3;
4656 if (DONT_TOUCH(element)) /* object may be nasty to player or others */
4658 TestIfBadThingTouchesHero(newx, newy);
4659 TestIfBadThingTouchesFriend(newx, newy);
4661 if (!IS_CUSTOM_ELEMENT(element))
4662 TestIfBadThingTouchesOtherBadThing(newx, newy);
4664 else if (element == EL_PENGUIN)
4665 TestIfFriendTouchesBadThing(newx, newy);
4667 if (CAN_FALL(element) && direction == MV_DOWN &&
4668 (newy == lev_fieldy - 1 || !IS_FREE(x, newy + 1)))
4672 TestIfElementTouchesCustomElement(x, y); /* for empty space */
4676 if (ChangePage[newx][newy] != -1) /* delayed change */
4677 ChangeElement(newx, newy, ChangePage[newx][newy]);
4680 if (!IN_LEV_FIELD(nextx, nexty) || !IS_FREE(nextx, nexty))
4681 CheckElementSideChange(newx, newy, Feld[newx][newy], direction,
4684 TestIfPlayerTouchesCustomElement(newx, newy);
4685 TestIfElementTouchesCustomElement(newx, newy);
4688 int AmoebeNachbarNr(int ax, int ay)
4691 int element = Feld[ax][ay];
4693 static int xy[4][2] =
4703 int x = ax + xy[i][0];
4704 int y = ay + xy[i][1];
4706 if (!IN_LEV_FIELD(x, y))
4709 if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
4710 group_nr = AmoebaNr[x][y];
4716 void AmoebenVereinigen(int ax, int ay)
4718 int i, x, y, xx, yy;
4719 int new_group_nr = AmoebaNr[ax][ay];
4720 static int xy[4][2] =
4728 if (new_group_nr == 0)
4736 if (!IN_LEV_FIELD(x, y))
4739 if ((Feld[x][y] == EL_AMOEBA_FULL ||
4740 Feld[x][y] == EL_BD_AMOEBA ||
4741 Feld[x][y] == EL_AMOEBA_DEAD) &&
4742 AmoebaNr[x][y] != new_group_nr)
4744 int old_group_nr = AmoebaNr[x][y];
4746 if (old_group_nr == 0)
4749 AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
4750 AmoebaCnt[old_group_nr] = 0;
4751 AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
4752 AmoebaCnt2[old_group_nr] = 0;
4754 for (yy=0; yy<lev_fieldy; yy++)
4756 for (xx=0; xx<lev_fieldx; xx++)
4758 if (AmoebaNr[xx][yy] == old_group_nr)
4759 AmoebaNr[xx][yy] = new_group_nr;
4766 void AmoebeUmwandeln(int ax, int ay)
4770 if (Feld[ax][ay] == EL_AMOEBA_DEAD)
4772 int group_nr = AmoebaNr[ax][ay];
4777 printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
4778 printf("AmoebeUmwandeln(): This should never happen!\n");
4783 for (y=0; y<lev_fieldy; y++)
4785 for (x=0; x<lev_fieldx; x++)
4787 if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
4790 Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
4794 PlaySoundLevel(ax, ay, (IS_GEM(level.amoeba_content) ?
4795 SND_AMOEBA_TURNING_TO_GEM :
4796 SND_AMOEBA_TURNING_TO_ROCK));
4801 static int xy[4][2] =
4814 if (!IN_LEV_FIELD(x, y))
4817 if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
4819 PlaySoundLevel(x, y, (IS_GEM(level.amoeba_content) ?
4820 SND_AMOEBA_TURNING_TO_GEM :
4821 SND_AMOEBA_TURNING_TO_ROCK));
4828 void AmoebeUmwandelnBD(int ax, int ay, int new_element)
4831 int group_nr = AmoebaNr[ax][ay];
4832 boolean done = FALSE;
4837 printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
4838 printf("AmoebeUmwandelnBD(): This should never happen!\n");
4843 for (y=0; y<lev_fieldy; y++)
4845 for (x=0; x<lev_fieldx; x++)
4847 if (AmoebaNr[x][y] == group_nr &&
4848 (Feld[x][y] == EL_AMOEBA_DEAD ||
4849 Feld[x][y] == EL_BD_AMOEBA ||
4850 Feld[x][y] == EL_AMOEBA_GROWING))
4853 Feld[x][y] = new_element;
4854 InitField(x, y, FALSE);
4855 DrawLevelField(x, y);
4862 PlaySoundLevel(ax, ay, (new_element == EL_BD_ROCK ?
4863 SND_BD_AMOEBA_TURNING_TO_ROCK :
4864 SND_BD_AMOEBA_TURNING_TO_GEM));
4867 void AmoebeWaechst(int x, int y)
4869 static unsigned long sound_delay = 0;
4870 static unsigned long sound_delay_value = 0;
4872 if (!MovDelay[x][y]) /* start new growing cycle */
4876 if (DelayReached(&sound_delay, sound_delay_value))
4879 PlaySoundLevelElementAction(x, y, Store[x][y], ACTION_GROWING);
4881 if (Store[x][y] == EL_BD_AMOEBA)
4882 PlaySoundLevel(x, y, SND_BD_AMOEBA_GROWING);
4884 PlaySoundLevel(x, y, SND_AMOEBA_GROWING);
4886 sound_delay_value = 30;
4890 if (MovDelay[x][y]) /* wait some time before growing bigger */
4893 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
4895 int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
4896 6 - MovDelay[x][y]);
4898 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
4901 if (!MovDelay[x][y])
4903 Feld[x][y] = Store[x][y];
4905 DrawLevelField(x, y);
4910 void AmoebaDisappearing(int x, int y)
4912 static unsigned long sound_delay = 0;
4913 static unsigned long sound_delay_value = 0;
4915 if (!MovDelay[x][y]) /* start new shrinking cycle */
4919 if (DelayReached(&sound_delay, sound_delay_value))
4920 sound_delay_value = 30;
4923 if (MovDelay[x][y]) /* wait some time before shrinking */
4926 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
4928 int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
4929 6 - MovDelay[x][y]);
4931 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
4934 if (!MovDelay[x][y])
4936 Feld[x][y] = EL_EMPTY;
4937 DrawLevelField(x, y);
4939 /* don't let mole enter this field in this cycle;
4940 (give priority to objects falling to this field from above) */
4946 void AmoebeAbleger(int ax, int ay)
4949 int element = Feld[ax][ay];
4950 int graphic = el2img(element);
4951 int newax = ax, neway = ay;
4952 static int xy[4][2] =
4960 if (!level.amoeba_speed)
4962 Feld[ax][ay] = EL_AMOEBA_DEAD;
4963 DrawLevelField(ax, ay);
4967 if (IS_ANIMATED(graphic))
4968 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
4970 if (!MovDelay[ax][ay]) /* start making new amoeba field */
4971 MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
4973 if (MovDelay[ax][ay]) /* wait some time before making new amoeba */
4976 if (MovDelay[ax][ay])
4980 if (element == EL_AMOEBA_WET) /* object is an acid / amoeba drop */
4983 int x = ax + xy[start][0];
4984 int y = ay + xy[start][1];
4986 if (!IN_LEV_FIELD(x, y))
4989 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
4990 if (IS_FREE(x, y) ||
4991 Feld[x][y] == EL_SAND || Feld[x][y] == EL_QUICKSAND_EMPTY)
4997 if (newax == ax && neway == ay)
5000 else /* normal or "filled" (BD style) amoeba */
5003 boolean waiting_for_player = FALSE;
5007 int j = (start + i) % 4;
5008 int x = ax + xy[j][0];
5009 int y = ay + xy[j][1];
5011 if (!IN_LEV_FIELD(x, y))
5014 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
5015 if (IS_FREE(x, y) ||
5016 Feld[x][y] == EL_SAND || Feld[x][y] == EL_QUICKSAND_EMPTY)
5022 else if (IS_PLAYER(x, y))
5023 waiting_for_player = TRUE;
5026 if (newax == ax && neway == ay) /* amoeba cannot grow */
5028 if (i == 4 && (!waiting_for_player || game.emulation == EMU_BOULDERDASH))
5030 Feld[ax][ay] = EL_AMOEBA_DEAD;
5031 DrawLevelField(ax, ay);
5032 AmoebaCnt[AmoebaNr[ax][ay]]--;
5034 if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0) /* amoeba is completely dead */
5036 if (element == EL_AMOEBA_FULL)
5037 AmoebeUmwandeln(ax, ay);
5038 else if (element == EL_BD_AMOEBA)
5039 AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
5044 else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
5046 /* amoeba gets larger by growing in some direction */
5048 int new_group_nr = AmoebaNr[ax][ay];
5051 if (new_group_nr == 0)
5053 printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
5054 printf("AmoebeAbleger(): This should never happen!\n");
5059 AmoebaNr[newax][neway] = new_group_nr;
5060 AmoebaCnt[new_group_nr]++;
5061 AmoebaCnt2[new_group_nr]++;
5063 /* if amoeba touches other amoeba(s) after growing, unify them */
5064 AmoebenVereinigen(newax, neway);
5066 if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
5068 AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
5074 if (element != EL_AMOEBA_WET || neway < ay || !IS_FREE(newax, neway) ||
5075 (neway == lev_fieldy - 1 && newax != ax))
5077 Feld[newax][neway] = EL_AMOEBA_GROWING; /* creation of new amoeba */
5078 Store[newax][neway] = element;
5080 else if (neway == ay)
5082 Feld[newax][neway] = EL_AMOEBA_DROP; /* drop left/right of amoeba */
5084 PlaySoundLevelAction(newax, neway, ACTION_GROWING);
5086 PlaySoundLevel(newax, neway, SND_AMOEBA_GROWING);
5091 InitMovingField(ax, ay, MV_DOWN); /* drop dripping from amoeba */
5092 Feld[ax][ay] = EL_AMOEBA_DROPPING;
5093 Store[ax][ay] = EL_AMOEBA_DROP;
5094 ContinueMoving(ax, ay);
5098 DrawLevelField(newax, neway);
5101 void Life(int ax, int ay)
5104 static int life[4] = { 2, 3, 3, 3 }; /* parameters for "game of life" */
5106 int element = Feld[ax][ay];
5107 int graphic = el2img(element);
5108 boolean changed = FALSE;
5110 if (IS_ANIMATED(graphic))
5111 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
5116 if (!MovDelay[ax][ay]) /* start new "game of life" cycle */
5117 MovDelay[ax][ay] = life_time;
5119 if (MovDelay[ax][ay]) /* wait some time before next cycle */
5122 if (MovDelay[ax][ay])
5126 for (y1=-1; y1<2; y1++) for(x1=-1; x1<2; x1++)
5128 int xx = ax+x1, yy = ay+y1;
5131 if (!IN_LEV_FIELD(xx, yy))
5134 for (y2=-1; y2<2; y2++) for (x2=-1; x2<2; x2++)
5136 int x = xx+x2, y = yy+y2;
5138 if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
5141 if (((Feld[x][y] == element ||
5142 (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y))) &&
5144 (IS_FREE(x, y) && Stop[x][y]))
5148 if (xx == ax && yy == ay) /* field in the middle */
5150 if (nachbarn < life[0] || nachbarn > life[1])
5152 Feld[xx][yy] = EL_EMPTY;
5154 DrawLevelField(xx, yy);
5155 Stop[xx][yy] = TRUE;
5159 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
5160 else if (IS_FREE(xx, yy) || Feld[xx][yy] == EL_SAND)
5161 { /* free border field */
5162 if (nachbarn >= life[2] && nachbarn <= life[3])
5164 Feld[xx][yy] = element;
5165 MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
5167 DrawLevelField(xx, yy);
5168 Stop[xx][yy] = TRUE;
5175 PlaySoundLevel(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
5176 SND_GAME_OF_LIFE_GROWING);
5179 static void InitRobotWheel(int x, int y)
5181 ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
5184 static void RunRobotWheel(int x, int y)
5186 PlaySoundLevel(x, y, SND_ROBOT_WHEEL_ACTIVE);
5189 static void StopRobotWheel(int x, int y)
5191 if (ZX == x && ZY == y)
5195 static void InitTimegateWheel(int x, int y)
5197 ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
5200 static void RunTimegateWheel(int x, int y)
5202 PlaySoundLevel(x, y, SND_TIMEGATE_SWITCH_ACTIVE);
5205 void CheckExit(int x, int y)
5207 if (local_player->gems_still_needed > 0 ||
5208 local_player->sokobanfields_still_needed > 0 ||
5209 local_player->lights_still_needed > 0)
5211 int element = Feld[x][y];
5212 int graphic = el2img(element);
5214 if (IS_ANIMATED(graphic))
5215 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
5220 if (AllPlayersGone) /* do not re-open exit door closed after last player */
5223 Feld[x][y] = EL_EXIT_OPENING;
5225 PlaySoundLevelNearest(x, y, SND_CLASS_EXIT_OPENING);
5228 void CheckExitSP(int x, int y)
5230 if (local_player->gems_still_needed > 0)
5232 int element = Feld[x][y];
5233 int graphic = el2img(element);
5235 if (IS_ANIMATED(graphic))
5236 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
5241 if (AllPlayersGone) /* do not re-open exit door closed after last player */
5244 Feld[x][y] = EL_SP_EXIT_OPENING;
5246 PlaySoundLevelNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
5249 static void CloseAllOpenTimegates()
5253 for (y=0; y<lev_fieldy; y++)
5255 for (x=0; x<lev_fieldx; x++)
5257 int element = Feld[x][y];
5259 if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
5261 Feld[x][y] = EL_TIMEGATE_CLOSING;
5263 PlaySoundLevelAction(x, y, ACTION_CLOSING);
5265 PlaySoundLevel(x, y, SND_TIMEGATE_CLOSING);
5272 void EdelsteinFunkeln(int x, int y)
5274 if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
5277 if (Feld[x][y] == EL_BD_DIAMOND)
5280 if (MovDelay[x][y] == 0) /* next animation frame */
5281 MovDelay[x][y] = 11 * !SimpleRND(500);
5283 if (MovDelay[x][y] != 0) /* wait some time before next frame */
5287 if (setup.direct_draw && MovDelay[x][y])
5288 SetDrawtoField(DRAW_BUFFERED);
5290 DrawLevelElementAnimation(x, y, Feld[x][y]);
5292 if (MovDelay[x][y] != 0)
5294 int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
5295 10 - MovDelay[x][y]);
5297 DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
5299 if (setup.direct_draw)
5303 dest_x = FX + SCREENX(x) * TILEX;
5304 dest_y = FY + SCREENY(y) * TILEY;
5306 BlitBitmap(drawto_field, window,
5307 dest_x, dest_y, TILEX, TILEY, dest_x, dest_y);
5308 SetDrawtoField(DRAW_DIRECT);
5314 void MauerWaechst(int x, int y)
5318 if (!MovDelay[x][y]) /* next animation frame */
5319 MovDelay[x][y] = 3 * delay;
5321 if (MovDelay[x][y]) /* wait some time before next frame */
5325 if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
5327 int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
5328 int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
5330 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5333 if (!MovDelay[x][y])
5335 if (MovDir[x][y] == MV_LEFT)
5337 if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
5338 DrawLevelField(x - 1, y);
5340 else if (MovDir[x][y] == MV_RIGHT)
5342 if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
5343 DrawLevelField(x + 1, y);
5345 else if (MovDir[x][y] == MV_UP)
5347 if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
5348 DrawLevelField(x, y - 1);
5352 if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
5353 DrawLevelField(x, y + 1);
5356 Feld[x][y] = Store[x][y];
5358 GfxDir[x][y] = MovDir[x][y] = MV_NO_MOVING;
5359 DrawLevelField(x, y);
5364 void MauerAbleger(int ax, int ay)
5366 int element = Feld[ax][ay];
5367 int graphic = el2img(element);
5368 boolean oben_frei = FALSE, unten_frei = FALSE;
5369 boolean links_frei = FALSE, rechts_frei = FALSE;
5370 boolean oben_massiv = FALSE, unten_massiv = FALSE;
5371 boolean links_massiv = FALSE, rechts_massiv = FALSE;
5372 boolean new_wall = FALSE;
5374 if (IS_ANIMATED(graphic))
5375 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
5377 if (!MovDelay[ax][ay]) /* start building new wall */
5378 MovDelay[ax][ay] = 6;
5380 if (MovDelay[ax][ay]) /* wait some time before building new wall */
5383 if (MovDelay[ax][ay])
5387 if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
5389 if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
5391 if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
5393 if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
5396 if (element == EL_EXPANDABLE_WALL_VERTICAL ||
5397 element == EL_EXPANDABLE_WALL_ANY)
5401 Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
5402 Store[ax][ay-1] = element;
5403 GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
5404 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
5405 DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
5406 IMG_EXPANDABLE_WALL_GROWING_UP, 0);
5411 Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
5412 Store[ax][ay+1] = element;
5413 GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
5414 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
5415 DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
5416 IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
5421 if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
5422 element == EL_EXPANDABLE_WALL_ANY ||
5423 element == EL_EXPANDABLE_WALL)
5427 Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
5428 Store[ax-1][ay] = element;
5429 GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
5430 if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
5431 DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
5432 IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
5438 Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
5439 Store[ax+1][ay] = element;
5440 GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
5441 if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
5442 DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
5443 IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
5448 if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
5449 DrawLevelField(ax, ay);
5451 if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
5453 if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
5454 unten_massiv = TRUE;
5455 if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
5456 links_massiv = TRUE;
5457 if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
5458 rechts_massiv = TRUE;
5460 if (((oben_massiv && unten_massiv) ||
5461 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
5462 element == EL_EXPANDABLE_WALL) &&
5463 ((links_massiv && rechts_massiv) ||
5464 element == EL_EXPANDABLE_WALL_VERTICAL))
5465 Feld[ax][ay] = EL_WALL;
5469 PlaySoundLevelAction(ax, ay, ACTION_GROWING);
5471 PlaySoundLevel(ax, ay, SND_EXPANDABLE_WALL_GROWING);
5475 void CheckForDragon(int x, int y)
5478 boolean dragon_found = FALSE;
5479 static int xy[4][2] =
5491 int xx = x + j*xy[i][0], yy = y + j*xy[i][1];
5493 if (IN_LEV_FIELD(xx, yy) &&
5494 (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
5496 if (Feld[xx][yy] == EL_DRAGON)
5497 dragon_found = TRUE;
5510 int xx = x + j*xy[i][0], yy = y + j*xy[i][1];
5512 if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
5514 Feld[xx][yy] = EL_EMPTY;
5515 DrawLevelField(xx, yy);
5524 static void InitBuggyBase(int x, int y)
5526 int element = Feld[x][y];
5527 int activating_delay = FRAMES_PER_SECOND / 4;
5530 (element == EL_SP_BUGGY_BASE ?
5531 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
5532 element == EL_SP_BUGGY_BASE_ACTIVATING ?
5534 element == EL_SP_BUGGY_BASE_ACTIVE ?
5535 1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
5538 static void WarnBuggyBase(int x, int y)
5541 static int xy[4][2] =
5551 int xx = x + xy[i][0], yy = y + xy[i][1];
5553 if (IS_PLAYER(xx, yy))
5555 PlaySoundLevel(x, y, SND_SP_BUGGY_BASE_ACTIVE);
5562 static void InitTrap(int x, int y)
5564 ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
5567 static void ActivateTrap(int x, int y)
5569 PlaySoundLevel(x, y, SND_TRAP_ACTIVATING);
5572 static void ChangeActiveTrap(int x, int y)
5574 int graphic = IMG_TRAP_ACTIVE;
5576 /* if new animation frame was drawn, correct crumbled sand border */
5577 if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
5578 DrawLevelFieldCrumbledSand(x, y);
5581 static void ChangeElementNowExt(int x, int y, int target_element)
5583 /* check if element under player changes from accessible to unaccessible
5584 (needed for special case of dropping element which then changes) */
5585 if (IS_PLAYER(x, y) && !PLAYER_PROTECTED(x, y) &&
5586 IS_ACCESSIBLE(Feld[x][y]) && !IS_ACCESSIBLE(target_element))
5593 Feld[x][y] = target_element;
5595 Changed[x][y] |= ChangeEvent[x][y]; /* ignore same changes in this frame */
5597 ResetGfxAnimation(x, y);
5598 ResetRandomAnimationValue(x, y);
5600 InitField(x, y, FALSE);
5601 if (CAN_MOVE(Feld[x][y]))
5604 DrawLevelField(x, y);
5606 if (GFX_CRUMBLED(Feld[x][y]))
5607 DrawLevelFieldCrumbledSandNeighbours(x, y);
5609 TestIfBadThingTouchesHero(x, y);
5610 TestIfPlayerTouchesCustomElement(x, y);
5611 TestIfElementTouchesCustomElement(x, y);
5613 if (ELEM_IS_PLAYER(target_element))
5614 RelocatePlayer(x, y, target_element);
5617 static boolean ChangeElementNow(int x, int y, int element, int page)
5619 struct ElementChangeInfo *change = &element_info[element].change_page[page];
5621 /* always use default change event to prevent running into a loop */
5622 if (ChangeEvent[x][y] == CE_BITMASK_DEFAULT)
5623 ChangeEvent[x][y] = CH_EVENT_BIT(CE_DELAY);
5625 /* do not change already changed elements with same change event */
5627 if (Changed[x][y] & ChangeEvent[x][y])
5634 Changed[x][y] |= ChangeEvent[x][y]; /* ignore same changes in this frame */
5636 CheckTriggeredElementChange(x, y, Feld[x][y], CE_OTHER_IS_CHANGING);
5638 if (change->explode)
5645 if (change->use_content)
5647 boolean complete_change = TRUE;
5648 boolean can_change[3][3];
5651 for (yy = 0; yy < 3; yy++) for(xx = 0; xx < 3 ; xx++)
5653 boolean half_destructible;
5654 int ex = x + xx - 1;
5655 int ey = y + yy - 1;
5658 can_change[xx][yy] = TRUE;
5660 if (ex == x && ey == y) /* do not check changing element itself */
5663 if (change->content[xx][yy] == EL_EMPTY_SPACE)
5665 can_change[xx][yy] = FALSE; /* do not change empty borders */
5670 if (!IN_LEV_FIELD(ex, ey))
5672 can_change[xx][yy] = FALSE;
5673 complete_change = FALSE;
5680 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
5681 e = MovingOrBlocked2Element(ex, ey);
5683 half_destructible = (IS_FREE(ex, ey) || IS_DIGGABLE(e));
5685 if ((change->power <= CP_NON_DESTRUCTIVE && !IS_FREE(ex, ey)) ||
5686 (change->power <= CP_HALF_DESTRUCTIVE && !half_destructible) ||
5687 (change->power <= CP_FULL_DESTRUCTIVE && IS_INDESTRUCTIBLE(e)))
5689 can_change[xx][yy] = FALSE;
5690 complete_change = FALSE;
5694 if (!change->only_complete || complete_change)
5696 boolean something_has_changed = FALSE;
5698 if (change->only_complete && change->use_random_change &&
5699 RND(100) < change->random)
5702 for (yy = 0; yy < 3; yy++) for(xx = 0; xx < 3 ; xx++)
5704 int ex = x + xx - 1;
5705 int ey = y + yy - 1;
5707 if (can_change[xx][yy] && (!change->use_random_change ||
5708 RND(100) < change->random))
5710 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
5711 RemoveMovingField(ex, ey);
5713 ChangeEvent[ex][ey] = ChangeEvent[x][y];
5715 ChangeElementNowExt(ex, ey, change->content[xx][yy]);
5717 something_has_changed = TRUE;
5719 /* for symmetry reasons, freeze newly created border elements */
5720 if (ex != x || ey != y)
5721 Stop[ex][ey] = TRUE; /* no more moving in this frame */
5725 if (something_has_changed)
5726 PlaySoundLevelElementAction(x, y, element, ACTION_CHANGING);
5731 ChangeElementNowExt(x, y, change->target_element);
5733 PlaySoundLevelElementAction(x, y, element, ACTION_CHANGING);
5739 static void ChangeElement(int x, int y, int page)
5741 int element = MovingOrBlocked2Element(x, y);
5742 struct ElementInfo *ei = &element_info[element];
5743 struct ElementChangeInfo *change = &ei->change_page[page];
5747 if (!CAN_CHANGE(element))
5750 printf("ChangeElement(): %d,%d: element = %d ('%s')\n",
5751 x, y, element, element_info[element].token_name);
5752 printf("ChangeElement(): This should never happen!\n");
5758 if (ChangeDelay[x][y] == 0) /* initialize element change */
5760 ChangeDelay[x][y] = ( change->delay_fixed * change->delay_frames +
5761 RND(change->delay_random * change->delay_frames)) + 1;
5763 ResetGfxAnimation(x, y);
5764 ResetRandomAnimationValue(x, y);
5766 if (change->pre_change_function)
5767 change->pre_change_function(x, y);
5770 ChangeDelay[x][y]--;
5772 if (ChangeDelay[x][y] != 0) /* continue element change */
5774 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
5776 if (IS_ANIMATED(graphic))
5777 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
5779 if (change->change_function)
5780 change->change_function(x, y);
5782 else /* finish element change */
5784 if (ChangePage[x][y] != -1) /* remember page from delayed change */
5786 page = ChangePage[x][y];
5787 ChangePage[x][y] = -1;
5790 if (IS_MOVING(x, y)) /* never change a running system ;-) */
5792 ChangeDelay[x][y] = 1; /* try change after next move step */
5793 ChangePage[x][y] = page; /* remember page to use for change */
5798 if (ChangeElementNow(x, y, element, page))
5800 if (change->post_change_function)
5801 change->post_change_function(x, y);
5806 static boolean CheckTriggeredElementSideChange(int lx, int ly,
5807 int trigger_element,
5813 if (!(trigger_events[trigger_element] & CH_EVENT_BIT(trigger_event)))
5816 for (i=0; i < NUM_CUSTOM_ELEMENTS; i++)
5818 int element = EL_CUSTOM_START + i;
5820 boolean change_element = FALSE;
5823 if (!CAN_CHANGE(element) || !HAS_ANY_CHANGE_EVENT(element, trigger_event))
5826 for (j=0; j < element_info[element].num_change_pages; j++)
5828 struct ElementChangeInfo *change = &element_info[element].change_page[j];
5830 if (change->can_change &&
5832 change->events & CH_EVENT_BIT(trigger_event) &&
5834 change->sides & trigger_side &&
5835 change->trigger_element == trigger_element)
5838 if (!(change->events & CH_EVENT_BIT(trigger_event)))
5839 printf("::: !!! %d triggers %d: using wrong page %d [event %d]\n",
5840 trigger_element-EL_CUSTOM_START+1, i+1, j, trigger_event);
5843 change_element = TRUE;
5850 if (!change_element)
5853 for (y=0; y<lev_fieldy; y++) for (x=0; x<lev_fieldx; x++)
5856 if (x == lx && y == ly) /* do not change trigger element itself */
5860 if (Feld[x][y] == element)
5862 ChangeDelay[x][y] = 1;
5863 ChangeEvent[x][y] = CH_EVENT_BIT(trigger_event);
5864 ChangeElement(x, y, page);
5872 static boolean CheckTriggeredElementChange(int lx, int ly, int trigger_element,
5875 return CheckTriggeredElementSideChange(lx, ly, trigger_element, CH_SIDE_ANY,
5879 static boolean CheckElementSideChange(int x, int y, int element, int side,
5880 int trigger_event, int page)
5882 if (!CAN_CHANGE(element) || !HAS_ANY_CHANGE_EVENT(element, trigger_event))
5885 if (Feld[x][y] == EL_BLOCKED)
5887 Blocked2Moving(x, y, &x, &y);
5888 element = Feld[x][y];
5892 page = element_info[element].event_page_nr[trigger_event];
5894 if (!(element_info[element].change_page[page].sides & side))
5897 ChangeDelay[x][y] = 1;
5898 ChangeEvent[x][y] = CH_EVENT_BIT(trigger_event);
5899 ChangeElement(x, y, page);
5904 static boolean CheckElementChange(int x, int y, int element, int trigger_event)
5906 return CheckElementSideChange(x, y, element, CH_SIDE_ANY, trigger_event, -1);
5910 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
5913 static byte stored_player_action[MAX_PLAYERS];
5914 static int num_stored_actions = 0;
5916 boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
5917 int left = player_action & JOY_LEFT;
5918 int right = player_action & JOY_RIGHT;
5919 int up = player_action & JOY_UP;
5920 int down = player_action & JOY_DOWN;
5921 int button1 = player_action & JOY_BUTTON_1;
5922 int button2 = player_action & JOY_BUTTON_2;
5923 int dx = (left ? -1 : right ? 1 : 0);
5924 int dy = (up ? -1 : down ? 1 : 0);
5927 stored_player_action[player->index_nr] = 0;
5928 num_stored_actions++;
5932 printf("::: player %d [%d]\n", player->index_nr, FrameCounter);
5935 if (!player->active || tape.pausing)
5941 printf("::: player %d acts [%d]\n", player->index_nr, FrameCounter);
5945 snapped = SnapField(player, dx, dy);
5949 dropped = DropElement(player);
5951 moved = MovePlayer(player, dx, dy);
5954 if (tape.single_step && tape.recording && !tape.pausing)
5956 if (button1 || (dropped && !moved))
5958 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
5959 SnapField(player, 0, 0); /* stop snapping */
5964 return player_action;
5966 stored_player_action[player->index_nr] = player_action;
5972 printf("::: player %d waits [%d]\n", player->index_nr, FrameCounter);
5975 /* no actions for this player (no input at player's configured device) */
5977 DigField(player, 0, 0, 0, 0, DF_NO_PUSH);
5978 SnapField(player, 0, 0);
5979 CheckGravityMovement(player);
5981 if (player->MovPos == 0)
5982 InitPlayerGfxAnimation(player, ACTION_DEFAULT, player->MovDir);
5984 if (player->MovPos == 0) /* needed for tape.playing */
5985 player->is_moving = FALSE;
5991 if (tape.recording && num_stored_actions >= MAX_PLAYERS)
5993 printf("::: player %d recorded [%d]\n", player->index_nr, FrameCounter);
5995 TapeRecordAction(stored_player_action);
5996 num_stored_actions = 0;
6003 static void PlayerActions(struct PlayerInfo *player, byte player_action)
6005 static byte stored_player_action[MAX_PLAYERS];
6006 static int num_stored_actions = 0;
6007 boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
6008 int left = player_action & JOY_LEFT;
6009 int right = player_action & JOY_RIGHT;
6010 int up = player_action & JOY_UP;
6011 int down = player_action & JOY_DOWN;
6012 int button1 = player_action & JOY_BUTTON_1;
6013 int button2 = player_action & JOY_BUTTON_2;
6014 int dx = (left ? -1 : right ? 1 : 0);
6015 int dy = (up ? -1 : down ? 1 : 0);
6017 stored_player_action[player->index_nr] = 0;
6018 num_stored_actions++;
6020 printf("::: player %d [%d]\n", player->index_nr, FrameCounter);
6022 if (!player->active || tape.pausing)
6027 printf("::: player %d acts [%d]\n", player->index_nr, FrameCounter);
6030 snapped = SnapField(player, dx, dy);
6034 dropped = DropElement(player);
6036 moved = MovePlayer(player, dx, dy);
6039 if (tape.single_step && tape.recording && !tape.pausing)
6041 if (button1 || (dropped && !moved))
6043 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
6044 SnapField(player, 0, 0); /* stop snapping */
6048 stored_player_action[player->index_nr] = player_action;
6052 printf("::: player %d waits [%d]\n", player->index_nr, FrameCounter);
6054 /* no actions for this player (no input at player's configured device) */
6056 DigField(player, 0, 0, 0, 0, DF_NO_PUSH);
6057 SnapField(player, 0, 0);
6058 CheckGravityMovement(player);
6060 if (player->MovPos == 0)
6061 InitPlayerGfxAnimation(player, ACTION_DEFAULT, player->MovDir);
6063 if (player->MovPos == 0) /* needed for tape.playing */
6064 player->is_moving = FALSE;
6067 if (tape.recording && num_stored_actions >= MAX_PLAYERS)
6069 printf("::: player %d recorded [%d]\n", player->index_nr, FrameCounter);
6071 TapeRecordAction(stored_player_action);
6072 num_stored_actions = 0;
6079 static unsigned long action_delay = 0;
6080 unsigned long action_delay_value;
6081 int magic_wall_x = 0, magic_wall_y = 0;
6082 int i, x, y, element, graphic;
6083 byte *recorded_player_action;
6084 byte summarized_player_action = 0;
6086 byte tape_action[MAX_PLAYERS];
6089 if (game_status != GAME_MODE_PLAYING)
6092 action_delay_value =
6093 (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
6095 if (tape.playing && tape.index_search && !tape.pausing)
6096 action_delay_value = 0;
6098 /* ---------- main game synchronization point ---------- */
6100 WaitUntilDelayReached(&action_delay, action_delay_value);
6102 if (network_playing && !network_player_action_received)
6106 printf("DEBUG: try to get network player actions in time\n");
6110 #if defined(PLATFORM_UNIX)
6111 /* last chance to get network player actions without main loop delay */
6115 if (game_status != GAME_MODE_PLAYING)
6118 if (!network_player_action_received)
6122 printf("DEBUG: failed to get network player actions in time\n");
6132 recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
6134 for (i=0; i<MAX_PLAYERS; i++)
6136 summarized_player_action |= stored_player[i].action;
6138 if (!network_playing)
6139 stored_player[i].effective_action = stored_player[i].action;
6142 #if defined(PLATFORM_UNIX)
6143 if (network_playing)
6144 SendToServer_MovePlayer(summarized_player_action);
6147 if (!options.network && !setup.team_mode)
6148 local_player->effective_action = summarized_player_action;
6150 for (i=0; i < MAX_PLAYERS; i++)
6152 int actual_player_action = stored_player[i].effective_action;
6154 if (stored_player[i].programmed_action)
6155 actual_player_action = stored_player[i].programmed_action;
6157 if (recorded_player_action)
6158 actual_player_action = recorded_player_action[i];
6160 tape_action[i] = PlayerActions(&stored_player[i], actual_player_action);
6162 ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
6167 TapeRecordAction(tape_action);
6170 network_player_action_received = FALSE;
6172 ScrollScreen(NULL, SCROLL_GO_ON);
6178 for (i=0; i<MAX_PLAYERS; i++)
6179 stored_player[i].Frame++;
6183 if (game.engine_version < VERSION_IDENT(2,2,0,7))
6185 for (i=0; i<MAX_PLAYERS; i++)
6187 struct PlayerInfo *player = &stored_player[i];
6191 if (player->active && player->is_pushing && player->is_moving &&
6194 ContinueMoving(x, y);
6196 /* continue moving after pushing (this is actually a bug) */
6197 if (!IS_MOVING(x, y))
6206 for (y=0; y<lev_fieldy; y++) for (x=0; x<lev_fieldx; x++)
6208 Changed[x][y] = CE_BITMASK_DEFAULT;
6209 ChangeEvent[x][y] = CE_BITMASK_DEFAULT;
6212 if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
6214 printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
6215 printf("GameActions(): This should never happen!\n");
6217 ChangePage[x][y] = -1;
6222 if (WasJustMoving[x][y] > 0)
6223 WasJustMoving[x][y]--;
6224 if (WasJustFalling[x][y] > 0)
6225 WasJustFalling[x][y]--;
6230 /* reset finished pushing action (not done in ContinueMoving() to allow
6231 continous pushing animation for elements with zero push delay) */
6232 if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
6234 ResetGfxAnimation(x, y);
6235 DrawLevelField(x, y);
6240 if (IS_BLOCKED(x, y))
6244 Blocked2Moving(x, y, &oldx, &oldy);
6245 if (!IS_MOVING(oldx, oldy))
6247 printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
6248 printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
6249 printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
6250 printf("GameActions(): This should never happen!\n");
6256 for (y=0; y<lev_fieldy; y++) for (x=0; x<lev_fieldx; x++)
6258 element = Feld[x][y];
6260 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
6262 graphic = el2img(element);
6268 printf("::: %d,%d: %d [%d]\n", x, y, element, FrameCounter);
6270 element = graphic = 0;
6274 if (graphic_info[graphic].anim_global_sync)
6275 GfxFrame[x][y] = FrameCounter;
6277 if (ANIM_MODE(graphic) == ANIM_RANDOM &&
6278 IS_NEXT_FRAME(GfxFrame[x][y], graphic))
6279 ResetRandomAnimationValue(x, y);
6281 SetRandomAnimationValue(x, y);
6284 PlaySoundLevelActionIfLoop(x, y, GfxAction[x][y]);
6287 if (IS_INACTIVE(element))
6289 if (IS_ANIMATED(graphic))
6290 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
6296 /* this may take place after moving, so 'element' may have changed */
6298 if (IS_CHANGING(x, y))
6300 if (IS_CHANGING(x, y) &&
6301 (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
6305 ChangeElement(x, y, ChangePage[x][y] != -1 ? ChangePage[x][y] :
6306 element_info[element].event_page_nr[CE_DELAY]);
6308 ChangeElement(x, y, element_info[element].event_page_nr[CE_DELAY]);
6311 element = Feld[x][y];
6312 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
6316 if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
6321 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
6323 if (element == EL_MOLE)
6324 printf("::: %d, %d, %d [%d]\n",
6325 IS_ANIMATED(graphic), IS_MOVING(x, y), Stop[x][y],
6329 if (element == EL_YAMYAM)
6330 printf("::: %d, %d, %d\n",
6331 IS_ANIMATED(graphic), IS_MOVING(x, y), Stop[x][y]);
6335 if (IS_ANIMATED(graphic) &&
6339 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
6342 if (element == EL_BUG)
6343 printf("::: %d, %d\n", graphic, GfxFrame[x][y]);
6347 if (element == EL_MOLE)
6348 printf("::: %d, %d\n", graphic, GfxFrame[x][y]);
6352 if (IS_GEM(element) || element == EL_SP_INFOTRON)
6353 EdelsteinFunkeln(x, y);
6355 else if ((element == EL_ACID ||
6356 element == EL_EXIT_OPEN ||
6357 element == EL_SP_EXIT_OPEN ||
6358 element == EL_SP_TERMINAL ||
6359 element == EL_SP_TERMINAL_ACTIVE ||
6360 element == EL_EXTRA_TIME ||
6361 element == EL_SHIELD_NORMAL ||
6362 element == EL_SHIELD_DEADLY) &&
6363 IS_ANIMATED(graphic))
6364 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
6365 else if (IS_MOVING(x, y))
6366 ContinueMoving(x, y);
6367 else if (IS_ACTIVE_BOMB(element))
6368 CheckDynamite(x, y);
6370 else if (element == EL_EXPLOSION && !game.explosions_delayed)
6371 Explode(x, y, ExplodePhase[x][y], EX_NORMAL);
6373 else if (element == EL_AMOEBA_GROWING)
6374 AmoebeWaechst(x, y);
6375 else if (element == EL_AMOEBA_SHRINKING)
6376 AmoebaDisappearing(x, y);
6378 #if !USE_NEW_AMOEBA_CODE
6379 else if (IS_AMOEBALIVE(element))
6380 AmoebeAbleger(x, y);
6383 else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
6385 else if (element == EL_EXIT_CLOSED)
6387 else if (element == EL_SP_EXIT_CLOSED)
6389 else if (element == EL_EXPANDABLE_WALL_GROWING)
6391 else if (element == EL_EXPANDABLE_WALL ||
6392 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
6393 element == EL_EXPANDABLE_WALL_VERTICAL ||
6394 element == EL_EXPANDABLE_WALL_ANY)
6396 else if (element == EL_FLAMES)
6397 CheckForDragon(x, y);
6399 else if (IS_AUTO_CHANGING(element))
6400 ChangeElement(x, y);
6402 else if (element == EL_EXPLOSION)
6403 ; /* drawing of correct explosion animation is handled separately */
6404 else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
6405 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
6408 /* this may take place after moving, so 'element' may have changed */
6409 if (IS_AUTO_CHANGING(Feld[x][y]))
6410 ChangeElement(x, y);
6413 if (IS_BELT_ACTIVE(element))
6414 PlaySoundLevelAction(x, y, ACTION_ACTIVE);
6416 if (game.magic_wall_active)
6418 int jx = local_player->jx, jy = local_player->jy;
6420 /* play the element sound at the position nearest to the player */
6421 if ((element == EL_MAGIC_WALL_FULL ||
6422 element == EL_MAGIC_WALL_ACTIVE ||
6423 element == EL_MAGIC_WALL_EMPTYING ||
6424 element == EL_BD_MAGIC_WALL_FULL ||
6425 element == EL_BD_MAGIC_WALL_ACTIVE ||
6426 element == EL_BD_MAGIC_WALL_EMPTYING) &&
6427 ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
6435 #if USE_NEW_AMOEBA_CODE
6436 /* new experimental amoeba growth stuff */
6438 if (!(FrameCounter % 8))
6441 static unsigned long random = 1684108901;
6443 for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
6446 x = (random >> 10) % lev_fieldx;
6447 y = (random >> 20) % lev_fieldy;
6449 x = RND(lev_fieldx);
6450 y = RND(lev_fieldy);
6452 element = Feld[x][y];
6454 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
6455 if (!IS_PLAYER(x,y) &&
6456 (element == EL_EMPTY ||
6457 element == EL_SAND ||
6458 element == EL_QUICKSAND_EMPTY ||
6459 element == EL_ACID_SPLASH_LEFT ||
6460 element == EL_ACID_SPLASH_RIGHT))
6462 if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
6463 (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
6464 (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
6465 (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
6466 Feld[x][y] = EL_AMOEBA_DROP;
6469 random = random * 129 + 1;
6475 if (game.explosions_delayed)
6478 game.explosions_delayed = FALSE;
6480 for (y=0; y<lev_fieldy; y++) for (x=0; x<lev_fieldx; x++)
6482 element = Feld[x][y];
6484 if (ExplodeField[x][y])
6485 Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
6486 else if (element == EL_EXPLOSION)
6487 Explode(x, y, ExplodePhase[x][y], EX_NORMAL);
6489 ExplodeField[x][y] = EX_NO_EXPLOSION;
6492 game.explosions_delayed = TRUE;
6495 if (game.magic_wall_active)
6497 if (!(game.magic_wall_time_left % 4))
6499 int element = Feld[magic_wall_x][magic_wall_y];
6501 if (element == EL_BD_MAGIC_WALL_FULL ||
6502 element == EL_BD_MAGIC_WALL_ACTIVE ||
6503 element == EL_BD_MAGIC_WALL_EMPTYING)
6504 PlaySoundLevel(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
6506 PlaySoundLevel(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
6509 if (game.magic_wall_time_left > 0)
6511 game.magic_wall_time_left--;
6512 if (!game.magic_wall_time_left)
6514 for (y=0; y<lev_fieldy; y++) for (x=0; x<lev_fieldx; x++)
6516 element = Feld[x][y];
6518 if (element == EL_MAGIC_WALL_ACTIVE ||
6519 element == EL_MAGIC_WALL_FULL)
6521 Feld[x][y] = EL_MAGIC_WALL_DEAD;
6522 DrawLevelField(x, y);
6524 else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
6525 element == EL_BD_MAGIC_WALL_FULL)
6527 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
6528 DrawLevelField(x, y);
6532 game.magic_wall_active = FALSE;
6537 if (game.light_time_left > 0)
6539 game.light_time_left--;
6541 if (game.light_time_left == 0)
6542 RedrawAllLightSwitchesAndInvisibleElements();
6545 if (game.timegate_time_left > 0)
6547 game.timegate_time_left--;
6549 if (game.timegate_time_left == 0)
6550 CloseAllOpenTimegates();
6553 for (i=0; i<MAX_PLAYERS; i++)
6555 struct PlayerInfo *player = &stored_player[i];
6557 if (SHIELD_ON(player))
6559 if (player->shield_deadly_time_left)
6560 PlaySoundLevel(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
6561 else if (player->shield_normal_time_left)
6562 PlaySoundLevel(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
6566 if (TimeFrames >= FRAMES_PER_SECOND)
6571 for (i=0; i<MAX_PLAYERS; i++)
6573 struct PlayerInfo *player = &stored_player[i];
6575 if (SHIELD_ON(player))
6577 player->shield_normal_time_left--;
6579 if (player->shield_deadly_time_left > 0)
6580 player->shield_deadly_time_left--;
6584 if (tape.recording || tape.playing)
6585 DrawVideoDisplay(VIDEO_STATE_TIME_ON, TimePlayed);
6591 if (TimeLeft <= 10 && setup.time_limit)
6592 PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, SOUND_MIDDLE);
6594 DrawText(DX_TIME, DY_TIME, int2str(TimeLeft, 3), FONT_TEXT_2);
6596 if (!TimeLeft && setup.time_limit)
6597 for (i=0; i<MAX_PLAYERS; i++)
6598 KillHero(&stored_player[i]);
6600 else if (level.time == 0 && !AllPlayersGone) /* level without time limit */
6601 DrawText(DX_TIME, DY_TIME, int2str(TimePlayed, 3), FONT_TEXT_2);
6606 if (options.debug) /* calculate frames per second */
6608 static unsigned long fps_counter = 0;
6609 static int fps_frames = 0;
6610 unsigned long fps_delay_ms = Counter() - fps_counter;
6614 if (fps_delay_ms >= 500) /* calculate fps every 0.5 seconds */
6616 global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
6619 fps_counter = Counter();
6622 redraw_mask |= REDRAW_FPS;
6626 if (stored_player[0].jx != stored_player[0].last_jx ||
6627 stored_player[0].jy != stored_player[0].last_jy)
6628 printf("::: %d, %d, %d, %d, %d\n",
6629 stored_player[0].MovDir,
6630 stored_player[0].MovPos,
6631 stored_player[0].GfxPos,
6632 stored_player[0].Frame,
6633 stored_player[0].StepFrame);
6640 for (i=0; i<MAX_PLAYERS; i++)
6643 MOVE_DELAY_NORMAL_SPEED / stored_player[i].move_delay_value;
6645 stored_player[i].Frame += move_frames;
6647 if (stored_player[i].MovPos != 0)
6648 stored_player[i].StepFrame += move_frames;
6653 if (local_player->show_envelope != 0 && local_player->MovPos == 0)
6655 ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
6657 local_player->show_envelope = 0;
6662 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
6664 int min_x = x, min_y = y, max_x = x, max_y = y;
6667 for (i=0; i<MAX_PLAYERS; i++)
6669 int jx = stored_player[i].jx, jy = stored_player[i].jy;
6671 if (!stored_player[i].active || &stored_player[i] == player)
6674 min_x = MIN(min_x, jx);
6675 min_y = MIN(min_y, jy);
6676 max_x = MAX(max_x, jx);
6677 max_y = MAX(max_y, jy);
6680 return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
6683 static boolean AllPlayersInVisibleScreen()
6687 for (i=0; i<MAX_PLAYERS; i++)
6689 int jx = stored_player[i].jx, jy = stored_player[i].jy;
6691 if (!stored_player[i].active)
6694 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
6701 void ScrollLevel(int dx, int dy)
6703 int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
6706 BlitBitmap(drawto_field, drawto_field,
6707 FX + TILEX * (dx == -1) - softscroll_offset,
6708 FY + TILEY * (dy == -1) - softscroll_offset,
6709 SXSIZE - TILEX * (dx!=0) + 2 * softscroll_offset,
6710 SYSIZE - TILEY * (dy!=0) + 2 * softscroll_offset,
6711 FX + TILEX * (dx == 1) - softscroll_offset,
6712 FY + TILEY * (dy == 1) - softscroll_offset);
6716 x = (dx == 1 ? BX1 : BX2);
6717 for (y=BY1; y <= BY2; y++)
6718 DrawScreenField(x, y);
6723 y = (dy == 1 ? BY1 : BY2);
6724 for (x=BX1; x <= BX2; x++)
6725 DrawScreenField(x, y);
6728 redraw_mask |= REDRAW_FIELD;
6731 static void CheckGravityMovement(struct PlayerInfo *player)
6733 if (game.gravity && !player->programmed_action)
6735 int move_dir_vertical = player->action & (MV_UP | MV_DOWN);
6736 int move_dir_horizontal = player->action & (MV_LEFT | MV_RIGHT);
6738 (player->last_move_dir & (MV_LEFT | MV_RIGHT) ?
6739 (move_dir_vertical ? move_dir_vertical : move_dir_horizontal) :
6740 (move_dir_horizontal ? move_dir_horizontal : move_dir_vertical));
6741 int jx = player->jx, jy = player->jy;
6742 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
6743 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
6744 int new_jx = jx + dx, new_jy = jy + dy;
6745 boolean field_under_player_is_free =
6746 (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
6747 boolean player_is_moving_to_valid_field =
6748 (IN_LEV_FIELD(new_jx, new_jy) &&
6749 (Feld[new_jx][new_jy] == EL_SP_BASE ||
6750 Feld[new_jx][new_jy] == EL_SAND));
6751 /* !!! extend EL_SAND to anything diggable !!! */
6753 if (field_under_player_is_free &&
6754 !player_is_moving_to_valid_field &&
6755 !IS_WALKABLE_INSIDE(Feld[jx][jy]))
6756 player->programmed_action = MV_DOWN;
6762 -----------------------------------------------------------------------------
6763 dx, dy: direction (non-diagonal) to try to move the player to
6764 real_dx, real_dy: direction as read from input device (can be diagonal)
6767 boolean MovePlayerOneStep(struct PlayerInfo *player,
6768 int dx, int dy, int real_dx, int real_dy)
6771 static int change_sides[4][2] =
6773 /* enter side leave side */
6774 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* moving left */
6775 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* moving right */
6776 { CH_SIDE_BOTTOM, CH_SIDE_TOP }, /* moving up */
6777 { CH_SIDE_TOP, CH_SIDE_BOTTOM } /* moving down */
6779 int move_direction = (dx == -1 ? MV_LEFT :
6780 dx == +1 ? MV_RIGHT :
6782 dy == +1 ? MV_DOWN : MV_NO_MOVING);
6783 int enter_side = change_sides[MV_DIR_BIT(move_direction)][0];
6784 int leave_side = change_sides[MV_DIR_BIT(move_direction)][1];
6786 int jx = player->jx, jy = player->jy;
6787 int new_jx = jx + dx, new_jy = jy + dy;
6791 if (!player->active || (!dx && !dy))
6792 return MF_NO_ACTION;
6794 player->MovDir = (dx < 0 ? MV_LEFT :
6797 dy > 0 ? MV_DOWN : MV_NO_MOVING);
6799 if (!IN_LEV_FIELD(new_jx, new_jy))
6800 return MF_NO_ACTION;
6802 if (!options.network && !AllPlayersInSight(player, new_jx, new_jy))
6803 return MF_NO_ACTION;
6806 element = MovingOrBlocked2Element(new_jx, new_jy);
6808 element = MovingOrBlocked2ElementIfNotLeaving(new_jx, new_jy);
6811 if (DONT_RUN_INTO(element))
6813 if (element == EL_ACID && dx == 0 && dy == 1)
6816 Feld[jx][jy] = EL_PLAYER_1;
6817 InitMovingField(jx, jy, MV_DOWN);
6818 Store[jx][jy] = EL_ACID;
6819 ContinueMoving(jx, jy);
6823 TestIfHeroRunsIntoBadThing(jx, jy, player->MovDir);
6828 can_move = DigField(player, new_jx, new_jy, real_dx, real_dy, DF_DIG);
6829 if (can_move != MF_MOVING)
6832 /* check if DigField() has caused relocation of the player */
6833 if (player->jx != jx || player->jy != jy)
6834 return MF_NO_ACTION;
6836 StorePlayer[jx][jy] = 0;
6837 player->last_jx = jx;
6838 player->last_jy = jy;
6839 player->jx = new_jx;
6840 player->jy = new_jy;
6841 StorePlayer[new_jx][new_jy] = player->element_nr;
6844 (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
6846 ScrollPlayer(player, SCROLL_INIT);
6849 if (IS_CUSTOM_ELEMENT(Feld[jx][jy]))
6851 CheckTriggeredElementSideChange(jx, jy, Feld[jx][jy], leave_side,
6852 CE_OTHER_GETS_LEFT);
6853 CheckElementSideChange(jx, jy, Feld[jx][jy], leave_side,
6854 CE_LEFT_BY_PLAYER, -1);
6857 if (IS_CUSTOM_ELEMENT(Feld[new_jx][new_jy]))
6859 CheckTriggeredElementSideChange(new_jx, new_jy, Feld[new_jx][new_jy],
6860 enter_side, CE_OTHER_GETS_ENTERED);
6861 CheckElementSideChange(new_jx, new_jy, Feld[new_jx][new_jy], enter_side,
6862 CE_ENTERED_BY_PLAYER, -1);
6869 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
6871 int jx = player->jx, jy = player->jy;
6872 int old_jx = jx, old_jy = jy;
6873 int moved = MF_NO_ACTION;
6875 if (!player->active || (!dx && !dy))
6879 if (!FrameReached(&player->move_delay, player->move_delay_value) &&
6883 if (!FrameReached(&player->move_delay, player->move_delay_value) &&
6884 !(tape.playing && tape.file_version < FILE_VERSION_2_0))
6888 /* remove the last programmed player action */
6889 player->programmed_action = 0;
6893 /* should only happen if pre-1.2 tape recordings are played */
6894 /* this is only for backward compatibility */
6896 int original_move_delay_value = player->move_delay_value;
6899 printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%ld]\n",
6903 /* scroll remaining steps with finest movement resolution */
6904 player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
6906 while (player->MovPos)
6908 ScrollPlayer(player, SCROLL_GO_ON);
6909 ScrollScreen(NULL, SCROLL_GO_ON);
6915 player->move_delay_value = original_move_delay_value;
6918 if (player->last_move_dir & (MV_LEFT | MV_RIGHT))
6920 if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
6921 moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
6925 if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
6926 moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
6932 if (moved & MF_MOVING && !ScreenMovPos &&
6933 (player == local_player || !options.network))
6935 int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
6936 int offset = (setup.scroll_delay ? 3 : 0);
6938 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
6940 /* actual player has left the screen -- scroll in that direction */
6941 if (jx != old_jx) /* player has moved horizontally */
6942 scroll_x += (jx - old_jx);
6943 else /* player has moved vertically */
6944 scroll_y += (jy - old_jy);
6948 if (jx != old_jx) /* player has moved horizontally */
6950 if ((player->MovDir == MV_LEFT && scroll_x > jx - MIDPOSX + offset) ||
6951 (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
6952 scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
6954 /* don't scroll over playfield boundaries */
6955 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
6956 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
6958 /* don't scroll more than one field at a time */
6959 scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
6961 /* don't scroll against the player's moving direction */
6962 if ((player->MovDir == MV_LEFT && scroll_x > old_scroll_x) ||
6963 (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
6964 scroll_x = old_scroll_x;
6966 else /* player has moved vertically */
6968 if ((player->MovDir == MV_UP && scroll_y > jy - MIDPOSY + offset) ||
6969 (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
6970 scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
6972 /* don't scroll over playfield boundaries */
6973 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
6974 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
6976 /* don't scroll more than one field at a time */
6977 scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
6979 /* don't scroll against the player's moving direction */
6980 if ((player->MovDir == MV_UP && scroll_y > old_scroll_y) ||
6981 (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
6982 scroll_y = old_scroll_y;
6986 if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
6988 if (!options.network && !AllPlayersInVisibleScreen())
6990 scroll_x = old_scroll_x;
6991 scroll_y = old_scroll_y;
6995 ScrollScreen(player, SCROLL_INIT);
6996 ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
7003 InitPlayerGfxAnimation(player, ACTION_DEFAULT);
7005 if (!(moved & MF_MOVING) && !player->is_pushing)
7010 player->StepFrame = 0;
7012 if (moved & MF_MOVING)
7014 if (old_jx != jx && old_jy == jy)
7015 player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
7016 else if (old_jx == jx && old_jy != jy)
7017 player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
7019 DrawLevelField(jx, jy); /* for "crumbled sand" */
7021 player->last_move_dir = player->MovDir;
7022 player->is_moving = TRUE;
7024 player->is_snapping = FALSE;
7028 player->is_switching = FALSE;
7034 static int change_sides[4][2] =
7036 /* enter side leave side */
7037 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* moving left */
7038 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* moving right */
7039 { CH_SIDE_BOTTOM, CH_SIDE_TOP }, /* moving up */
7040 { CH_SIDE_TOP, CH_SIDE_BOTTOM } /* moving down */
7042 int move_direction = player->MovDir;
7043 int enter_side = change_sides[MV_DIR_BIT(move_direction)][0];
7044 int leave_side = change_sides[MV_DIR_BIT(move_direction)][1];
7047 if (IS_CUSTOM_ELEMENT(Feld[old_jx][old_jy]))
7049 CheckTriggeredElementSideChange(old_jx, old_jy, Feld[old_jx][old_jy],
7050 leave_side, CE_OTHER_GETS_LEFT);
7051 CheckElementSideChange(old_jx, old_jy, Feld[old_jx][old_jy],
7052 leave_side, CE_LEFT_BY_PLAYER, -1);
7055 if (IS_CUSTOM_ELEMENT(Feld[jx][jy]))
7057 CheckTriggeredElementSideChange(jx, jy, Feld[jx][jy],
7058 enter_side, CE_OTHER_GETS_ENTERED);
7059 CheckElementSideChange(jx, jy, Feld[jx][jy],
7060 enter_side, CE_ENTERED_BY_PLAYER, -1);
7071 CheckGravityMovement(player);
7074 player->last_move_dir = MV_NO_MOVING;
7076 player->is_moving = FALSE;
7079 if (game.engine_version < VERSION_IDENT(3,0,7,0))
7081 TestIfHeroTouchesBadThing(jx, jy);
7082 TestIfPlayerTouchesCustomElement(jx, jy);
7085 if (!player->active)
7091 void ScrollPlayer(struct PlayerInfo *player, int mode)
7093 int jx = player->jx, jy = player->jy;
7094 int last_jx = player->last_jx, last_jy = player->last_jy;
7095 int move_stepsize = TILEX / player->move_delay_value;
7097 if (!player->active || !player->MovPos)
7100 if (mode == SCROLL_INIT)
7102 player->actual_frame_counter = FrameCounter;
7103 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
7105 if (Feld[last_jx][last_jy] == EL_EMPTY)
7106 Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
7113 else if (!FrameReached(&player->actual_frame_counter, 1))
7116 player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
7117 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
7119 if (Feld[last_jx][last_jy] == EL_PLAYER_IS_LEAVING)
7120 Feld[last_jx][last_jy] = EL_EMPTY;
7122 /* before DrawPlayer() to draw correct player graphic for this case */
7123 if (player->MovPos == 0)
7124 CheckGravityMovement(player);
7127 DrawPlayer(player); /* needed here only to cleanup last field */
7130 if (player->MovPos == 0) /* player reached destination field */
7132 if (IS_PASSABLE(Feld[last_jx][last_jy]))
7134 /* continue with normal speed after quickly moving through gate */
7135 HALVE_PLAYER_SPEED(player);
7137 /* be able to make the next move without delay */
7138 player->move_delay = 0;
7141 player->last_jx = jx;
7142 player->last_jy = jy;
7144 if (Feld[jx][jy] == EL_EXIT_OPEN ||
7145 Feld[jx][jy] == EL_SP_EXIT_OPEN ||
7146 Feld[jx][jy] == EL_SP_EXIT_OPENING) /* <-- special case */
7148 DrawPlayer(player); /* needed here only to cleanup last field */
7151 if (local_player->friends_still_needed == 0 ||
7152 IS_SP_ELEMENT(Feld[jx][jy]))
7153 player->LevelSolved = player->GameOver = TRUE;
7156 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
7158 TestIfHeroTouchesBadThing(jx, jy);
7159 TestIfPlayerTouchesCustomElement(jx, jy);
7161 TestIfElementTouchesCustomElement(jx, jy); /* for empty space */
7164 if (!player->active)
7168 if (tape.single_step && tape.recording && !tape.pausing &&
7169 !player->programmed_action)
7170 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
7174 void ScrollScreen(struct PlayerInfo *player, int mode)
7176 static unsigned long screen_frame_counter = 0;
7178 if (mode == SCROLL_INIT)
7180 /* set scrolling step size according to actual player's moving speed */
7181 ScrollStepSize = TILEX / player->move_delay_value;
7183 screen_frame_counter = FrameCounter;
7184 ScreenMovDir = player->MovDir;
7185 ScreenMovPos = player->MovPos;
7186 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
7189 else if (!FrameReached(&screen_frame_counter, 1))
7194 ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
7195 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
7196 redraw_mask |= REDRAW_FIELD;
7199 ScreenMovDir = MV_NO_MOVING;
7202 void TestIfPlayerTouchesCustomElement(int x, int y)
7204 static int xy[4][2] =
7211 static int change_sides[4][2] =
7213 /* center side border side */
7214 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
7215 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
7216 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
7217 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
7219 static int touch_dir[4] =
7226 int center_element = Feld[x][y]; /* should always be non-moving! */
7231 int xx = x + xy[i][0];
7232 int yy = y + xy[i][1];
7233 int center_side = change_sides[i][0];
7234 int border_side = change_sides[i][1];
7237 if (!IN_LEV_FIELD(xx, yy))
7240 if (IS_PLAYER(x, y))
7242 if (game.engine_version < VERSION_IDENT(3,0,7,0))
7243 border_element = Feld[xx][yy]; /* may be moving! */
7244 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
7245 border_element = Feld[xx][yy];
7246 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
7247 border_element = MovingOrBlocked2Element(xx, yy);
7249 continue; /* center and border element do not touch */
7251 CheckTriggeredElementSideChange(xx, yy, border_element, border_side,
7252 CE_OTHER_GETS_TOUCHED);
7253 CheckElementSideChange(xx, yy, border_element, border_side,
7254 CE_TOUCHED_BY_PLAYER, -1);
7256 else if (IS_PLAYER(xx, yy))
7258 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
7260 struct PlayerInfo *player = PLAYERINFO(xx, yy);
7262 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
7263 continue; /* center and border element do not touch */
7266 CheckTriggeredElementSideChange(x, y, center_element, center_side,
7267 CE_OTHER_GETS_TOUCHED);
7268 CheckElementSideChange(x, y, center_element, center_side,
7269 CE_TOUCHED_BY_PLAYER, -1);
7276 void TestIfElementTouchesCustomElement(int x, int y)
7278 static int xy[4][2] =
7285 static int change_sides[4][2] =
7287 /* center side border side */
7288 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
7289 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
7290 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
7291 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
7293 static int touch_dir[4] =
7300 boolean change_center_element = FALSE;
7301 int center_element_change_page = 0;
7302 int center_element = Feld[x][y]; /* should always be non-moving! */
7307 int xx = x + xy[i][0];
7308 int yy = y + xy[i][1];
7309 int center_side = change_sides[i][0];
7310 int border_side = change_sides[i][1];
7313 if (!IN_LEV_FIELD(xx, yy))
7316 if (game.engine_version < VERSION_IDENT(3,0,7,0))
7317 border_element = Feld[xx][yy]; /* may be moving! */
7318 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
7319 border_element = Feld[xx][yy];
7320 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
7321 border_element = MovingOrBlocked2Element(xx, yy);
7323 continue; /* center and border element do not touch */
7325 /* check for change of center element (but change it only once) */
7326 if (IS_CUSTOM_ELEMENT(center_element) &&
7327 HAS_ANY_CHANGE_EVENT(center_element, CE_OTHER_IS_TOUCHING) &&
7328 !change_center_element)
7330 for (j=0; j < element_info[center_element].num_change_pages; j++)
7332 struct ElementChangeInfo *change =
7333 &element_info[center_element].change_page[j];
7335 if (change->can_change &&
7336 change->events & CH_EVENT_BIT(CE_OTHER_IS_TOUCHING) &&
7337 change->sides & border_side &&
7338 change->trigger_element == border_element)
7340 change_center_element = TRUE;
7341 center_element_change_page = j;
7348 /* check for change of border element */
7349 if (IS_CUSTOM_ELEMENT(border_element) &&
7350 HAS_ANY_CHANGE_EVENT(border_element, CE_OTHER_IS_TOUCHING))
7352 for (j=0; j < element_info[border_element].num_change_pages; j++)
7354 struct ElementChangeInfo *change =
7355 &element_info[border_element].change_page[j];
7357 if (change->can_change &&
7358 change->events & CH_EVENT_BIT(CE_OTHER_IS_TOUCHING) &&
7359 change->sides & center_side &&
7360 change->trigger_element == center_element)
7362 CheckElementSideChange(xx, yy, border_element, CH_SIDE_ANY,
7363 CE_OTHER_IS_TOUCHING, j);
7370 if (change_center_element)
7371 CheckElementSideChange(x, y, center_element, CH_SIDE_ANY,
7372 CE_OTHER_IS_TOUCHING, center_element_change_page);
7375 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
7377 int i, kill_x = -1, kill_y = -1;
7378 static int test_xy[4][2] =
7385 static int test_dir[4] =
7395 int test_x, test_y, test_move_dir, test_element;
7397 test_x = good_x + test_xy[i][0];
7398 test_y = good_y + test_xy[i][1];
7399 if (!IN_LEV_FIELD(test_x, test_y))
7403 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NO_MOVING);
7406 test_element = Feld[test_x][test_y];
7408 test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
7411 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
7412 2nd case: DONT_TOUCH style bad thing does not move away from good thing
7414 if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
7415 (DONT_TOUCH(test_element) && test_move_dir != test_dir[i]))
7423 if (kill_x != -1 || kill_y != -1)
7425 if (IS_PLAYER(good_x, good_y))
7427 struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
7429 if (player->shield_deadly_time_left > 0)
7430 Bang(kill_x, kill_y);
7431 else if (!PLAYER_PROTECTED(good_x, good_y))
7435 Bang(good_x, good_y);
7439 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
7441 int i, kill_x = -1, kill_y = -1;
7442 int bad_element = Feld[bad_x][bad_y];
7443 static int test_xy[4][2] =
7450 static int touch_dir[4] =
7457 static int test_dir[4] =
7465 if (bad_element == EL_EXPLOSION) /* skip just exploding bad things */
7470 int test_x, test_y, test_move_dir, test_element;
7472 test_x = bad_x + test_xy[i][0];
7473 test_y = bad_y + test_xy[i][1];
7474 if (!IN_LEV_FIELD(test_x, test_y))
7478 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NO_MOVING);
7480 test_element = Feld[test_x][test_y];
7482 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
7483 2nd case: DONT_TOUCH style bad thing does not move away from good thing
7485 if ((DONT_RUN_INTO(bad_element) && bad_move_dir == test_dir[i]) ||
7486 (DONT_TOUCH(bad_element) && test_move_dir != test_dir[i]))
7488 /* good thing is player or penguin that does not move away */
7489 if (IS_PLAYER(test_x, test_y))
7491 struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
7493 if (bad_element == EL_ROBOT && player->is_moving)
7494 continue; /* robot does not kill player if he is moving */
7496 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
7498 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
7499 continue; /* center and border element do not touch */
7506 else if (test_element == EL_PENGUIN)
7515 if (kill_x != -1 || kill_y != -1)
7517 if (IS_PLAYER(kill_x, kill_y))
7519 struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
7521 if (player->shield_deadly_time_left > 0)
7523 else if (!PLAYER_PROTECTED(kill_x, kill_y))
7527 Bang(kill_x, kill_y);
7531 void TestIfHeroTouchesBadThing(int x, int y)
7533 TestIfGoodThingHitsBadThing(x, y, MV_NO_MOVING);
7536 void TestIfHeroRunsIntoBadThing(int x, int y, int move_dir)
7538 TestIfGoodThingHitsBadThing(x, y, move_dir);
7541 void TestIfBadThingTouchesHero(int x, int y)
7543 TestIfBadThingHitsGoodThing(x, y, MV_NO_MOVING);
7546 void TestIfBadThingRunsIntoHero(int x, int y, int move_dir)
7548 TestIfBadThingHitsGoodThing(x, y, move_dir);
7551 void TestIfFriendTouchesBadThing(int x, int y)
7553 TestIfGoodThingHitsBadThing(x, y, MV_NO_MOVING);
7556 void TestIfBadThingTouchesFriend(int x, int y)
7558 TestIfBadThingHitsGoodThing(x, y, MV_NO_MOVING);
7561 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
7563 int i, kill_x = bad_x, kill_y = bad_y;
7564 static int xy[4][2] =
7576 x = bad_x + xy[i][0];
7577 y = bad_y + xy[i][1];
7578 if (!IN_LEV_FIELD(x, y))
7581 element = Feld[x][y];
7582 if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
7583 element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
7591 if (kill_x != bad_x || kill_y != bad_y)
7595 void KillHero(struct PlayerInfo *player)
7597 int jx = player->jx, jy = player->jy;
7599 if (!player->active)
7602 /* remove accessible field at the player's position */
7603 Feld[jx][jy] = EL_EMPTY;
7605 /* deactivate shield (else Bang()/Explode() would not work right) */
7606 player->shield_normal_time_left = 0;
7607 player->shield_deadly_time_left = 0;
7613 static void KillHeroUnlessProtected(int x, int y)
7615 if (!PLAYER_PROTECTED(x, y))
7616 KillHero(PLAYERINFO(x, y));
7619 void BuryHero(struct PlayerInfo *player)
7621 int jx = player->jx, jy = player->jy;
7623 if (!player->active)
7627 PlaySoundLevelElementAction(jx, jy, player->element_nr, ACTION_DYING);
7629 PlaySoundLevel(jx, jy, SND_CLASS_PLAYER_DYING);
7631 PlaySoundLevel(jx, jy, SND_GAME_LOSING);
7633 player->GameOver = TRUE;
7637 void RemoveHero(struct PlayerInfo *player)
7639 int jx = player->jx, jy = player->jy;
7640 int i, found = FALSE;
7642 player->present = FALSE;
7643 player->active = FALSE;
7645 if (!ExplodeField[jx][jy])
7646 StorePlayer[jx][jy] = 0;
7648 for (i=0; i<MAX_PLAYERS; i++)
7649 if (stored_player[i].active)
7653 AllPlayersGone = TRUE;
7660 =============================================================================
7661 checkDiagonalPushing()
7662 -----------------------------------------------------------------------------
7663 check if diagonal input device direction results in pushing of object
7664 (by checking if the alternative direction is walkable, diggable, ...)
7665 =============================================================================
7668 static boolean checkDiagonalPushing(struct PlayerInfo *player,
7669 int x, int y, int real_dx, int real_dy)
7671 int jx, jy, dx, dy, xx, yy;
7673 if (real_dx == 0 || real_dy == 0) /* no diagonal direction => push */
7676 /* diagonal direction: check alternative direction */
7681 xx = jx + (dx == 0 ? real_dx : 0);
7682 yy = jy + (dy == 0 ? real_dy : 0);
7684 return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
7688 =============================================================================
7690 -----------------------------------------------------------------------------
7691 x, y: field next to player (non-diagonal) to try to dig to
7692 real_dx, real_dy: direction as read from input device (can be diagonal)
7693 =============================================================================
7696 int DigField(struct PlayerInfo *player,
7697 int x, int y, int real_dx, int real_dy, int mode)
7699 static int change_sides[4] =
7701 CH_SIDE_RIGHT, /* moving left */
7702 CH_SIDE_LEFT, /* moving right */
7703 CH_SIDE_BOTTOM, /* moving up */
7704 CH_SIDE_TOP, /* moving down */
7706 boolean use_spring_bug = (game.engine_version < VERSION_IDENT(2,2,0,0));
7707 int jx = player->jx, jy = player->jy;
7708 int dx = x - jx, dy = y - jy;
7709 int nextx = x + dx, nexty = y + dy;
7710 int move_direction = (dx == -1 ? MV_LEFT :
7711 dx == +1 ? MV_RIGHT :
7713 dy == +1 ? MV_DOWN : MV_NO_MOVING);
7714 int dig_side = change_sides[MV_DIR_BIT(move_direction)];
7717 if (player->MovPos == 0)
7719 player->is_digging = FALSE;
7720 player->is_collecting = FALSE;
7723 if (player->MovPos == 0) /* last pushing move finished */
7724 player->is_pushing = FALSE;
7726 if (mode == DF_NO_PUSH) /* player just stopped pushing */
7728 player->is_switching = FALSE;
7729 player->push_delay = 0;
7731 return MF_NO_ACTION;
7734 if (IS_MOVING(x, y) || IS_PLAYER(x, y))
7735 return MF_NO_ACTION;
7738 if (IS_TUBE(Feld[jx][jy]) || IS_TUBE(Back[jx][jy]))
7740 if (IS_TUBE(Feld[jx][jy]) ||
7741 (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0)))
7745 int tube_element = (IS_TUBE(Feld[jx][jy]) ? Feld[jx][jy] : Back[jx][jy]);
7746 int tube_leave_directions[][2] =
7748 { EL_TUBE_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
7749 { EL_TUBE_VERTICAL, MV_UP | MV_DOWN },
7750 { EL_TUBE_HORIZONTAL, MV_LEFT | MV_RIGHT },
7751 { EL_TUBE_VERTICAL_LEFT, MV_LEFT | MV_UP | MV_DOWN },
7752 { EL_TUBE_VERTICAL_RIGHT, MV_RIGHT | MV_UP | MV_DOWN },
7753 { EL_TUBE_HORIZONTAL_UP, MV_LEFT | MV_RIGHT | MV_UP },
7754 { EL_TUBE_HORIZONTAL_DOWN, MV_LEFT | MV_RIGHT | MV_DOWN },
7755 { EL_TUBE_LEFT_UP, MV_LEFT | MV_UP },
7756 { EL_TUBE_LEFT_DOWN, MV_LEFT | MV_DOWN },
7757 { EL_TUBE_RIGHT_UP, MV_RIGHT | MV_UP },
7758 { EL_TUBE_RIGHT_DOWN, MV_RIGHT | MV_DOWN },
7759 { -1, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN }
7762 while (tube_leave_directions[i][0] != tube_element)
7765 if (tube_leave_directions[i][0] == -1) /* should not happen */
7769 if (!(tube_leave_directions[i][1] & move_direction))
7770 return MF_NO_ACTION; /* tube has no opening in this direction */
7773 element = Feld[x][y];
7775 if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
7776 game.engine_version >= VERSION_IDENT(2,2,0,0))
7777 return MF_NO_ACTION;
7781 case EL_SP_PORT_LEFT:
7782 case EL_SP_PORT_RIGHT:
7784 case EL_SP_PORT_DOWN:
7785 case EL_SP_PORT_HORIZONTAL:
7786 case EL_SP_PORT_VERTICAL:
7787 case EL_SP_PORT_ANY:
7788 case EL_SP_GRAVITY_PORT_LEFT:
7789 case EL_SP_GRAVITY_PORT_RIGHT:
7790 case EL_SP_GRAVITY_PORT_UP:
7791 case EL_SP_GRAVITY_PORT_DOWN:
7793 element != EL_SP_PORT_LEFT &&
7794 element != EL_SP_GRAVITY_PORT_LEFT &&
7795 element != EL_SP_PORT_HORIZONTAL &&
7796 element != EL_SP_PORT_ANY) ||
7798 element != EL_SP_PORT_RIGHT &&
7799 element != EL_SP_GRAVITY_PORT_RIGHT &&
7800 element != EL_SP_PORT_HORIZONTAL &&
7801 element != EL_SP_PORT_ANY) ||
7803 element != EL_SP_PORT_UP &&
7804 element != EL_SP_GRAVITY_PORT_UP &&
7805 element != EL_SP_PORT_VERTICAL &&
7806 element != EL_SP_PORT_ANY) ||
7808 element != EL_SP_PORT_DOWN &&
7809 element != EL_SP_GRAVITY_PORT_DOWN &&
7810 element != EL_SP_PORT_VERTICAL &&
7811 element != EL_SP_PORT_ANY) ||
7812 !IN_LEV_FIELD(nextx, nexty) ||
7813 !IS_FREE(nextx, nexty))
7814 return MF_NO_ACTION;
7816 if (element == EL_SP_GRAVITY_PORT_LEFT ||
7817 element == EL_SP_GRAVITY_PORT_RIGHT ||
7818 element == EL_SP_GRAVITY_PORT_UP ||
7819 element == EL_SP_GRAVITY_PORT_DOWN)
7820 game.gravity = !game.gravity;
7822 /* automatically move to the next field with double speed */
7823 player->programmed_action = move_direction;
7824 DOUBLE_PLAYER_SPEED(player);
7826 PlaySoundLevel(x, y, SND_CLASS_SP_PORT_PASSING);
7830 case EL_TUBE_VERTICAL:
7831 case EL_TUBE_HORIZONTAL:
7832 case EL_TUBE_VERTICAL_LEFT:
7833 case EL_TUBE_VERTICAL_RIGHT:
7834 case EL_TUBE_HORIZONTAL_UP:
7835 case EL_TUBE_HORIZONTAL_DOWN:
7836 case EL_TUBE_LEFT_UP:
7837 case EL_TUBE_LEFT_DOWN:
7838 case EL_TUBE_RIGHT_UP:
7839 case EL_TUBE_RIGHT_DOWN:
7842 int tube_enter_directions[][2] =
7844 { EL_TUBE_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
7845 { EL_TUBE_VERTICAL, MV_UP | MV_DOWN },
7846 { EL_TUBE_HORIZONTAL, MV_LEFT | MV_RIGHT },
7847 { EL_TUBE_VERTICAL_LEFT, MV_RIGHT | MV_UP | MV_DOWN },
7848 { EL_TUBE_VERTICAL_RIGHT, MV_LEFT | MV_UP | MV_DOWN },
7849 { EL_TUBE_HORIZONTAL_UP, MV_LEFT | MV_RIGHT | MV_DOWN },
7850 { EL_TUBE_HORIZONTAL_DOWN, MV_LEFT | MV_RIGHT | MV_UP },
7851 { EL_TUBE_LEFT_UP, MV_RIGHT | MV_DOWN },
7852 { EL_TUBE_LEFT_DOWN, MV_RIGHT | MV_UP },
7853 { EL_TUBE_RIGHT_UP, MV_LEFT | MV_DOWN },
7854 { EL_TUBE_RIGHT_DOWN, MV_LEFT | MV_UP },
7855 { -1, MV_NO_MOVING }
7858 while (tube_enter_directions[i][0] != element)
7861 if (tube_enter_directions[i][0] == -1) /* should not happen */
7865 if (!(tube_enter_directions[i][1] & move_direction))
7866 return MF_NO_ACTION; /* tube has no opening in this direction */
7868 PlaySoundLevel(x, y, SND_CLASS_TUBE_WALKING);
7874 if (IS_WALKABLE(element))
7876 int sound_action = ACTION_WALKING;
7878 if (element >= EL_GATE_1 && element <= EL_GATE_4)
7880 if (!player->key[element - EL_GATE_1])
7881 return MF_NO_ACTION;
7883 else if (element >= EL_GATE_1_GRAY && element <= EL_GATE_4_GRAY)
7885 if (!player->key[element - EL_GATE_1_GRAY])
7886 return MF_NO_ACTION;
7888 else if (element == EL_EXIT_OPEN ||
7889 element == EL_SP_EXIT_OPEN ||
7890 element == EL_SP_EXIT_OPENING)
7892 sound_action = ACTION_PASSING; /* player is passing exit */
7894 else if (element == EL_EMPTY)
7896 sound_action = ACTION_MOVING; /* nothing to walk on */
7899 /* play sound from background or player, whatever is available */
7900 if (element_info[element].sound[sound_action] != SND_UNDEFINED)
7901 PlaySoundLevelElementAction(x, y, element, sound_action);
7903 PlaySoundLevelElementAction(x, y, player->element_nr, sound_action);
7907 else if (IS_PASSABLE(element))
7909 if (!IN_LEV_FIELD(nextx, nexty) || !IS_FREE(nextx, nexty))
7910 return MF_NO_ACTION;
7913 if (CAN_MOVE(element)) /* only fixed elements can be passed! */
7914 return MF_NO_ACTION;
7917 if (element >= EL_EM_GATE_1 && element <= EL_EM_GATE_4)
7919 if (!player->key[element - EL_EM_GATE_1])
7920 return MF_NO_ACTION;
7922 else if (element >= EL_EM_GATE_1_GRAY && element <= EL_EM_GATE_4_GRAY)
7924 if (!player->key[element - EL_EM_GATE_1_GRAY])
7925 return MF_NO_ACTION;
7928 /* automatically move to the next field with double speed */
7929 player->programmed_action = move_direction;
7930 DOUBLE_PLAYER_SPEED(player);
7932 PlaySoundLevelAction(x, y, ACTION_PASSING);
7936 else if (IS_DIGGABLE(element))
7940 if (mode != DF_SNAP)
7943 GfxElement[x][y] = GFX_ELEMENT(element);
7946 (GFX_CRUMBLED(element) ? EL_SAND : GFX_ELEMENT(element));
7948 player->is_digging = TRUE;
7951 PlaySoundLevelElementAction(x, y, element, ACTION_DIGGING);
7953 CheckTriggeredElementChange(x, y, element, CE_OTHER_GETS_DIGGED);
7956 if (mode == DF_SNAP)
7957 TestIfElementTouchesCustomElement(x, y); /* for empty space */
7962 else if (IS_COLLECTIBLE(element))
7966 if (mode != DF_SNAP)
7968 GfxElement[x][y] = element;
7969 player->is_collecting = TRUE;
7972 if (element == EL_SPEED_PILL)
7973 player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
7974 else if (element == EL_EXTRA_TIME && level.time > 0)
7977 DrawText(DX_TIME, DY_TIME, int2str(TimeLeft, 3), FONT_TEXT_2);
7979 else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
7981 player->shield_normal_time_left += 10;
7982 if (element == EL_SHIELD_DEADLY)
7983 player->shield_deadly_time_left += 10;
7985 else if (element == EL_DYNAMITE || element == EL_SP_DISK_RED)
7987 if (player->inventory_size < MAX_INVENTORY_SIZE)
7988 player->inventory_element[player->inventory_size++] = element;
7990 DrawText(DX_DYNAMITE, DY_DYNAMITE,
7991 int2str(local_player->inventory_size, 3), FONT_TEXT_2);
7993 else if (element == EL_DYNABOMB_INCREASE_NUMBER)
7995 player->dynabomb_count++;
7996 player->dynabombs_left++;
7998 else if (element == EL_DYNABOMB_INCREASE_SIZE)
8000 player->dynabomb_size++;
8002 else if (element == EL_DYNABOMB_INCREASE_POWER)
8004 player->dynabomb_xl = TRUE;
8006 else if ((element >= EL_KEY_1 && element <= EL_KEY_4) ||
8007 (element >= EL_EM_KEY_1 && element <= EL_EM_KEY_4))
8009 int key_nr = (element >= EL_KEY_1 && element <= EL_KEY_4 ?
8010 element - EL_KEY_1 : element - EL_EM_KEY_1);
8012 player->key[key_nr] = TRUE;
8014 DrawMiniGraphicExt(drawto, DX_KEYS + key_nr * MINI_TILEX, DY_KEYS,
8015 el2edimg(EL_KEY_1 + key_nr));
8016 redraw_mask |= REDRAW_DOOR_1;
8018 else if (IS_ENVELOPE(element))
8021 player->show_envelope = element;
8023 ShowEnvelope(element - EL_ENVELOPE_1);
8026 else if (IS_DROPPABLE(element)) /* can be collected and dropped */
8030 for (i=0; i < element_info[element].collect_count; i++)
8031 if (player->inventory_size < MAX_INVENTORY_SIZE)
8032 player->inventory_element[player->inventory_size++] = element;
8034 DrawText(DX_DYNAMITE, DY_DYNAMITE,
8035 int2str(local_player->inventory_size, 3), FONT_TEXT_2);
8037 else if (element_info[element].collect_count > 0)
8039 local_player->gems_still_needed -=
8040 element_info[element].collect_count;
8041 if (local_player->gems_still_needed < 0)
8042 local_player->gems_still_needed = 0;
8044 DrawText(DX_EMERALDS, DY_EMERALDS,
8045 int2str(local_player->gems_still_needed, 3), FONT_TEXT_2);
8048 RaiseScoreElement(element);
8049 PlaySoundLevelElementAction(x, y, element, ACTION_COLLECTING);
8051 CheckTriggeredElementChange(x, y, element, CE_OTHER_GETS_COLLECTED);
8054 if (mode == DF_SNAP)
8055 TestIfElementTouchesCustomElement(x, y); /* for empty space */
8060 else if (IS_PUSHABLE(element))
8062 if (mode == DF_SNAP && element != EL_BD_ROCK)
8063 return MF_NO_ACTION;
8065 if (CAN_FALL(element) && dy)
8066 return MF_NO_ACTION;
8068 if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
8069 !(element == EL_SPRING && use_spring_bug))
8070 return MF_NO_ACTION;
8073 if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
8074 ((move_direction & MV_VERTICAL &&
8075 ((element_info[element].move_pattern & MV_LEFT &&
8076 IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
8077 (element_info[element].move_pattern & MV_RIGHT &&
8078 IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
8079 (move_direction & MV_HORIZONTAL &&
8080 ((element_info[element].move_pattern & MV_UP &&
8081 IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
8082 (element_info[element].move_pattern & MV_DOWN &&
8083 IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
8084 return MF_NO_ACTION;
8088 /* do not push elements already moving away faster than player */
8089 if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
8090 ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
8091 return MF_NO_ACTION;
8093 if (element == EL_SPRING && MovDir[x][y] != MV_NO_MOVING)
8094 return MF_NO_ACTION;
8098 if (game.engine_version >= VERSION_IDENT(3,0,7,1))
8100 if (player->push_delay_value == -1)
8101 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
8103 else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
8105 if (!player->is_pushing)
8106 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
8110 if (game.engine_version >= VERSION_IDENT(2,2,0,7) &&
8111 (game.engine_version < VERSION_IDENT(3,0,7,1) ||
8112 !player_is_pushing))
8113 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
8116 if (!player->is_pushing &&
8117 game.engine_version >= VERSION_IDENT(2,2,0,7))
8118 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
8122 printf("::: push delay: %ld [%d, %d] [%d]\n",
8123 player->push_delay_value, FrameCounter, game.engine_version,
8124 player->is_pushing);
8127 player->is_pushing = TRUE;
8129 if (!(IN_LEV_FIELD(nextx, nexty) &&
8130 (IS_FREE(nextx, nexty) ||
8131 (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY &&
8132 IS_SB_ELEMENT(element)))))
8133 return MF_NO_ACTION;
8135 if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
8136 return MF_NO_ACTION;
8138 if (player->push_delay == 0) /* new pushing; restart delay */
8139 player->push_delay = FrameCounter;
8141 if (!FrameReached(&player->push_delay, player->push_delay_value) &&
8142 !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
8143 element != EL_SPRING && element != EL_BALLOON)
8145 /* make sure that there is no move delay before next try to push */
8146 if (game.engine_version >= VERSION_IDENT(3,0,7,1))
8147 player->move_delay = INITIAL_MOVE_DELAY_OFF;
8149 return MF_NO_ACTION;
8153 printf("::: NOW PUSHING... [%d]\n", FrameCounter);
8156 if (IS_SB_ELEMENT(element))
8158 if (element == EL_SOKOBAN_FIELD_FULL)
8160 Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
8161 local_player->sokobanfields_still_needed++;
8164 if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
8166 Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
8167 local_player->sokobanfields_still_needed--;
8170 Feld[x][y] = EL_SOKOBAN_OBJECT;
8172 if (Back[x][y] == Back[nextx][nexty])
8173 PlaySoundLevelAction(x, y, ACTION_PUSHING);
8174 else if (Back[x][y] != 0)
8175 PlaySoundLevelElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
8178 PlaySoundLevelElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
8181 if (local_player->sokobanfields_still_needed == 0 &&
8182 game.emulation == EMU_SOKOBAN)
8184 player->LevelSolved = player->GameOver = TRUE;
8185 PlaySoundLevel(x, y, SND_GAME_SOKOBAN_SOLVING);
8189 PlaySoundLevelElementAction(x, y, element, ACTION_PUSHING);
8191 InitMovingField(x, y, move_direction);
8192 GfxAction[x][y] = ACTION_PUSHING;
8194 if (mode == DF_SNAP)
8195 ContinueMoving(x, y);
8197 MovPos[x][y] = (dx != 0 ? dx : dy);
8199 Pushed[x][y] = TRUE;
8200 Pushed[nextx][nexty] = TRUE;
8202 if (game.engine_version < VERSION_IDENT(2,2,0,7))
8203 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
8205 player->push_delay_value = -1; /* get new value later */
8207 CheckTriggeredElementSideChange(x, y, element, dig_side,
8208 CE_OTHER_GETS_PUSHED);
8209 CheckElementSideChange(x, y, element, dig_side,
8210 CE_PUSHED_BY_PLAYER, -1);
8214 else if (IS_SWITCHABLE(element))
8216 if (PLAYER_SWITCHING(player, x, y))
8219 player->is_switching = TRUE;
8220 player->switch_x = x;
8221 player->switch_y = y;
8223 PlaySoundLevelElementAction(x, y, element, ACTION_ACTIVATING);
8225 if (element == EL_ROBOT_WHEEL)
8227 Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
8231 DrawLevelField(x, y);
8233 else if (element == EL_SP_TERMINAL)
8237 for (yy=0; yy < lev_fieldy; yy++) for (xx=0; xx < lev_fieldx; xx++)
8239 if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
8241 else if (Feld[xx][yy] == EL_SP_TERMINAL)
8242 Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
8245 else if (IS_BELT_SWITCH(element))
8247 ToggleBeltSwitch(x, y);
8249 else if (element == EL_SWITCHGATE_SWITCH_UP ||
8250 element == EL_SWITCHGATE_SWITCH_DOWN)
8252 ToggleSwitchgateSwitch(x, y);
8254 else if (element == EL_LIGHT_SWITCH ||
8255 element == EL_LIGHT_SWITCH_ACTIVE)
8257 ToggleLightSwitch(x, y);
8260 PlaySoundLevel(x, y, element == EL_LIGHT_SWITCH ?
8261 SND_LIGHT_SWITCH_ACTIVATING :
8262 SND_LIGHT_SWITCH_DEACTIVATING);
8265 else if (element == EL_TIMEGATE_SWITCH)
8267 ActivateTimegateSwitch(x, y);
8269 else if (element == EL_BALLOON_SWITCH_LEFT ||
8270 element == EL_BALLOON_SWITCH_RIGHT ||
8271 element == EL_BALLOON_SWITCH_UP ||
8272 element == EL_BALLOON_SWITCH_DOWN ||
8273 element == EL_BALLOON_SWITCH_ANY)
8275 if (element == EL_BALLOON_SWITCH_ANY)
8276 game.balloon_dir = move_direction;
8278 game.balloon_dir = (element == EL_BALLOON_SWITCH_LEFT ? MV_LEFT :
8279 element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
8280 element == EL_BALLOON_SWITCH_UP ? MV_UP :
8281 element == EL_BALLOON_SWITCH_DOWN ? MV_DOWN :
8284 else if (element == EL_LAMP)
8286 Feld[x][y] = EL_LAMP_ACTIVE;
8287 local_player->lights_still_needed--;
8289 DrawLevelField(x, y);
8291 else if (element == EL_TIME_ORB_FULL)
8293 Feld[x][y] = EL_TIME_ORB_EMPTY;
8295 DrawText(DX_TIME, DY_TIME, int2str(TimeLeft, 3), FONT_TEXT_2);
8297 DrawLevelField(x, y);
8300 PlaySoundStereo(SND_TIME_ORB_FULL_COLLECTING, SOUND_MIDDLE);
8308 if (!PLAYER_SWITCHING(player, x, y))
8310 player->is_switching = TRUE;
8311 player->switch_x = x;
8312 player->switch_y = y;
8314 CheckTriggeredElementSideChange(x, y, element, dig_side,
8315 CE_OTHER_IS_SWITCHING);
8316 CheckElementSideChange(x, y, element, dig_side, CE_SWITCHED, -1);
8319 CheckTriggeredElementSideChange(x, y, element, dig_side,
8320 CE_OTHER_GETS_PRESSED);
8321 CheckElementSideChange(x, y, element, dig_side,
8322 CE_PRESSED_BY_PLAYER, -1);
8325 return MF_NO_ACTION;
8328 player->push_delay = 0;
8330 if (Feld[x][y] != element) /* really digged/collected something */
8331 player->is_collecting = !player->is_digging;
8336 boolean SnapField(struct PlayerInfo *player, int dx, int dy)
8338 int jx = player->jx, jy = player->jy;
8339 int x = jx + dx, y = jy + dy;
8340 int snap_direction = (dx == -1 ? MV_LEFT :
8341 dx == +1 ? MV_RIGHT :
8343 dy == +1 ? MV_DOWN : MV_NO_MOVING);
8345 if (player->MovPos && game.engine_version >= VERSION_IDENT(2,2,0,0))
8348 if (!player->active || !IN_LEV_FIELD(x, y))
8356 if (player->MovPos == 0)
8357 player->is_pushing = FALSE;
8359 player->is_snapping = FALSE;
8361 if (player->MovPos == 0)
8363 player->is_moving = FALSE;
8364 player->is_digging = FALSE;
8365 player->is_collecting = FALSE;
8371 if (player->is_snapping)
8374 player->MovDir = snap_direction;
8376 player->is_moving = FALSE;
8377 player->is_digging = FALSE;
8378 player->is_collecting = FALSE;
8380 if (DigField(player, x, y, 0, 0, DF_SNAP) == MF_NO_ACTION)
8383 player->is_snapping = TRUE;
8385 player->is_moving = FALSE;
8386 player->is_digging = FALSE;
8387 player->is_collecting = FALSE;
8389 DrawLevelField(x, y);
8395 boolean DropElement(struct PlayerInfo *player)
8397 int jx = player->jx, jy = player->jy;
8400 if (!player->active || player->MovPos)
8403 old_element = Feld[jx][jy];
8405 /* check if player has anything that can be dropped */
8406 if (player->inventory_size == 0 && player->dynabombs_left == 0)
8409 /* check if anything can be dropped at the current position */
8410 if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
8413 /* collected custom elements can only be dropped on empty fields */
8414 if (player->inventory_size > 0 &&
8415 IS_CUSTOM_ELEMENT(player->inventory_element[player->inventory_size - 1])
8416 && old_element != EL_EMPTY)
8419 if (old_element != EL_EMPTY)
8420 Back[jx][jy] = old_element; /* store old element on this field */
8422 MovDelay[jx][jy] = 96;
8424 ResetGfxAnimation(jx, jy);
8425 ResetRandomAnimationValue(jx, jy);
8427 if (player->inventory_size > 0)
8429 int new_element = player->inventory_element[--player->inventory_size];
8431 Feld[jx][jy] = (new_element == EL_DYNAMITE ? EL_DYNAMITE_ACTIVE :
8432 new_element == EL_SP_DISK_RED ? EL_SP_DISK_RED_ACTIVE :
8435 DrawText(DX_DYNAMITE, DY_DYNAMITE,
8436 int2str(local_player->inventory_size, 3), FONT_TEXT_2);
8438 if (IN_SCR_FIELD(SCREENX(jx), SCREENY(jy)))
8439 DrawGraphicThruMask(SCREENX(jx), SCREENY(jy), el2img(Feld[jx][jy]), 0);
8441 PlaySoundLevelAction(jx, jy, ACTION_DROPPING);
8443 CheckTriggeredElementChange(jx, jy, new_element, CE_OTHER_GETS_DROPPED);
8444 CheckElementChange(jx, jy, new_element, CE_DROPPED_BY_PLAYER);
8446 TestIfElementTouchesCustomElement(jx, jy);
8448 else /* player is dropping a dyna bomb */
8450 player->dynabombs_left--;
8453 EL_DYNABOMB_PLAYER_1_ACTIVE + (player->element_nr - EL_PLAYER_1);
8455 if (IN_SCR_FIELD(SCREENX(jx), SCREENY(jy)))
8456 DrawGraphicThruMask(SCREENX(jx), SCREENY(jy), el2img(Feld[jx][jy]), 0);
8458 PlaySoundLevelAction(jx, jy, ACTION_DROPPING);
8464 /* ------------------------------------------------------------------------- */
8465 /* game sound playing functions */
8466 /* ------------------------------------------------------------------------- */
8468 static int *loop_sound_frame = NULL;
8469 static int *loop_sound_volume = NULL;
8471 void InitPlaySoundLevel()
8473 int num_sounds = getSoundListSize();
8475 if (loop_sound_frame != NULL)
8476 free(loop_sound_frame);
8478 if (loop_sound_volume != NULL)
8479 free(loop_sound_volume);
8481 loop_sound_frame = checked_calloc(num_sounds * sizeof(int));
8482 loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
8485 static void PlaySoundLevel(int x, int y, int nr)
8487 int sx = SCREENX(x), sy = SCREENY(y);
8488 int volume, stereo_position;
8489 int max_distance = 8;
8490 int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
8492 if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
8493 (!setup.sound_loops && IS_LOOP_SOUND(nr)))
8496 if (!IN_LEV_FIELD(x, y) ||
8497 sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
8498 sy < -max_distance || sy >= SCR_FIELDY + max_distance)
8501 volume = SOUND_MAX_VOLUME;
8503 if (!IN_SCR_FIELD(sx, sy))
8505 int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
8506 int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
8508 volume -= volume * (dx > dy ? dx : dy) / max_distance;
8511 stereo_position = (SOUND_MAX_LEFT +
8512 (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
8513 (SCR_FIELDX + 2 * max_distance));
8515 if (IS_LOOP_SOUND(nr))
8517 /* This assures that quieter loop sounds do not overwrite louder ones,
8518 while restarting sound volume comparison with each new game frame. */
8520 if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
8523 loop_sound_volume[nr] = volume;
8524 loop_sound_frame[nr] = FrameCounter;
8527 PlaySoundExt(nr, volume, stereo_position, type);
8530 static void PlaySoundLevelNearest(int x, int y, int sound_action)
8532 PlaySoundLevel(x < LEVELX(BX1) ? LEVELX(BX1) :
8533 x > LEVELX(BX2) ? LEVELX(BX2) : x,
8534 y < LEVELY(BY1) ? LEVELY(BY1) :
8535 y > LEVELY(BY2) ? LEVELY(BY2) : y,
8539 static void PlaySoundLevelAction(int x, int y, int action)
8541 PlaySoundLevelElementAction(x, y, Feld[x][y], action);
8544 static void PlaySoundLevelElementAction(int x, int y, int element, int action)
8546 int sound_effect = element_info[element].sound[action];
8548 if (sound_effect != SND_UNDEFINED)
8549 PlaySoundLevel(x, y, sound_effect);
8552 static void PlaySoundLevelActionIfLoop(int x, int y, int action)
8554 int sound_effect = element_info[Feld[x][y]].sound[action];
8556 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
8557 PlaySoundLevel(x, y, sound_effect);
8560 static void StopSoundLevelActionIfLoop(int x, int y, int action)
8562 int sound_effect = element_info[Feld[x][y]].sound[action];
8564 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
8565 StopSoundExt(sound_effect, SND_CTRL_STOP_SOUND);
8568 void RaiseScore(int value)
8570 local_player->score += value;
8571 DrawText(DX_SCORE, DY_SCORE, int2str(local_player->score, 5), FONT_TEXT_2);
8574 void RaiseScoreElement(int element)
8580 case EL_EMERALD_YELLOW:
8581 case EL_EMERALD_RED:
8582 case EL_EMERALD_PURPLE:
8583 case EL_SP_INFOTRON:
8584 RaiseScore(level.score[SC_EMERALD]);
8587 RaiseScore(level.score[SC_DIAMOND]);
8590 RaiseScore(level.score[SC_CRYSTAL]);
8593 RaiseScore(level.score[SC_PEARL]);
8596 case EL_BD_BUTTERFLY:
8597 case EL_SP_ELECTRON:
8598 RaiseScore(level.score[SC_BUG]);
8602 case EL_SP_SNIKSNAK:
8603 RaiseScore(level.score[SC_SPACESHIP]);
8606 case EL_DARK_YAMYAM:
8607 RaiseScore(level.score[SC_YAMYAM]);
8610 RaiseScore(level.score[SC_ROBOT]);
8613 RaiseScore(level.score[SC_PACMAN]);
8616 RaiseScore(level.score[SC_NUT]);
8619 case EL_SP_DISK_RED:
8620 case EL_DYNABOMB_INCREASE_NUMBER:
8621 case EL_DYNABOMB_INCREASE_SIZE:
8622 case EL_DYNABOMB_INCREASE_POWER:
8623 RaiseScore(level.score[SC_DYNAMITE]);
8625 case EL_SHIELD_NORMAL:
8626 case EL_SHIELD_DEADLY:
8627 RaiseScore(level.score[SC_SHIELD]);
8630 RaiseScore(level.score[SC_TIME_BONUS]);
8636 RaiseScore(level.score[SC_KEY]);
8639 RaiseScore(element_info[element].collect_score);
8644 void RequestQuitGame(boolean ask_if_really_quit)
8646 if (AllPlayersGone ||
8647 !ask_if_really_quit ||
8648 level_editor_test_game ||
8649 Request("Do you really want to quit the game ?",
8650 REQ_ASK | REQ_STAY_CLOSED))
8652 #if defined(PLATFORM_UNIX)
8653 if (options.network)
8654 SendToServer_StopPlaying();
8658 game_status = GAME_MODE_MAIN;
8664 OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
8669 /* ---------- new game button stuff ---------------------------------------- */
8671 /* graphic position values for game buttons */
8672 #define GAME_BUTTON_XSIZE 30
8673 #define GAME_BUTTON_YSIZE 30
8674 #define GAME_BUTTON_XPOS 5
8675 #define GAME_BUTTON_YPOS 215
8676 #define SOUND_BUTTON_XPOS 5
8677 #define SOUND_BUTTON_YPOS (GAME_BUTTON_YPOS + GAME_BUTTON_YSIZE)
8679 #define GAME_BUTTON_STOP_XPOS (GAME_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
8680 #define GAME_BUTTON_PAUSE_XPOS (GAME_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
8681 #define GAME_BUTTON_PLAY_XPOS (GAME_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
8682 #define SOUND_BUTTON_MUSIC_XPOS (SOUND_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
8683 #define SOUND_BUTTON_LOOPS_XPOS (SOUND_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
8684 #define SOUND_BUTTON_SIMPLE_XPOS (SOUND_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
8691 } gamebutton_info[NUM_GAME_BUTTONS] =
8694 GAME_BUTTON_STOP_XPOS, GAME_BUTTON_YPOS,
8699 GAME_BUTTON_PAUSE_XPOS, GAME_BUTTON_YPOS,
8704 GAME_BUTTON_PLAY_XPOS, GAME_BUTTON_YPOS,
8709 SOUND_BUTTON_MUSIC_XPOS, SOUND_BUTTON_YPOS,
8710 SOUND_CTRL_ID_MUSIC,
8711 "background music on/off"
8714 SOUND_BUTTON_LOOPS_XPOS, SOUND_BUTTON_YPOS,
8715 SOUND_CTRL_ID_LOOPS,
8716 "sound loops on/off"
8719 SOUND_BUTTON_SIMPLE_XPOS, SOUND_BUTTON_YPOS,
8720 SOUND_CTRL_ID_SIMPLE,
8721 "normal sounds on/off"
8725 void CreateGameButtons()
8729 for (i=0; i<NUM_GAME_BUTTONS; i++)
8731 Bitmap *gd_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
8732 struct GadgetInfo *gi;
8735 unsigned long event_mask;
8736 int gd_xoffset, gd_yoffset;
8737 int gd_x1, gd_x2, gd_y1, gd_y2;
8740 gd_xoffset = gamebutton_info[i].x;
8741 gd_yoffset = gamebutton_info[i].y;
8742 gd_x1 = DOOR_GFX_PAGEX4 + gd_xoffset;
8743 gd_x2 = DOOR_GFX_PAGEX3 + gd_xoffset;
8745 if (id == GAME_CTRL_ID_STOP ||
8746 id == GAME_CTRL_ID_PAUSE ||
8747 id == GAME_CTRL_ID_PLAY)
8749 button_type = GD_TYPE_NORMAL_BUTTON;
8751 event_mask = GD_EVENT_RELEASED;
8752 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
8753 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
8757 button_type = GD_TYPE_CHECK_BUTTON;
8759 ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
8760 (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
8761 (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
8762 event_mask = GD_EVENT_PRESSED;
8763 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset;
8764 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
8767 gi = CreateGadget(GDI_CUSTOM_ID, id,
8768 GDI_INFO_TEXT, gamebutton_info[i].infotext,
8769 GDI_X, DX + gd_xoffset,
8770 GDI_Y, DY + gd_yoffset,
8771 GDI_WIDTH, GAME_BUTTON_XSIZE,
8772 GDI_HEIGHT, GAME_BUTTON_YSIZE,
8773 GDI_TYPE, button_type,
8774 GDI_STATE, GD_BUTTON_UNPRESSED,
8775 GDI_CHECKED, checked,
8776 GDI_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y1,
8777 GDI_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y1,
8778 GDI_ALT_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y2,
8779 GDI_ALT_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y2,
8780 GDI_EVENT_MASK, event_mask,
8781 GDI_CALLBACK_ACTION, HandleGameButtons,
8785 Error(ERR_EXIT, "cannot create gadget");
8787 game_gadget[id] = gi;
8791 void FreeGameButtons()
8795 for (i=0; i<NUM_GAME_BUTTONS; i++)
8796 FreeGadget(game_gadget[i]);
8799 static void MapGameButtons()
8803 for (i=0; i<NUM_GAME_BUTTONS; i++)
8804 MapGadget(game_gadget[i]);
8807 void UnmapGameButtons()
8811 for (i=0; i<NUM_GAME_BUTTONS; i++)
8812 UnmapGadget(game_gadget[i]);
8815 static void HandleGameButtons(struct GadgetInfo *gi)
8817 int id = gi->custom_id;
8819 if (game_status != GAME_MODE_PLAYING)
8824 case GAME_CTRL_ID_STOP:
8825 RequestQuitGame(TRUE);
8828 case GAME_CTRL_ID_PAUSE:
8829 if (options.network)
8831 #if defined(PLATFORM_UNIX)
8833 SendToServer_ContinuePlaying();
8835 SendToServer_PausePlaying();
8839 TapeTogglePause(TAPE_TOGGLE_MANUAL);
8842 case GAME_CTRL_ID_PLAY:
8845 #if defined(PLATFORM_UNIX)
8846 if (options.network)
8847 SendToServer_ContinuePlaying();
8851 tape.pausing = FALSE;
8852 DrawVideoDisplay(VIDEO_STATE_PAUSE_OFF,0);
8857 case SOUND_CTRL_ID_MUSIC:
8858 if (setup.sound_music)
8860 setup.sound_music = FALSE;
8863 else if (audio.music_available)
8865 setup.sound = setup.sound_music = TRUE;
8867 SetAudioMode(setup.sound);
8868 PlayMusic(level_nr);
8872 case SOUND_CTRL_ID_LOOPS:
8873 if (setup.sound_loops)
8874 setup.sound_loops = FALSE;
8875 else if (audio.loops_available)
8877 setup.sound = setup.sound_loops = TRUE;
8878 SetAudioMode(setup.sound);
8882 case SOUND_CTRL_ID_SIMPLE:
8883 if (setup.sound_simple)
8884 setup.sound_simple = FALSE;
8885 else if (audio.sound_available)
8887 setup.sound = setup.sound_simple = TRUE;
8888 SetAudioMode(setup.sound);