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 MAZE_RUNNER_CAN_ENTER_FIELD(x, y) \
148 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
149 IS_FOOD_DARK_YAMYAM(Feld[x][y])))
151 #define MOLE_CAN_ENTER_FIELD(x, y, condition) \
152 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || (condition)))
154 #define IN_LEV_FIELD_AND_IS_FREE(x, y) (IN_LEV_FIELD(x, y) && IS_FREE(x, y))
155 #define IN_LEV_FIELD_AND_NOT_FREE(x, y) (IN_LEV_FIELD(x, y) && !IS_FREE(x, y))
157 /* game button identifiers */
158 #define GAME_CTRL_ID_STOP 0
159 #define GAME_CTRL_ID_PAUSE 1
160 #define GAME_CTRL_ID_PLAY 2
161 #define SOUND_CTRL_ID_MUSIC 3
162 #define SOUND_CTRL_ID_LOOPS 4
163 #define SOUND_CTRL_ID_SIMPLE 5
165 #define NUM_GAME_BUTTONS 6
168 /* forward declaration for internal use */
170 static boolean MovePlayerOneStep(struct PlayerInfo *, int, int, int, int);
171 static boolean MovePlayer(struct PlayerInfo *, int, int);
172 static void ScrollPlayer(struct PlayerInfo *, int);
173 static void ScrollScreen(struct PlayerInfo *, int);
175 static void InitBeltMovement(void);
176 static void CloseAllOpenTimegates(void);
177 static void CheckGravityMovement(struct PlayerInfo *);
178 static void KillHeroUnlessProtected(int, int);
180 static void TestIfPlayerTouchesCustomElement(int, int);
181 static void TestIfElementTouchesCustomElement(int, int);
183 static void ChangeElement(int, int, int);
184 static boolean CheckTriggeredElementSideChange(int, int, int, int, int);
185 static boolean CheckTriggeredElementChange(int, int, int, int);
186 static boolean CheckElementSideChange(int, int, int, int, int, int);
187 static boolean CheckElementChange(int, int, int, int);
189 static void PlayLevelSound(int, int, int);
190 static void PlayLevelSoundNearest(int, int, int);
191 static void PlayLevelSoundAction(int, int, int);
192 static void PlayLevelSoundElementAction(int, int, int, int);
193 static void PlayLevelSoundElementActionIfLoop(int, int, int, int);
194 static void PlayLevelSoundActionIfLoop(int, int, int);
195 static void StopLevelSoundActionIfLoop(int, int, int);
196 static void PlayLevelMusic();
198 static void MapGameButtons();
199 static void HandleGameButtons(struct GadgetInfo *);
201 static struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS];
204 /* ------------------------------------------------------------------------- */
205 /* definition of elements that automatically change to other elements after */
206 /* a specified time, eventually calling a function when changing */
207 /* ------------------------------------------------------------------------- */
209 /* forward declaration for changer functions */
210 static void InitBuggyBase(int x, int y);
211 static void WarnBuggyBase(int x, int y);
213 static void InitTrap(int x, int y);
214 static void ActivateTrap(int x, int y);
215 static void ChangeActiveTrap(int x, int y);
217 static void InitRobotWheel(int x, int y);
218 static void RunRobotWheel(int x, int y);
219 static void StopRobotWheel(int x, int y);
221 static void InitTimegateWheel(int x, int y);
222 static void RunTimegateWheel(int x, int y);
224 struct ChangingElementInfo
229 void (*pre_change_function)(int x, int y);
230 void (*change_function)(int x, int y);
231 void (*post_change_function)(int x, int y);
234 static struct ChangingElementInfo change_delay_list[] =
285 EL_SWITCHGATE_OPENING,
293 EL_SWITCHGATE_CLOSING,
294 EL_SWITCHGATE_CLOSED,
326 EL_ACID_SPLASH_RIGHT,
335 EL_SP_BUGGY_BASE_ACTIVATING,
342 EL_SP_BUGGY_BASE_ACTIVATING,
343 EL_SP_BUGGY_BASE_ACTIVE,
350 EL_SP_BUGGY_BASE_ACTIVE,
374 EL_ROBOT_WHEEL_ACTIVE,
382 EL_TIMEGATE_SWITCH_ACTIVE,
403 int push_delay_fixed, push_delay_random;
408 { EL_BALLOON, 0, 0 },
410 { EL_SOKOBAN_OBJECT, 2, 0 },
411 { EL_SOKOBAN_FIELD_FULL, 2, 0 },
412 { EL_SATELLITE, 2, 0 },
413 { EL_SP_DISK_YELLOW, 2, 0 },
415 { EL_UNDEFINED, 0, 0 },
423 move_stepsize_list[] =
425 { EL_AMOEBA_DROP, 2 },
426 { EL_AMOEBA_DROPPING, 2 },
427 { EL_QUICKSAND_FILLING, 1 },
428 { EL_QUICKSAND_EMPTYING, 1 },
429 { EL_MAGIC_WALL_FILLING, 2 },
430 { EL_BD_MAGIC_WALL_FILLING, 2 },
431 { EL_MAGIC_WALL_EMPTYING, 2 },
432 { EL_BD_MAGIC_WALL_EMPTYING, 2 },
442 collect_count_list[] =
445 { EL_BD_DIAMOND, 1 },
446 { EL_EMERALD_YELLOW, 1 },
447 { EL_EMERALD_RED, 1 },
448 { EL_EMERALD_PURPLE, 1 },
450 { EL_SP_INFOTRON, 1 },
457 static unsigned long trigger_events[MAX_NUM_ELEMENTS];
459 #define IS_AUTO_CHANGING(e) (element_info[e].change_events & \
460 CH_EVENT_BIT(CE_DELAY))
461 #define IS_JUST_CHANGING(x, y) (ChangeDelay[x][y] != 0)
462 #define IS_CHANGING(x, y) (IS_AUTO_CHANGING(Feld[x][y]) || \
463 IS_JUST_CHANGING(x, y))
465 #define CE_PAGE(e, ce) (element_info[e].event_page[ce])
468 void GetPlayerConfig()
470 if (!audio.sound_available)
471 setup.sound_simple = FALSE;
473 if (!audio.loops_available)
474 setup.sound_loops = FALSE;
476 if (!audio.music_available)
477 setup.sound_music = FALSE;
479 if (!video.fullscreen_available)
480 setup.fullscreen = FALSE;
482 setup.sound = (setup.sound_simple || setup.sound_loops || setup.sound_music);
484 SetAudioMode(setup.sound);
488 static int getBeltNrFromBeltElement(int element)
490 return (element < EL_CONVEYOR_BELT_2_LEFT ? 0 :
491 element < EL_CONVEYOR_BELT_3_LEFT ? 1 :
492 element < EL_CONVEYOR_BELT_4_LEFT ? 2 : 3);
495 static int getBeltNrFromBeltActiveElement(int element)
497 return (element < EL_CONVEYOR_BELT_2_LEFT_ACTIVE ? 0 :
498 element < EL_CONVEYOR_BELT_3_LEFT_ACTIVE ? 1 :
499 element < EL_CONVEYOR_BELT_4_LEFT_ACTIVE ? 2 : 3);
502 static int getBeltNrFromBeltSwitchElement(int element)
504 return (element < EL_CONVEYOR_BELT_2_SWITCH_LEFT ? 0 :
505 element < EL_CONVEYOR_BELT_3_SWITCH_LEFT ? 1 :
506 element < EL_CONVEYOR_BELT_4_SWITCH_LEFT ? 2 : 3);
509 static int getBeltDirNrFromBeltSwitchElement(int element)
511 static int belt_base_element[4] =
513 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
514 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
515 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
516 EL_CONVEYOR_BELT_4_SWITCH_LEFT
519 int belt_nr = getBeltNrFromBeltSwitchElement(element);
520 int belt_dir_nr = element - belt_base_element[belt_nr];
522 return (belt_dir_nr % 3);
525 static int getBeltDirFromBeltSwitchElement(int element)
527 static int belt_move_dir[3] =
534 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(element);
536 return belt_move_dir[belt_dir_nr];
539 static void InitPlayerField(int x, int y, int element, boolean init_game)
541 if (element == EL_SP_MURPHY)
545 if (stored_player[0].present)
547 Feld[x][y] = EL_SP_MURPHY_CLONE;
553 stored_player[0].use_murphy_graphic = TRUE;
556 Feld[x][y] = EL_PLAYER_1;
562 struct PlayerInfo *player = &stored_player[Feld[x][y] - EL_PLAYER_1];
563 int jx = player->jx, jy = player->jy;
565 player->present = TRUE;
567 if (!options.network || player->connected)
569 player->active = TRUE;
571 /* remove potentially duplicate players */
572 if (StorePlayer[jx][jy] == Feld[x][y])
573 StorePlayer[jx][jy] = 0;
575 StorePlayer[x][y] = Feld[x][y];
579 printf("Player %d activated.\n", player->element_nr);
580 printf("[Local player is %d and currently %s.]\n",
581 local_player->element_nr,
582 local_player->active ? "active" : "not active");
586 Feld[x][y] = EL_EMPTY;
587 player->jx = player->last_jx = x;
588 player->jy = player->last_jy = y;
592 static void InitField(int x, int y, boolean init_game)
594 int element = Feld[x][y];
603 InitPlayerField(x, y, element, init_game);
607 if (x < lev_fieldx-1 && Feld[x+1][y] == EL_ACID)
608 Feld[x][y] = EL_ACID_POOL_TOPLEFT;
609 else if (x > 0 && Feld[x-1][y] == EL_ACID)
610 Feld[x][y] = EL_ACID_POOL_TOPRIGHT;
611 else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPLEFT)
612 Feld[x][y] = EL_ACID_POOL_BOTTOMLEFT;
613 else if (y > 0 && Feld[x][y-1] == EL_ACID)
614 Feld[x][y] = EL_ACID_POOL_BOTTOM;
615 else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPRIGHT)
616 Feld[x][y] = EL_ACID_POOL_BOTTOMRIGHT;
624 case EL_SPACESHIP_RIGHT:
625 case EL_SPACESHIP_UP:
626 case EL_SPACESHIP_LEFT:
627 case EL_SPACESHIP_DOWN:
629 case EL_BD_BUTTERFLY_RIGHT:
630 case EL_BD_BUTTERFLY_UP:
631 case EL_BD_BUTTERFLY_LEFT:
632 case EL_BD_BUTTERFLY_DOWN:
633 case EL_BD_BUTTERFLY:
634 case EL_BD_FIREFLY_RIGHT:
635 case EL_BD_FIREFLY_UP:
636 case EL_BD_FIREFLY_LEFT:
637 case EL_BD_FIREFLY_DOWN:
639 case EL_PACMAN_RIGHT:
663 if (y == lev_fieldy - 1)
665 Feld[x][y] = EL_AMOEBA_GROWING;
666 Store[x][y] = EL_AMOEBA_WET;
670 case EL_DYNAMITE_ACTIVE:
675 local_player->lights_still_needed++;
678 case EL_SOKOBAN_FIELD_EMPTY:
679 local_player->sokobanfields_still_needed++;
683 local_player->friends_still_needed++;
688 GfxDir[x][y] = MovDir[x][y] = 1 << RND(4);
693 Feld[x][y] = EL_EMPTY;
698 case EL_EM_KEY_1_FILE:
699 Feld[x][y] = EL_EM_KEY_1;
701 case EL_EM_KEY_2_FILE:
702 Feld[x][y] = EL_EM_KEY_2;
704 case EL_EM_KEY_3_FILE:
705 Feld[x][y] = EL_EM_KEY_3;
707 case EL_EM_KEY_4_FILE:
708 Feld[x][y] = EL_EM_KEY_4;
712 case EL_CONVEYOR_BELT_1_SWITCH_LEFT:
713 case EL_CONVEYOR_BELT_1_SWITCH_MIDDLE:
714 case EL_CONVEYOR_BELT_1_SWITCH_RIGHT:
715 case EL_CONVEYOR_BELT_2_SWITCH_LEFT:
716 case EL_CONVEYOR_BELT_2_SWITCH_MIDDLE:
717 case EL_CONVEYOR_BELT_2_SWITCH_RIGHT:
718 case EL_CONVEYOR_BELT_3_SWITCH_LEFT:
719 case EL_CONVEYOR_BELT_3_SWITCH_MIDDLE:
720 case EL_CONVEYOR_BELT_3_SWITCH_RIGHT:
721 case EL_CONVEYOR_BELT_4_SWITCH_LEFT:
722 case EL_CONVEYOR_BELT_4_SWITCH_MIDDLE:
723 case EL_CONVEYOR_BELT_4_SWITCH_RIGHT:
726 int belt_nr = getBeltNrFromBeltSwitchElement(Feld[x][y]);
727 int belt_dir = getBeltDirFromBeltSwitchElement(Feld[x][y]);
728 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(Feld[x][y]);
730 if (game.belt_dir_nr[belt_nr] == 3) /* initial value */
732 game.belt_dir[belt_nr] = belt_dir;
733 game.belt_dir_nr[belt_nr] = belt_dir_nr;
735 else /* more than one switch -- set it like the first switch */
737 Feld[x][y] = Feld[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
742 case EL_SWITCHGATE_SWITCH_DOWN: /* always start with same switch pos */
744 Feld[x][y] = EL_SWITCHGATE_SWITCH_UP;
747 case EL_LIGHT_SWITCH_ACTIVE:
749 game.light_time_left = level.time_light * FRAMES_PER_SECOND;
753 if (IS_CUSTOM_ELEMENT(element) && CAN_MOVE(element))
759 void DrawGameDoorValues()
763 for (i = 0; i < MAX_PLAYERS; i++)
764 for (j = 0; j < 4; j++)
765 if (stored_player[i].key[j])
766 DrawMiniGraphicExt(drawto, DX_KEYS + j * MINI_TILEX, DY_KEYS,
767 el2edimg(EL_KEY_1 + j));
769 DrawText(DX + XX_EMERALDS, DY + YY_EMERALDS,
770 int2str(local_player->gems_still_needed, 3), FONT_TEXT_2);
771 DrawText(DX + XX_DYNAMITE, DY + YY_DYNAMITE,
772 int2str(local_player->inventory_size, 3), FONT_TEXT_2);
773 DrawText(DX + XX_SCORE, DY + YY_SCORE,
774 int2str(local_player->score, 5), FONT_TEXT_2);
775 DrawText(DX + XX_TIME, DY + YY_TIME,
776 int2str(TimeLeft, 3), FONT_TEXT_2);
781 =============================================================================
783 -----------------------------------------------------------------------------
784 initialize game engine due to level / tape version number
785 =============================================================================
788 static void InitGameEngine()
792 /* set game engine from tape file when re-playing, else from level file */
793 game.engine_version = (tape.playing ? tape.engine_version :
796 /* dynamically adjust element properties according to game engine version */
797 InitElementPropertiesEngine(game.engine_version);
800 printf("level %d: level version == %06d\n", level_nr, level.game_version);
801 printf(" tape version == %06d [%s] [file: %06d]\n",
802 tape.engine_version, (tape.playing ? "PLAYING" : "RECORDING"),
804 printf(" => game.engine_version == %06d\n", game.engine_version);
807 /* ---------- initialize player's initial move delay --------------------- */
809 /* dynamically adjust player properties according to game engine version */
810 game.initial_move_delay =
811 (game.engine_version <= VERSION_IDENT(2,0,1,0) ? INITIAL_MOVE_DELAY_ON :
812 INITIAL_MOVE_DELAY_OFF);
814 /* dynamically adjust player properties according to level information */
815 game.initial_move_delay_value =
816 (level.double_speed ? MOVE_DELAY_HIGH_SPEED : MOVE_DELAY_NORMAL_SPEED);
818 /* ---------- initialize player's initial push delay --------------------- */
820 /* dynamically adjust player properties according to game engine version */
821 game.initial_push_delay_value =
822 (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
824 /* ---------- initialize changing elements ------------------------------- */
826 /* initialize changing elements information */
827 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
829 struct ElementInfo *ei = &element_info[i];
831 /* this pointer might have been changed in the level editor */
832 ei->change = &ei->change_page[0];
834 if (!IS_CUSTOM_ELEMENT(i))
836 ei->change->target_element = EL_EMPTY_SPACE;
837 ei->change->delay_fixed = 0;
838 ei->change->delay_random = 0;
839 ei->change->delay_frames = 1;
842 ei->change_events = CE_BITMASK_DEFAULT;
843 for (j = 0; j < NUM_CHANGE_EVENTS; j++)
845 ei->event_page_nr[j] = 0;
846 ei->event_page[j] = &ei->change_page[0];
850 /* add changing elements from pre-defined list */
851 for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
853 struct ChangingElementInfo *ch_delay = &change_delay_list[i];
854 struct ElementInfo *ei = &element_info[ch_delay->element];
856 ei->change->target_element = ch_delay->target_element;
857 ei->change->delay_fixed = ch_delay->change_delay;
859 ei->change->pre_change_function = ch_delay->pre_change_function;
860 ei->change->change_function = ch_delay->change_function;
861 ei->change->post_change_function = ch_delay->post_change_function;
863 ei->change_events |= CH_EVENT_BIT(CE_DELAY);
867 /* add change events from custom element configuration */
868 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
870 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
872 for (j = 0; j < ei->num_change_pages; j++)
874 if (!ei->change_page[j].can_change)
877 for (k = 0; k < NUM_CHANGE_EVENTS; k++)
879 /* only add event page for the first page found with this event */
880 if (ei->change_page[j].events & CH_EVENT_BIT(k) &&
881 !(ei->change_events & CH_EVENT_BIT(k)))
883 ei->change_events |= CH_EVENT_BIT(k);
884 ei->event_page_nr[k] = j;
885 ei->event_page[k] = &ei->change_page[j];
893 /* add change events from custom element configuration */
894 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
896 int element = EL_CUSTOM_START + i;
898 /* only add custom elements that change after fixed/random frame delay */
899 if (CAN_CHANGE(element) && HAS_CHANGE_EVENT(element, CE_DELAY))
900 element_info[element].change_events |= CH_EVENT_BIT(CE_DELAY);
904 /* ---------- initialize trigger events ---------------------------------- */
906 /* initialize trigger events information */
907 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
908 trigger_events[i] = EP_BITMASK_DEFAULT;
911 /* add trigger events from element change event properties */
912 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
914 struct ElementInfo *ei = &element_info[i];
916 for (j = 0; j < ei->num_change_pages; j++)
918 if (!ei->change_page[j].can_change)
921 if (ei->change_page[j].events & CH_EVENT_BIT(CE_BY_OTHER_ACTION))
923 int trigger_element = ei->change_page[j].trigger_element;
925 trigger_events[trigger_element] |= ei->change_page[j].events;
930 /* add trigger events from element change event properties */
931 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
932 if (HAS_CHANGE_EVENT(i, CE_BY_OTHER_ACTION))
933 trigger_events[element_info[i].change->trigger_element] |=
934 element_info[i].change->events;
937 /* ---------- initialize push delay -------------------------------------- */
939 /* initialize push delay values to default */
940 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
942 if (!IS_CUSTOM_ELEMENT(i))
944 element_info[i].push_delay_fixed = game.default_push_delay_fixed;
945 element_info[i].push_delay_random = game.default_push_delay_random;
949 /* set push delay value for certain elements from pre-defined list */
950 for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
952 int e = push_delay_list[i].element;
954 element_info[e].push_delay_fixed = push_delay_list[i].push_delay_fixed;
955 element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
958 /* ---------- initialize move stepsize ----------------------------------- */
960 /* initialize move stepsize values to default */
961 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
962 if (!IS_CUSTOM_ELEMENT(i))
963 element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
965 /* set move stepsize value for certain elements from pre-defined list */
966 for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
968 int e = move_stepsize_list[i].element;
970 element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
973 /* ---------- initialize gem count --------------------------------------- */
975 /* initialize gem count values for each element */
976 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
977 if (!IS_CUSTOM_ELEMENT(i))
978 element_info[i].collect_count = 0;
980 /* add gem count values for all elements from pre-defined list */
981 for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
982 element_info[collect_count_list[i].element].collect_count =
983 collect_count_list[i].count;
988 =============================================================================
990 -----------------------------------------------------------------------------
991 initialize and start new game
992 =============================================================================
997 boolean emulate_bd = TRUE; /* unless non-BOULDERDASH elements found */
998 boolean emulate_sb = TRUE; /* unless non-SOKOBAN elements found */
999 boolean emulate_sp = TRUE; /* unless non-SUPAPLEX elements found */
1006 #if USE_NEW_AMOEBA_CODE
1007 printf("Using new amoeba code.\n");
1009 printf("Using old amoeba code.\n");
1014 /* don't play tapes over network */
1015 network_playing = (options.network && !tape.playing);
1017 for (i = 0; i < MAX_PLAYERS; i++)
1019 struct PlayerInfo *player = &stored_player[i];
1021 player->index_nr = i;
1022 player->element_nr = EL_PLAYER_1 + i;
1024 player->present = FALSE;
1025 player->active = FALSE;
1028 player->effective_action = 0;
1029 player->programmed_action = 0;
1032 player->gems_still_needed = level.gems_needed;
1033 player->sokobanfields_still_needed = 0;
1034 player->lights_still_needed = 0;
1035 player->friends_still_needed = 0;
1037 for (j = 0; j < 4; j++)
1038 player->key[j] = FALSE;
1040 player->dynabomb_count = 0;
1041 player->dynabomb_size = 1;
1042 player->dynabombs_left = 0;
1043 player->dynabomb_xl = FALSE;
1045 player->MovDir = MV_NO_MOVING;
1048 player->GfxDir = MV_NO_MOVING;
1049 player->GfxAction = ACTION_DEFAULT;
1051 player->StepFrame = 0;
1053 player->use_murphy_graphic = FALSE;
1055 player->actual_frame_counter = 0;
1057 player->step_counter = 0;
1059 player->last_move_dir = MV_NO_MOVING;
1061 player->is_waiting = FALSE;
1062 player->is_moving = FALSE;
1063 player->is_digging = FALSE;
1064 player->is_snapping = FALSE;
1065 player->is_collecting = FALSE;
1066 player->is_pushing = FALSE;
1067 player->is_switching = FALSE;
1069 player->is_bored = FALSE;
1070 player->is_sleeping = FALSE;
1072 player->frame_counter_bored = -1;
1073 player->frame_counter_sleeping = -1;
1075 player->anim_delay_counter = 0;
1076 player->post_delay_counter = 0;
1078 player->special_action_bored = ACTION_DEFAULT;
1079 player->special_action_sleeping = ACTION_DEFAULT;
1081 player->num_special_action_bored = 0;
1082 player->num_special_action_sleeping = 0;
1084 /* determine number of special actions for bored and sleeping animation */
1085 for (j = ACTION_BORING_1; j <= ACTION_BORING_LAST; j++)
1087 boolean found = FALSE;
1089 for (k = 0; k < NUM_DIRECTIONS; k++)
1090 if (el_act_dir2img(player->element_nr, j, k) !=
1091 el_act_dir2img(player->element_nr, ACTION_DEFAULT, k))
1095 player->num_special_action_bored++;
1099 for (j = ACTION_SLEEPING_1; j <= ACTION_SLEEPING_LAST; j++)
1101 boolean found = FALSE;
1103 for (k = 0; k < NUM_DIRECTIONS; k++)
1104 if (el_act_dir2img(player->element_nr, j, k) !=
1105 el_act_dir2img(player->element_nr, ACTION_DEFAULT, k))
1109 player->num_special_action_sleeping++;
1114 player->switch_x = -1;
1115 player->switch_y = -1;
1117 player->show_envelope = 0;
1119 player->move_delay = game.initial_move_delay;
1120 player->move_delay_value = game.initial_move_delay_value;
1122 player->push_delay = 0;
1123 player->push_delay_value = game.initial_push_delay_value;
1125 player->last_jx = player->last_jy = 0;
1126 player->jx = player->jy = 0;
1128 player->shield_normal_time_left = 0;
1129 player->shield_deadly_time_left = 0;
1131 player->inventory_size = 0;
1133 DigField(player, 0, 0, 0, 0, DF_NO_PUSH);
1134 SnapField(player, 0, 0);
1136 player->LevelSolved = FALSE;
1137 player->GameOver = FALSE;
1140 network_player_action_received = FALSE;
1142 #if defined(PLATFORM_UNIX)
1143 /* initial null action */
1144 if (network_playing)
1145 SendToServer_MovePlayer(MV_NO_MOVING);
1153 TimeLeft = level.time;
1155 ScreenMovDir = MV_NO_MOVING;
1159 ScrollStepSize = 0; /* will be correctly initialized by ScrollScreen() */
1161 AllPlayersGone = FALSE;
1163 game.yamyam_content_nr = 0;
1164 game.magic_wall_active = FALSE;
1165 game.magic_wall_time_left = 0;
1166 game.light_time_left = 0;
1167 game.timegate_time_left = 0;
1168 game.switchgate_pos = 0;
1169 game.balloon_dir = MV_NO_MOVING;
1170 game.gravity = level.initial_gravity;
1171 game.explosions_delayed = TRUE;
1173 game.envelope_active = FALSE;
1175 for (i = 0; i < 4; i++)
1177 game.belt_dir[i] = MV_NO_MOVING;
1178 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
1181 for (i = 0; i < MAX_NUM_AMOEBA; i++)
1182 AmoebaCnt[i] = AmoebaCnt2[i] = 0;
1184 for (x = 0; x < lev_fieldx; x++)
1186 for (y = 0; y < lev_fieldy; y++)
1188 Feld[x][y] = level.field[x][y];
1189 MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
1190 ChangeDelay[x][y] = 0;
1191 ChangePage[x][y] = -1;
1192 Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
1194 WasJustMoving[x][y] = 0;
1195 WasJustFalling[x][y] = 0;
1197 Pushed[x][y] = FALSE;
1199 Changed[x][y] = CE_BITMASK_DEFAULT;
1200 ChangeEvent[x][y] = CE_BITMASK_DEFAULT;
1202 ExplodePhase[x][y] = 0;
1203 ExplodeField[x][y] = EX_NO_EXPLOSION;
1205 RunnerVisit[x][y] = 0;
1206 PlayerVisit[x][y] = 0;
1209 GfxRandom[x][y] = INIT_GFX_RANDOM();
1210 GfxElement[x][y] = EL_UNDEFINED;
1211 GfxAction[x][y] = ACTION_DEFAULT;
1212 GfxDir[x][y] = MV_NO_MOVING;
1216 for (y = 0; y < lev_fieldy; y++)
1218 for (x = 0; x < lev_fieldx; x++)
1220 if (emulate_bd && !IS_BD_ELEMENT(Feld[x][y]))
1222 if (emulate_sb && !IS_SB_ELEMENT(Feld[x][y]))
1224 if (emulate_sp && !IS_SP_ELEMENT(Feld[x][y]))
1227 InitField(x, y, TRUE);
1233 game.emulation = (emulate_bd ? EMU_BOULDERDASH :
1234 emulate_sb ? EMU_SOKOBAN :
1235 emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
1237 /* correct non-moving belts to start moving left */
1238 for (i = 0; i < 4; i++)
1239 if (game.belt_dir[i] == MV_NO_MOVING)
1240 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
1242 /* check if any connected player was not found in playfield */
1243 for (i = 0; i < MAX_PLAYERS; i++)
1245 struct PlayerInfo *player = &stored_player[i];
1247 if (player->connected && !player->present)
1249 for (j = 0; j < MAX_PLAYERS; j++)
1251 struct PlayerInfo *some_player = &stored_player[j];
1252 int jx = some_player->jx, jy = some_player->jy;
1254 /* assign first free player found that is present in the playfield */
1255 if (some_player->present && !some_player->connected)
1257 player->present = TRUE;
1258 player->active = TRUE;
1259 some_player->present = FALSE;
1261 StorePlayer[jx][jy] = player->element_nr;
1262 player->jx = player->last_jx = jx;
1263 player->jy = player->last_jy = jy;
1273 /* when playing a tape, eliminate all players who do not participate */
1275 for (i = 0; i < MAX_PLAYERS; i++)
1277 if (stored_player[i].active && !tape.player_participates[i])
1279 struct PlayerInfo *player = &stored_player[i];
1280 int jx = player->jx, jy = player->jy;
1282 player->active = FALSE;
1283 StorePlayer[jx][jy] = 0;
1284 Feld[jx][jy] = EL_EMPTY;
1288 else if (!options.network && !setup.team_mode) /* && !tape.playing */
1290 /* when in single player mode, eliminate all but the first active player */
1292 for (i = 0; i < MAX_PLAYERS; i++)
1294 if (stored_player[i].active)
1296 for (j = i + 1; j < MAX_PLAYERS; j++)
1298 if (stored_player[j].active)
1300 struct PlayerInfo *player = &stored_player[j];
1301 int jx = player->jx, jy = player->jy;
1303 player->active = FALSE;
1304 StorePlayer[jx][jy] = 0;
1305 Feld[jx][jy] = EL_EMPTY;
1312 /* when recording the game, store which players take part in the game */
1315 for (i = 0; i < MAX_PLAYERS; i++)
1316 if (stored_player[i].active)
1317 tape.player_participates[i] = TRUE;
1322 for (i = 0; i < MAX_PLAYERS; i++)
1324 struct PlayerInfo *player = &stored_player[i];
1326 printf("Player %d: present == %d, connected == %d, active == %d.\n",
1331 if (local_player == player)
1332 printf("Player %d is local player.\n", i+1);
1336 if (BorderElement == EL_EMPTY)
1339 SBX_Right = lev_fieldx - SCR_FIELDX;
1341 SBY_Lower = lev_fieldy - SCR_FIELDY;
1346 SBX_Right = lev_fieldx - SCR_FIELDX + 1;
1348 SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
1351 if (lev_fieldx + (SBX_Left == -1 ? 2 : 0) <= SCR_FIELDX)
1352 SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
1354 if (lev_fieldy + (SBY_Upper == -1 ? 2 : 0) <= SCR_FIELDY)
1355 SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
1357 /* if local player not found, look for custom element that might create
1358 the player (make some assumptions about the right custom element) */
1359 if (!local_player->present)
1361 int start_x = 0, start_y = 0;
1362 int found_rating = 0;
1363 int found_element = EL_UNDEFINED;
1365 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
1367 int element = Feld[x][y];
1372 if (!IS_CUSTOM_ELEMENT(element))
1375 if (CAN_CHANGE(element))
1377 for (i = 0; i < element_info[element].num_change_pages; i++)
1379 content = element_info[element].change_page[i].target_element;
1380 is_player = ELEM_IS_PLAYER(content);
1382 if (is_player && (found_rating < 3 || element < found_element))
1388 found_element = element;
1393 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
1395 content = element_info[element].content[xx][yy];
1396 is_player = ELEM_IS_PLAYER(content);
1398 if (is_player && (found_rating < 2 || element < found_element))
1400 start_x = x + xx - 1;
1401 start_y = y + yy - 1;
1404 found_element = element;
1407 if (!CAN_CHANGE(element))
1410 for (i = 0; i < element_info[element].num_change_pages; i++)
1412 content = element_info[element].change_page[i].content[xx][yy];
1413 is_player = ELEM_IS_PLAYER(content);
1415 if (is_player && (found_rating < 1 || element < found_element))
1417 start_x = x + xx - 1;
1418 start_y = y + yy - 1;
1421 found_element = element;
1427 scroll_x = (start_x < SBX_Left + MIDPOSX ? SBX_Left :
1428 start_x > SBX_Right + MIDPOSX ? SBX_Right :
1431 scroll_y = (start_y < SBY_Upper + MIDPOSY ? SBY_Upper :
1432 start_y > SBY_Lower + MIDPOSY ? SBY_Lower :
1438 scroll_x = (local_player->jx < SBX_Left + MIDPOSX ? SBX_Left :
1439 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
1440 local_player->jx - MIDPOSX);
1442 scroll_y = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
1443 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
1444 local_player->jy - MIDPOSY);
1446 scroll_x = SBX_Left;
1447 scroll_y = SBY_Upper;
1448 if (local_player->jx >= SBX_Left + MIDPOSX)
1449 scroll_x = (local_player->jx <= SBX_Right + MIDPOSX ?
1450 local_player->jx - MIDPOSX :
1452 if (local_player->jy >= SBY_Upper + MIDPOSY)
1453 scroll_y = (local_player->jy <= SBY_Lower + MIDPOSY ?
1454 local_player->jy - MIDPOSY :
1459 CloseDoor(DOOR_CLOSE_1);
1464 /* after drawing the level, correct some elements */
1465 if (game.timegate_time_left == 0)
1466 CloseAllOpenTimegates();
1468 if (setup.soft_scrolling)
1469 BlitBitmap(fieldbuffer, backbuffer, FX, FY, SXSIZE, SYSIZE, SX, SY);
1471 redraw_mask |= REDRAW_FROM_BACKBUFFER;
1474 /* copy default game door content to main double buffer */
1475 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
1476 DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
1479 DrawText(DX + XX_LEVEL, DY + YY_LEVEL, int2str(level_nr, 2), FONT_TEXT_2);
1482 DrawTextExt(drawto, DX + XX_EMERALDS, DY + YY_EMERALDS,
1483 int2str(level_nr, 3), FONT_LEVEL_NUMBER, BLIT_OPAQUE);
1484 BlitBitmap(drawto, drawto,
1485 DX + XX_EMERALDS, DY + YY_EMERALDS + 1,
1486 getFontWidth(FONT_LEVEL_NUMBER) * 3,
1487 getFontHeight(FONT_LEVEL_NUMBER) - 1,
1488 DX + XX_LEVEL - 1, DY + YY_LEVEL + 1);
1491 DrawGameDoorValues();
1495 game_gadget[SOUND_CTRL_ID_MUSIC]->checked = setup.sound_music;
1496 game_gadget[SOUND_CTRL_ID_LOOPS]->checked = setup.sound_loops;
1497 game_gadget[SOUND_CTRL_ID_SIMPLE]->checked = setup.sound_simple;
1501 /* copy actual game door content to door double buffer for OpenDoor() */
1502 BlitBitmap(drawto, bitmap_db_door,
1503 DX, DY, DXSIZE, DYSIZE, DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1);
1505 OpenDoor(DOOR_OPEN_ALL);
1507 PlaySoundStereo(SND_GAME_STARTING, SOUND_MIDDLE);
1509 if (setup.sound_music)
1512 KeyboardAutoRepeatOffUnlessAutoplay();
1516 for (i = 0; i < 4; i++)
1517 printf("Player %d %sactive.\n",
1518 i + 1, (stored_player[i].active ? "" : "not "));
1522 printf("::: starting game [%d]\n", FrameCounter);
1526 void InitMovDir(int x, int y)
1528 int i, element = Feld[x][y];
1529 static int xy[4][2] =
1536 static int direction[3][4] =
1538 { MV_RIGHT, MV_UP, MV_LEFT, MV_DOWN },
1539 { MV_LEFT, MV_DOWN, MV_RIGHT, MV_UP },
1540 { MV_LEFT, MV_RIGHT, MV_UP, MV_DOWN }
1549 Feld[x][y] = EL_BUG;
1550 MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
1553 case EL_SPACESHIP_RIGHT:
1554 case EL_SPACESHIP_UP:
1555 case EL_SPACESHIP_LEFT:
1556 case EL_SPACESHIP_DOWN:
1557 Feld[x][y] = EL_SPACESHIP;
1558 MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
1561 case EL_BD_BUTTERFLY_RIGHT:
1562 case EL_BD_BUTTERFLY_UP:
1563 case EL_BD_BUTTERFLY_LEFT:
1564 case EL_BD_BUTTERFLY_DOWN:
1565 Feld[x][y] = EL_BD_BUTTERFLY;
1566 MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
1569 case EL_BD_FIREFLY_RIGHT:
1570 case EL_BD_FIREFLY_UP:
1571 case EL_BD_FIREFLY_LEFT:
1572 case EL_BD_FIREFLY_DOWN:
1573 Feld[x][y] = EL_BD_FIREFLY;
1574 MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
1577 case EL_PACMAN_RIGHT:
1579 case EL_PACMAN_LEFT:
1580 case EL_PACMAN_DOWN:
1581 Feld[x][y] = EL_PACMAN;
1582 MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
1585 case EL_SP_SNIKSNAK:
1586 MovDir[x][y] = MV_UP;
1589 case EL_SP_ELECTRON:
1590 MovDir[x][y] = MV_LEFT;
1597 Feld[x][y] = EL_MOLE;
1598 MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
1602 if (IS_CUSTOM_ELEMENT(element))
1604 if (element_info[element].move_direction_initial != MV_NO_MOVING)
1605 MovDir[x][y] = element_info[element].move_direction_initial;
1606 else if (element_info[element].move_pattern == MV_ALL_DIRECTIONS ||
1607 element_info[element].move_pattern == MV_TURNING_LEFT ||
1608 element_info[element].move_pattern == MV_TURNING_RIGHT)
1609 MovDir[x][y] = 1 << RND(4);
1610 else if (element_info[element].move_pattern == MV_HORIZONTAL)
1611 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
1612 else if (element_info[element].move_pattern == MV_VERTICAL)
1613 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
1614 else if (element_info[element].move_pattern & MV_ANY_DIRECTION)
1615 MovDir[x][y] = element_info[element].move_pattern;
1616 else if (element_info[element].move_pattern == MV_ALONG_LEFT_SIDE ||
1617 element_info[element].move_pattern == MV_ALONG_RIGHT_SIDE)
1619 for (i = 0; i < 4; i++)
1621 int x1 = x + xy[i][0];
1622 int y1 = y + xy[i][1];
1624 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
1626 if (element_info[element].move_pattern == MV_ALONG_RIGHT_SIDE)
1627 MovDir[x][y] = direction[0][i];
1629 MovDir[x][y] = direction[1][i];
1638 MovDir[x][y] = 1 << RND(4);
1640 if (element != EL_BUG &&
1641 element != EL_SPACESHIP &&
1642 element != EL_BD_BUTTERFLY &&
1643 element != EL_BD_FIREFLY)
1646 for (i = 0; i < 4; i++)
1648 int x1 = x + xy[i][0];
1649 int y1 = y + xy[i][1];
1651 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
1653 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
1655 MovDir[x][y] = direction[0][i];
1658 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
1659 element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
1661 MovDir[x][y] = direction[1][i];
1670 GfxDir[x][y] = MovDir[x][y];
1673 void InitAmoebaNr(int x, int y)
1676 int group_nr = AmoebeNachbarNr(x, y);
1680 for (i = 1; i < MAX_NUM_AMOEBA; i++)
1682 if (AmoebaCnt[i] == 0)
1690 AmoebaNr[x][y] = group_nr;
1691 AmoebaCnt[group_nr]++;
1692 AmoebaCnt2[group_nr]++;
1698 boolean raise_level = FALSE;
1700 if (local_player->MovPos)
1704 if (tape.auto_play) /* tape might already be stopped here */
1705 tape.auto_play_level_solved = TRUE;
1707 if (tape.playing && tape.auto_play)
1708 tape.auto_play_level_solved = TRUE;
1711 local_player->LevelSolved = FALSE;
1713 PlaySoundStereo(SND_GAME_WINNING, SOUND_MIDDLE);
1717 if (!tape.playing && setup.sound_loops)
1718 PlaySoundExt(SND_GAME_LEVELTIME_BONUS, SOUND_MAX_VOLUME, SOUND_MIDDLE,
1719 SND_CTRL_PLAY_LOOP);
1721 while (TimeLeft > 0)
1723 if (!tape.playing && !setup.sound_loops)
1724 PlaySoundStereo(SND_GAME_LEVELTIME_BONUS, SOUND_MIDDLE);
1725 if (TimeLeft > 0 && !(TimeLeft % 10))
1726 RaiseScore(level.score[SC_TIME_BONUS]);
1727 if (TimeLeft > 100 && !(TimeLeft % 10))
1731 DrawText(DX_TIME, DY_TIME, int2str(TimeLeft, 3), FONT_TEXT_2);
1738 if (!tape.playing && setup.sound_loops)
1739 StopSound(SND_GAME_LEVELTIME_BONUS);
1741 else if (level.time == 0) /* level without time limit */
1743 if (!tape.playing && setup.sound_loops)
1744 PlaySoundExt(SND_GAME_LEVELTIME_BONUS, SOUND_MAX_VOLUME, SOUND_MIDDLE,
1745 SND_CTRL_PLAY_LOOP);
1747 while (TimePlayed < 999)
1749 if (!tape.playing && !setup.sound_loops)
1750 PlaySoundStereo(SND_GAME_LEVELTIME_BONUS, SOUND_MIDDLE);
1751 if (TimePlayed < 999 && !(TimePlayed % 10))
1752 RaiseScore(level.score[SC_TIME_BONUS]);
1753 if (TimePlayed < 900 && !(TimePlayed % 10))
1757 DrawText(DX_TIME, DY_TIME, int2str(TimePlayed, 3), FONT_TEXT_2);
1764 if (!tape.playing && setup.sound_loops)
1765 StopSound(SND_GAME_LEVELTIME_BONUS);
1768 /* close exit door after last player */
1769 if ((Feld[ExitX][ExitY] == EL_EXIT_OPEN ||
1770 Feld[ExitX][ExitY] == EL_SP_EXIT_OPEN) && AllPlayersGone)
1772 int element = Feld[ExitX][ExitY];
1774 Feld[ExitX][ExitY] = (element == EL_EXIT_OPEN ? EL_EXIT_CLOSING :
1775 EL_SP_EXIT_CLOSING);
1777 PlayLevelSoundElementAction(ExitX, ExitY, element, ACTION_CLOSING);
1780 /* Hero disappears */
1781 DrawLevelField(ExitX, ExitY);
1787 CloseDoor(DOOR_CLOSE_1);
1792 SaveTape(tape.level_nr); /* Ask to save tape */
1795 if (level_nr == leveldir_current->handicap_level)
1797 leveldir_current->handicap_level++;
1798 SaveLevelSetup_SeriesInfo();
1801 if (level_editor_test_game)
1802 local_player->score = -1; /* no highscore when playing from editor */
1803 else if (level_nr < leveldir_current->last_level)
1804 raise_level = TRUE; /* advance to next level */
1806 if ((hi_pos = NewHiScore()) >= 0)
1808 game_status = GAME_MODE_SCORES;
1809 DrawHallOfFame(hi_pos);
1818 game_status = GAME_MODE_MAIN;
1835 LoadScore(level_nr);
1837 if (strcmp(setup.player_name, EMPTY_PLAYER_NAME) == 0 ||
1838 local_player->score < highscore[MAX_SCORE_ENTRIES - 1].Score)
1841 for (k = 0; k < MAX_SCORE_ENTRIES; k++)
1843 if (local_player->score > highscore[k].Score)
1845 /* player has made it to the hall of fame */
1847 if (k < MAX_SCORE_ENTRIES - 1)
1849 int m = MAX_SCORE_ENTRIES - 1;
1852 for (l = k; l < MAX_SCORE_ENTRIES; l++)
1853 if (!strcmp(setup.player_name, highscore[l].Name))
1855 if (m == k) /* player's new highscore overwrites his old one */
1859 for (l = m; l > k; l--)
1861 strcpy(highscore[l].Name, highscore[l - 1].Name);
1862 highscore[l].Score = highscore[l - 1].Score;
1869 strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
1870 highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
1871 highscore[k].Score = local_player->score;
1877 else if (!strncmp(setup.player_name, highscore[k].Name,
1878 MAX_PLAYER_NAME_LEN))
1879 break; /* player already there with a higher score */
1885 SaveScore(level_nr);
1890 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
1892 if (player->GfxAction != action || player->GfxDir != dir)
1895 printf("Player frame reset! (%d => %d, %d => %d)\n",
1896 player->GfxAction, action, player->GfxDir, dir);
1899 player->GfxAction = action;
1900 player->GfxDir = dir;
1902 player->StepFrame = 0;
1906 static void ResetRandomAnimationValue(int x, int y)
1908 GfxRandom[x][y] = INIT_GFX_RANDOM();
1911 static void ResetGfxAnimation(int x, int y)
1914 GfxAction[x][y] = ACTION_DEFAULT;
1915 GfxDir[x][y] = MovDir[x][y];
1918 void InitMovingField(int x, int y, int direction)
1920 int element = Feld[x][y];
1921 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
1922 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
1926 if (!WasJustMoving[x][y] || direction != MovDir[x][y])
1927 ResetGfxAnimation(x, y);
1929 MovDir[newx][newy] = MovDir[x][y] = direction;
1930 GfxDir[x][y] = direction;
1932 if (Feld[newx][newy] == EL_EMPTY)
1933 Feld[newx][newy] = EL_BLOCKED;
1935 if (direction == MV_DOWN && CAN_FALL(element))
1936 GfxAction[x][y] = ACTION_FALLING;
1938 GfxAction[x][y] = ACTION_MOVING;
1940 GfxFrame[newx][newy] = GfxFrame[x][y];
1941 GfxRandom[newx][newy] = GfxRandom[x][y];
1942 GfxAction[newx][newy] = GfxAction[x][y];
1943 GfxDir[newx][newy] = GfxDir[x][y];
1946 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
1948 int direction = MovDir[x][y];
1949 int newx = x + (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
1950 int newy = y + (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
1956 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
1958 int oldx = x, oldy = y;
1959 int direction = MovDir[x][y];
1961 if (direction == MV_LEFT)
1963 else if (direction == MV_RIGHT)
1965 else if (direction == MV_UP)
1967 else if (direction == MV_DOWN)
1970 *comes_from_x = oldx;
1971 *comes_from_y = oldy;
1974 int MovingOrBlocked2Element(int x, int y)
1976 int element = Feld[x][y];
1978 if (element == EL_BLOCKED)
1982 Blocked2Moving(x, y, &oldx, &oldy);
1983 return Feld[oldx][oldy];
1989 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
1991 /* like MovingOrBlocked2Element(), but if element is moving
1992 and (x,y) is the field the moving element is just leaving,
1993 return EL_BLOCKED instead of the element value */
1994 int element = Feld[x][y];
1996 if (IS_MOVING(x, y))
1998 if (element == EL_BLOCKED)
2002 Blocked2Moving(x, y, &oldx, &oldy);
2003 return Feld[oldx][oldy];
2012 static void RemoveField(int x, int y)
2014 Feld[x][y] = EL_EMPTY;
2021 ChangeDelay[x][y] = 0;
2022 ChangePage[x][y] = -1;
2023 Pushed[x][y] = FALSE;
2025 GfxElement[x][y] = EL_UNDEFINED;
2026 GfxAction[x][y] = ACTION_DEFAULT;
2027 GfxDir[x][y] = MV_NO_MOVING;
2030 void RemoveMovingField(int x, int y)
2032 int oldx = x, oldy = y, newx = x, newy = y;
2033 int element = Feld[x][y];
2034 int next_element = EL_UNDEFINED;
2036 if (element != EL_BLOCKED && !IS_MOVING(x, y))
2039 if (IS_MOVING(x, y))
2041 Moving2Blocked(x, y, &newx, &newy);
2042 if (Feld[newx][newy] != EL_BLOCKED)
2045 else if (element == EL_BLOCKED)
2047 Blocked2Moving(x, y, &oldx, &oldy);
2048 if (!IS_MOVING(oldx, oldy))
2052 if (element == EL_BLOCKED &&
2053 (Feld[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
2054 Feld[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
2055 Feld[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
2056 Feld[oldx][oldy] == EL_AMOEBA_DROPPING))
2057 next_element = get_next_element(Feld[oldx][oldy]);
2059 RemoveField(oldx, oldy);
2060 RemoveField(newx, newy);
2062 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
2064 if (next_element != EL_UNDEFINED)
2065 Feld[oldx][oldy] = next_element;
2067 DrawLevelField(oldx, oldy);
2068 DrawLevelField(newx, newy);
2071 void DrawDynamite(int x, int y)
2073 int sx = SCREENX(x), sy = SCREENY(y);
2074 int graphic = el2img(Feld[x][y]);
2077 if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
2080 if (IS_WALKABLE_INSIDE(Back[x][y]))
2084 DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
2085 else if (Store[x][y])
2086 DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
2088 frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
2091 if (Back[x][y] || Store[x][y])
2092 DrawGraphicThruMask(sx, sy, graphic, frame);
2094 DrawGraphic(sx, sy, graphic, frame);
2096 if (game.emulation == EMU_SUPAPLEX)
2097 DrawGraphic(sx, sy, IMG_SP_DISK_RED, frame);
2098 else if (Store[x][y])
2099 DrawGraphicThruMask(sx, sy, graphic, frame);
2101 DrawGraphic(sx, sy, graphic, frame);
2105 void CheckDynamite(int x, int y)
2107 if (MovDelay[x][y] != 0) /* dynamite is still waiting to explode */
2111 if (MovDelay[x][y] != 0)
2114 PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
2121 StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
2123 if (Feld[x][y] == EL_DYNAMITE_ACTIVE ||
2124 Feld[x][y] == EL_SP_DISK_RED_ACTIVE)
2125 StopSound(SND_DYNAMITE_ACTIVE);
2127 StopSound(SND_DYNABOMB_ACTIVE);
2133 void RelocatePlayer(int x, int y, int element)
2135 struct PlayerInfo *player = &stored_player[element - EL_PLAYER_1];
2138 RemoveField(x, y); /* temporarily remove newly placed player */
2139 DrawLevelField(x, y);
2142 if (player->present)
2144 while (player->MovPos)
2146 ScrollPlayer(player, SCROLL_GO_ON);
2147 ScrollScreen(NULL, SCROLL_GO_ON);
2153 Delay(GAME_FRAME_DELAY);
2156 DrawPlayer(player); /* needed here only to cleanup last field */
2157 DrawLevelField(player->jx, player->jy); /* remove player graphic */
2159 player->is_moving = FALSE;
2162 Feld[x][y] = element;
2163 InitPlayerField(x, y, element, TRUE);
2165 if (player == local_player)
2167 int scroll_xx = -999, scroll_yy = -999;
2169 while (scroll_xx != scroll_x || scroll_yy != scroll_y)
2172 int fx = FX, fy = FY;
2174 scroll_xx = (local_player->jx < SBX_Left + MIDPOSX ? SBX_Left :
2175 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
2176 local_player->jx - MIDPOSX);
2178 scroll_yy = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
2179 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
2180 local_player->jy - MIDPOSY);
2182 dx = (scroll_xx < scroll_x ? +1 : scroll_xx > scroll_x ? -1 : 0);
2183 dy = (scroll_yy < scroll_y ? +1 : scroll_yy > scroll_y ? -1 : 0);
2188 fx += dx * TILEX / 2;
2189 fy += dy * TILEY / 2;
2191 ScrollLevel(dx, dy);
2194 /* scroll in two steps of half tile size to make things smoother */
2195 BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
2197 Delay(GAME_FRAME_DELAY);
2199 /* scroll second step to align at full tile size */
2201 Delay(GAME_FRAME_DELAY);
2206 void Explode(int ex, int ey, int phase, int mode)
2210 int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
2211 int last_phase = num_phase * delay;
2212 int half_phase = (num_phase / 2) * delay;
2213 int first_phase_after_start = EX_PHASE_START + 1;
2215 if (game.explosions_delayed)
2217 ExplodeField[ex][ey] = mode;
2221 if (phase == EX_PHASE_START) /* initialize 'Store[][]' field */
2223 int center_element = Feld[ex][ey];
2226 /* --- This is only really needed (and now handled) in "Impact()". --- */
2227 /* do not explode moving elements that left the explode field in time */
2228 if (game.engine_version >= VERSION_IDENT(2,2,0,7) &&
2229 center_element == EL_EMPTY && (mode == EX_NORMAL || mode == EX_CENTER))
2233 if (mode == EX_NORMAL || mode == EX_CENTER)
2234 PlayLevelSoundAction(ex, ey, ACTION_EXPLODING);
2236 /* remove things displayed in background while burning dynamite */
2237 if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
2240 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
2242 /* put moving element to center field (and let it explode there) */
2243 center_element = MovingOrBlocked2Element(ex, ey);
2244 RemoveMovingField(ex, ey);
2245 Feld[ex][ey] = center_element;
2248 for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
2250 int xx = x - ex + 1;
2251 int yy = y - ey + 1;
2254 if (!IN_LEV_FIELD(x, y) ||
2255 ((mode != EX_NORMAL || center_element == EL_AMOEBA_TO_DIAMOND) &&
2256 (x != ex || y != ey)))
2259 element = Feld[x][y];
2261 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
2263 element = MovingOrBlocked2Element(x, y);
2265 if (!IS_EXPLOSION_PROOF(element))
2266 RemoveMovingField(x, y);
2272 if (IS_EXPLOSION_PROOF(element))
2275 /* indestructible elements can only explode in center (but not flames) */
2276 if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey)) ||
2277 element == EL_FLAMES)
2282 if ((IS_INDESTRUCTIBLE(element) &&
2283 (game.engine_version < VERSION_IDENT(2,2,0,0) ||
2284 (!IS_WALKABLE_OVER(element) && !IS_WALKABLE_UNDER(element)))) ||
2285 element == EL_FLAMES)
2289 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
2291 if (IS_ACTIVE_BOMB(element))
2293 /* re-activate things under the bomb like gate or penguin */
2294 Feld[x][y] = (Store[x][y] ? Store[x][y] : EL_EMPTY);
2301 /* save walkable background elements while explosion on same tile */
2303 if (IS_INDESTRUCTIBLE(element))
2304 Back[x][y] = element;
2306 if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element))
2307 Back[x][y] = element;
2310 /* ignite explodable elements reached by other explosion */
2311 if (element == EL_EXPLOSION)
2312 element = Store2[x][y];
2315 if (AmoebaNr[x][y] &&
2316 (element == EL_AMOEBA_FULL ||
2317 element == EL_BD_AMOEBA ||
2318 element == EL_AMOEBA_GROWING))
2320 AmoebaCnt[AmoebaNr[x][y]]--;
2321 AmoebaCnt2[AmoebaNr[x][y]]--;
2327 if (IS_PLAYER(ex, ey) && !PLAYER_PROTECTED(ex, ey))
2329 switch(StorePlayer[ex][ey])
2332 Store[x][y] = EL_EMERALD_RED;
2335 Store[x][y] = EL_EMERALD;
2338 Store[x][y] = EL_EMERALD_PURPLE;
2342 Store[x][y] = EL_EMERALD_YELLOW;
2346 if (game.emulation == EMU_SUPAPLEX)
2347 Store[x][y] = EL_EMPTY;
2349 else if (center_element == EL_MOLE)
2350 Store[x][y] = EL_EMERALD_RED;
2351 else if (center_element == EL_PENGUIN)
2352 Store[x][y] = EL_EMERALD_PURPLE;
2353 else if (center_element == EL_BUG)
2354 Store[x][y] = ((x == ex && y == ey) ? EL_DIAMOND : EL_EMERALD);
2355 else if (center_element == EL_BD_BUTTERFLY)
2356 Store[x][y] = EL_BD_DIAMOND;
2357 else if (center_element == EL_SP_ELECTRON)
2358 Store[x][y] = EL_SP_INFOTRON;
2359 else if (center_element == EL_AMOEBA_TO_DIAMOND)
2360 Store[x][y] = level.amoeba_content;
2361 else if (center_element == EL_YAMYAM)
2362 Store[x][y] = level.yamyam_content[game.yamyam_content_nr][xx][yy];
2363 else if (IS_CUSTOM_ELEMENT(center_element) &&
2364 element_info[center_element].content[xx][yy] != EL_EMPTY)
2365 Store[x][y] = element_info[center_element].content[xx][yy];
2366 else if (element == EL_WALL_EMERALD)
2367 Store[x][y] = EL_EMERALD;
2368 else if (element == EL_WALL_DIAMOND)
2369 Store[x][y] = EL_DIAMOND;
2370 else if (element == EL_WALL_BD_DIAMOND)
2371 Store[x][y] = EL_BD_DIAMOND;
2372 else if (element == EL_WALL_EMERALD_YELLOW)
2373 Store[x][y] = EL_EMERALD_YELLOW;
2374 else if (element == EL_WALL_EMERALD_RED)
2375 Store[x][y] = EL_EMERALD_RED;
2376 else if (element == EL_WALL_EMERALD_PURPLE)
2377 Store[x][y] = EL_EMERALD_PURPLE;
2378 else if (element == EL_WALL_PEARL)
2379 Store[x][y] = EL_PEARL;
2380 else if (element == EL_WALL_CRYSTAL)
2381 Store[x][y] = EL_CRYSTAL;
2382 else if (IS_CUSTOM_ELEMENT(element) && !CAN_EXPLODE(element))
2383 Store[x][y] = element_info[element].content[1][1];
2385 Store[x][y] = EL_EMPTY;
2387 if (x != ex || y != ey ||
2388 center_element == EL_AMOEBA_TO_DIAMOND || mode == EX_BORDER)
2389 Store2[x][y] = element;
2392 if (AmoebaNr[x][y] &&
2393 (element == EL_AMOEBA_FULL ||
2394 element == EL_BD_AMOEBA ||
2395 element == EL_AMOEBA_GROWING))
2397 AmoebaCnt[AmoebaNr[x][y]]--;
2398 AmoebaCnt2[AmoebaNr[x][y]]--;
2404 MovDir[x][y] = MovPos[x][y] = 0;
2405 GfxDir[x][y] = MovDir[x][y];
2410 Feld[x][y] = EL_EXPLOSION;
2412 GfxElement[x][y] = center_element;
2414 GfxElement[x][y] = EL_UNDEFINED;
2417 ExplodePhase[x][y] = 1;
2421 if (center_element == EL_YAMYAM)
2422 game.yamyam_content_nr =
2423 (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
2434 ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
2438 /* activate this even in non-DEBUG version until cause for crash in
2439 getGraphicAnimationFrame() (see below) is found and eliminated */
2443 if (GfxElement[x][y] == EL_UNDEFINED)
2446 printf("Explode(): x = %d, y = %d: GfxElement == EL_UNDEFINED\n", x, y);
2447 printf("Explode(): This should never happen!\n");
2450 GfxElement[x][y] = EL_EMPTY;
2454 if (phase == first_phase_after_start)
2456 int element = Store2[x][y];
2458 if (element == EL_BLACK_ORB)
2460 Feld[x][y] = Store2[x][y];
2465 else if (phase == half_phase)
2467 int element = Store2[x][y];
2469 if (IS_PLAYER(x, y))
2470 KillHeroUnlessProtected(x, y);
2471 else if (CAN_EXPLODE_BY_FIRE(element))
2473 Feld[x][y] = Store2[x][y];
2477 else if (element == EL_AMOEBA_TO_DIAMOND)
2478 AmoebeUmwandeln(x, y);
2481 if (phase == last_phase)
2485 element = Feld[x][y] = Store[x][y];
2486 Store[x][y] = Store2[x][y] = 0;
2487 GfxElement[x][y] = EL_UNDEFINED;
2489 if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
2490 element = Feld[x][y] = Back[x][y];
2493 MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
2494 GfxDir[x][y] = MV_NO_MOVING;
2495 ChangeDelay[x][y] = 0;
2496 ChangePage[x][y] = -1;
2498 InitField(x, y, FALSE);
2499 if (CAN_MOVE(element))
2501 DrawLevelField(x, y);
2503 TestIfElementTouchesCustomElement(x, y);
2505 if (GFX_CRUMBLED(element))
2506 DrawLevelFieldCrumbledSandNeighbours(x, y);
2508 if (IS_PLAYER(x, y) && !PLAYERINFO(x,y)->present)
2509 StorePlayer[x][y] = 0;
2511 if (ELEM_IS_PLAYER(element))
2512 RelocatePlayer(x, y, element);
2514 else if (phase >= delay && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
2517 int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
2519 int stored = Store[x][y];
2520 int graphic = (game.emulation != EMU_SUPAPLEX ? IMG_EXPLOSION :
2521 stored == EL_SP_INFOTRON ? IMG_SP_EXPLOSION_INFOTRON :
2524 int frame = getGraphicAnimationFrame(graphic, phase - delay);
2527 DrawLevelFieldCrumbledSand(x, y);
2529 if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
2531 DrawLevelElement(x, y, Back[x][y]);
2532 DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
2534 else if (IS_WALKABLE_UNDER(Back[x][y]))
2536 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
2537 DrawLevelElementThruMask(x, y, Back[x][y]);
2539 else if (!IS_WALKABLE_INSIDE(Back[x][y]))
2540 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
2544 void DynaExplode(int ex, int ey)
2547 int dynabomb_size = 1;
2548 boolean dynabomb_xl = FALSE;
2549 struct PlayerInfo *player;
2550 static int xy[4][2] =
2558 if (IS_ACTIVE_BOMB(Feld[ex][ey]))
2560 player = &stored_player[Feld[ex][ey] - EL_DYNABOMB_PLAYER_1_ACTIVE];
2561 dynabomb_size = player->dynabomb_size;
2562 dynabomb_xl = player->dynabomb_xl;
2563 player->dynabombs_left++;
2566 Explode(ex, ey, EX_PHASE_START, EX_CENTER);
2568 for (i = 0; i < 4; i++)
2570 for (j = 1; j <= dynabomb_size; j++)
2572 int x = ex + j * xy[i % 4][0];
2573 int y = ey + j * xy[i % 4][1];
2576 if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
2579 element = Feld[x][y];
2581 /* do not restart explosions of fields with active bombs */
2582 if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
2585 Explode(x, y, EX_PHASE_START, EX_BORDER);
2587 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
2588 if (element != EL_EMPTY &&
2589 element != EL_SAND &&
2590 element != EL_EXPLOSION &&
2597 void Bang(int x, int y)
2600 int element = MovingOrBlocked2Element(x, y);
2602 int element = Feld[x][y];
2606 if (IS_PLAYER(x, y) && !PLAYER_PROTECTED(x, y))
2608 if (IS_PLAYER(x, y))
2611 struct PlayerInfo *player = PLAYERINFO(x, y);
2613 element = Feld[x][y] = (player->use_murphy_graphic ? EL_SP_MURPHY :
2614 player->element_nr);
2619 PlayLevelSoundAction(x, y, ACTION_EXPLODING);
2621 if (game.emulation == EMU_SUPAPLEX)
2622 PlayLevelSound(x, y, SND_SP_ELEMENT_EXPLODING);
2624 PlayLevelSound(x, y, SND_ELEMENT_EXPLODING);
2629 if (IS_PLAYER(x, y)) /* remove objects that might cause smaller explosion */
2637 case EL_BD_BUTTERFLY:
2640 case EL_DARK_YAMYAM:
2644 RaiseScoreElement(element);
2645 Explode(x, y, EX_PHASE_START, EX_NORMAL);
2647 case EL_DYNABOMB_PLAYER_1_ACTIVE:
2648 case EL_DYNABOMB_PLAYER_2_ACTIVE:
2649 case EL_DYNABOMB_PLAYER_3_ACTIVE:
2650 case EL_DYNABOMB_PLAYER_4_ACTIVE:
2651 case EL_DYNABOMB_INCREASE_NUMBER:
2652 case EL_DYNABOMB_INCREASE_SIZE:
2653 case EL_DYNABOMB_INCREASE_POWER:
2658 case EL_LAMP_ACTIVE:
2659 if (IS_PLAYER(x, y))
2660 Explode(x, y, EX_PHASE_START, EX_NORMAL);
2662 Explode(x, y, EX_PHASE_START, EX_CENTER);
2665 if (CAN_EXPLODE_1X1(element))
2666 Explode(x, y, EX_PHASE_START, EX_CENTER);
2668 Explode(x, y, EX_PHASE_START, EX_NORMAL);
2672 CheckTriggeredElementChange(x, y, element, CE_OTHER_IS_EXPLODING);
2675 void SplashAcid(int x, int y)
2677 int element = Feld[x][y];
2679 if (element != EL_ACID_SPLASH_LEFT &&
2680 element != EL_ACID_SPLASH_RIGHT)
2682 PlayLevelSound(x, y, SND_ACID_SPLASHING);
2684 if (IN_LEV_FIELD(x-1, y) && IS_FREE(x-1, y) &&
2685 (!IN_LEV_FIELD(x-1, y-1) ||
2686 !CAN_FALL(MovingOrBlocked2Element(x-1, y-1))))
2687 Feld[x-1][y] = EL_ACID_SPLASH_LEFT;
2689 if (IN_LEV_FIELD(x+1, y) && IS_FREE(x+1, y) &&
2690 (!IN_LEV_FIELD(x+1, y-1) ||
2691 !CAN_FALL(MovingOrBlocked2Element(x+1, y-1))))
2692 Feld[x+1][y] = EL_ACID_SPLASH_RIGHT;
2696 static void InitBeltMovement()
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
2715 /* set frame order for belt animation graphic according to belt direction */
2716 for (i = 0; i < 4; i++)
2720 for (j = 0; j < 3; j++)
2722 int element = belt_base_active_element[belt_nr] + j;
2723 int graphic = el2img(element);
2725 if (game.belt_dir[i] == MV_LEFT)
2726 graphic_info[graphic].anim_mode &= ~ANIM_REVERSE;
2728 graphic_info[graphic].anim_mode |= ANIM_REVERSE;
2732 for (y = 0; y < lev_fieldy; y++)
2734 for (x = 0; x < lev_fieldx; x++)
2736 int element = Feld[x][y];
2738 for (i = 0; i < 4; i++)
2740 if (IS_BELT(element) && game.belt_dir[i] != MV_NO_MOVING)
2742 int e_belt_nr = getBeltNrFromBeltElement(element);
2745 if (e_belt_nr == belt_nr)
2747 int belt_part = Feld[x][y] - belt_base_element[belt_nr];
2749 Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
2757 static void ToggleBeltSwitch(int x, int y)
2759 static int belt_base_element[4] =
2761 EL_CONVEYOR_BELT_1_LEFT,
2762 EL_CONVEYOR_BELT_2_LEFT,
2763 EL_CONVEYOR_BELT_3_LEFT,
2764 EL_CONVEYOR_BELT_4_LEFT
2766 static int belt_base_active_element[4] =
2768 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
2769 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
2770 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
2771 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
2773 static int belt_base_switch_element[4] =
2775 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
2776 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
2777 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
2778 EL_CONVEYOR_BELT_4_SWITCH_LEFT
2780 static int belt_move_dir[4] =
2788 int element = Feld[x][y];
2789 int belt_nr = getBeltNrFromBeltSwitchElement(element);
2790 int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
2791 int belt_dir = belt_move_dir[belt_dir_nr];
2794 if (!IS_BELT_SWITCH(element))
2797 game.belt_dir_nr[belt_nr] = belt_dir_nr;
2798 game.belt_dir[belt_nr] = belt_dir;
2800 if (belt_dir_nr == 3)
2803 /* set frame order for belt animation graphic according to belt direction */
2804 for (i = 0; i < 3; i++)
2806 int element = belt_base_active_element[belt_nr] + i;
2807 int graphic = el2img(element);
2809 if (belt_dir == MV_LEFT)
2810 graphic_info[graphic].anim_mode &= ~ANIM_REVERSE;
2812 graphic_info[graphic].anim_mode |= ANIM_REVERSE;
2815 for (yy = 0; yy < lev_fieldy; yy++)
2817 for (xx = 0; xx < lev_fieldx; xx++)
2819 int element = Feld[xx][yy];
2821 if (IS_BELT_SWITCH(element))
2823 int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
2825 if (e_belt_nr == belt_nr)
2827 Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
2828 DrawLevelField(xx, yy);
2831 else if (IS_BELT(element) && belt_dir != MV_NO_MOVING)
2833 int e_belt_nr = getBeltNrFromBeltElement(element);
2835 if (e_belt_nr == belt_nr)
2837 int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
2839 Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
2840 DrawLevelField(xx, yy);
2843 else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NO_MOVING)
2845 int e_belt_nr = getBeltNrFromBeltActiveElement(element);
2847 if (e_belt_nr == belt_nr)
2849 int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
2851 Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
2852 DrawLevelField(xx, yy);
2859 static void ToggleSwitchgateSwitch(int x, int y)
2863 game.switchgate_pos = !game.switchgate_pos;
2865 for (yy = 0; yy < lev_fieldy; yy++)
2867 for (xx = 0; xx < lev_fieldx; xx++)
2869 int element = Feld[xx][yy];
2871 if (element == EL_SWITCHGATE_SWITCH_UP ||
2872 element == EL_SWITCHGATE_SWITCH_DOWN)
2874 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
2875 DrawLevelField(xx, yy);
2877 else if (element == EL_SWITCHGATE_OPEN ||
2878 element == EL_SWITCHGATE_OPENING)
2880 Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
2882 PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
2884 PlayLevelSound(xx, yy, SND_SWITCHGATE_CLOSING);
2887 else if (element == EL_SWITCHGATE_CLOSED ||
2888 element == EL_SWITCHGATE_CLOSING)
2890 Feld[xx][yy] = EL_SWITCHGATE_OPENING;
2892 PlayLevelSoundAction(xx, yy, ACTION_OPENING);
2894 PlayLevelSound(xx, yy, SND_SWITCHGATE_OPENING);
2901 static int getInvisibleActiveFromInvisibleElement(int element)
2903 return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
2904 element == EL_INVISIBLE_WALL ? EL_INVISIBLE_WALL_ACTIVE :
2905 element == EL_INVISIBLE_SAND ? EL_INVISIBLE_SAND_ACTIVE :
2909 static int getInvisibleFromInvisibleActiveElement(int element)
2911 return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
2912 element == EL_INVISIBLE_WALL_ACTIVE ? EL_INVISIBLE_WALL :
2913 element == EL_INVISIBLE_SAND_ACTIVE ? EL_INVISIBLE_SAND :
2917 static void RedrawAllLightSwitchesAndInvisibleElements()
2921 for (y = 0; y < lev_fieldy; y++)
2923 for (x = 0; x < lev_fieldx; x++)
2925 int element = Feld[x][y];
2927 if (element == EL_LIGHT_SWITCH &&
2928 game.light_time_left > 0)
2930 Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
2931 DrawLevelField(x, y);
2933 else if (element == EL_LIGHT_SWITCH_ACTIVE &&
2934 game.light_time_left == 0)
2936 Feld[x][y] = EL_LIGHT_SWITCH;
2937 DrawLevelField(x, y);
2939 else if (element == EL_INVISIBLE_STEELWALL ||
2940 element == EL_INVISIBLE_WALL ||
2941 element == EL_INVISIBLE_SAND)
2943 if (game.light_time_left > 0)
2944 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
2946 DrawLevelField(x, y);
2948 else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
2949 element == EL_INVISIBLE_WALL_ACTIVE ||
2950 element == EL_INVISIBLE_SAND_ACTIVE)
2952 if (game.light_time_left == 0)
2953 Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
2955 DrawLevelField(x, y);
2961 static void ToggleLightSwitch(int x, int y)
2963 int element = Feld[x][y];
2965 game.light_time_left =
2966 (element == EL_LIGHT_SWITCH ?
2967 level.time_light * FRAMES_PER_SECOND : 0);
2969 RedrawAllLightSwitchesAndInvisibleElements();
2972 static void ActivateTimegateSwitch(int x, int y)
2976 game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
2978 for (yy = 0; yy < lev_fieldy; yy++)
2980 for (xx = 0; xx < lev_fieldx; xx++)
2982 int element = Feld[xx][yy];
2984 if (element == EL_TIMEGATE_CLOSED ||
2985 element == EL_TIMEGATE_CLOSING)
2987 Feld[xx][yy] = EL_TIMEGATE_OPENING;
2988 PlayLevelSound(xx, yy, SND_TIMEGATE_OPENING);
2992 else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
2994 Feld[xx][yy] = EL_TIMEGATE_SWITCH;
2995 DrawLevelField(xx, yy);
3002 Feld[x][y] = EL_TIMEGATE_SWITCH_ACTIVE;
3005 inline static int getElementMoveStepsize(int x, int y)
3007 int element = Feld[x][y];
3008 int direction = MovDir[x][y];
3009 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
3010 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
3011 int horiz_move = (dx != 0);
3012 int sign = (horiz_move ? dx : dy);
3013 int step = sign * element_info[element].move_stepsize;
3015 /* special values for move stepsize for spring and things on conveyor belt */
3018 if (CAN_FALL(element) &&
3019 y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
3020 step = sign * MOVE_STEPSIZE_NORMAL / 2;
3021 else if (element == EL_SPRING)
3022 step = sign * MOVE_STEPSIZE_NORMAL * 2;
3028 void Impact(int x, int y)
3030 boolean lastline = (y == lev_fieldy-1);
3031 boolean object_hit = FALSE;
3032 boolean impact = (lastline || object_hit);
3033 int element = Feld[x][y];
3034 int smashed = EL_UNDEFINED;
3036 if (!lastline) /* check if element below was hit */
3038 if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
3041 object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
3042 MovDir[x][y + 1] != MV_DOWN ||
3043 MovPos[x][y + 1] <= TILEY / 2));
3045 /* do not smash moving elements that left the smashed field in time */
3046 if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
3047 ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
3051 smashed = MovingOrBlocked2Element(x, y + 1);
3053 impact = (lastline || object_hit);
3056 if (!lastline && smashed == EL_ACID) /* element falls into acid */
3062 /* only reset graphic animation if graphic really changes after impact */
3064 el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
3066 ResetGfxAnimation(x, y);
3067 DrawLevelField(x, y);
3070 if (impact && CAN_EXPLODE_IMPACT(element))
3075 else if (impact && element == EL_PEARL)
3077 Feld[x][y] = EL_PEARL_BREAKING;
3078 PlayLevelSound(x, y, SND_PEARL_BREAKING);
3081 else if (impact && CheckElementChange(x, y, element, CE_IMPACT))
3083 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
3088 if (impact && element == EL_AMOEBA_DROP)
3090 if (object_hit && IS_PLAYER(x, y + 1))
3091 KillHeroUnlessProtected(x, y + 1);
3092 else if (object_hit && smashed == EL_PENGUIN)
3096 Feld[x][y] = EL_AMOEBA_GROWING;
3097 Store[x][y] = EL_AMOEBA_WET;
3099 ResetRandomAnimationValue(x, y);
3104 if (object_hit) /* check which object was hit */
3106 if (CAN_PASS_MAGIC_WALL(element) &&
3107 (smashed == EL_MAGIC_WALL ||
3108 smashed == EL_BD_MAGIC_WALL))
3111 int activated_magic_wall =
3112 (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
3113 EL_BD_MAGIC_WALL_ACTIVE);
3115 /* activate magic wall / mill */
3116 for (yy = 0; yy < lev_fieldy; yy++)
3117 for (xx = 0; xx < lev_fieldx; xx++)
3118 if (Feld[xx][yy] == smashed)
3119 Feld[xx][yy] = activated_magic_wall;
3121 game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
3122 game.magic_wall_active = TRUE;
3124 PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
3125 SND_MAGIC_WALL_ACTIVATING :
3126 SND_BD_MAGIC_WALL_ACTIVATING));
3129 if (IS_PLAYER(x, y + 1))
3131 if (CAN_SMASH_PLAYER(element))
3133 KillHeroUnlessProtected(x, y + 1);
3137 else if (smashed == EL_PENGUIN)
3139 if (CAN_SMASH_PLAYER(element))
3145 else if (element == EL_BD_DIAMOND)
3147 if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
3153 else if ((element == EL_SP_INFOTRON ||
3154 element == EL_SP_ZONK) &&
3155 (smashed == EL_SP_SNIKSNAK ||
3156 smashed == EL_SP_ELECTRON ||
3157 smashed == EL_SP_DISK_ORANGE))
3163 else if (CAN_SMASH_ENEMIES(element) && IS_CLASSIC_ENEMY(smashed))
3169 else if (CAN_SMASH_EVERYTHING(element))
3171 if (IS_CLASSIC_ENEMY(smashed) ||
3172 CAN_EXPLODE_SMASHED(smashed))
3177 else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
3179 if (smashed == EL_LAMP ||
3180 smashed == EL_LAMP_ACTIVE)
3185 else if (smashed == EL_NUT)
3187 Feld[x][y + 1] = EL_NUT_BREAKING;
3188 PlayLevelSound(x, y, SND_NUT_BREAKING);
3189 RaiseScoreElement(EL_NUT);
3192 else if (smashed == EL_PEARL)
3194 Feld[x][y + 1] = EL_PEARL_BREAKING;
3195 PlayLevelSound(x, y, SND_PEARL_BREAKING);
3198 else if (smashed == EL_DIAMOND)
3200 Feld[x][y + 1] = EL_DIAMOND_BREAKING;
3201 PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
3204 else if (IS_BELT_SWITCH(smashed))
3206 ToggleBeltSwitch(x, y + 1);
3208 else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
3209 smashed == EL_SWITCHGATE_SWITCH_DOWN)
3211 ToggleSwitchgateSwitch(x, y + 1);
3213 else if (smashed == EL_LIGHT_SWITCH ||
3214 smashed == EL_LIGHT_SWITCH_ACTIVE)
3216 ToggleLightSwitch(x, y + 1);
3220 CheckElementChange(x, y + 1, smashed, CE_SMASHED);
3222 CheckTriggeredElementSideChange(x, y + 1, smashed, CH_SIDE_TOP,
3223 CE_OTHER_IS_SWITCHING);
3224 CheckElementSideChange(x, y + 1, smashed, CH_SIDE_TOP,
3230 CheckElementChange(x, y + 1, smashed, CE_SMASHED);
3235 /* play sound of magic wall / mill */
3237 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
3238 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE))
3240 if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
3241 PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
3242 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
3243 PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
3248 /* play sound of object that hits the ground */
3249 if (lastline || object_hit)
3250 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
3253 inline static void TurnRoundExt(int x, int y)
3265 { 0, 0 }, { 0, 0 }, { 0, 0 },
3270 int left, right, back;
3274 { MV_DOWN, MV_UP, MV_RIGHT },
3275 { MV_UP, MV_DOWN, MV_LEFT },
3277 { MV_LEFT, MV_RIGHT, MV_DOWN },
3281 { MV_RIGHT, MV_LEFT, MV_UP }
3284 int element = Feld[x][y];
3285 int move_pattern = element_info[element].move_pattern;
3287 int old_move_dir = MovDir[x][y];
3288 int left_dir = turn[old_move_dir].left;
3289 int right_dir = turn[old_move_dir].right;
3290 int back_dir = turn[old_move_dir].back;
3292 int left_dx = move_xy[left_dir].x, left_dy = move_xy[left_dir].y;
3293 int right_dx = move_xy[right_dir].x, right_dy = move_xy[right_dir].y;
3294 int move_dx = move_xy[old_move_dir].x, move_dy = move_xy[old_move_dir].y;
3295 int back_dx = move_xy[back_dir].x, back_dy = move_xy[back_dir].y;
3297 int left_x = x + left_dx, left_y = y + left_dy;
3298 int right_x = x + right_dx, right_y = y + right_dy;
3299 int move_x = x + move_dx, move_y = y + move_dy;
3303 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
3305 TestIfBadThingTouchesOtherBadThing(x, y);
3307 if (ENEMY_CAN_ENTER_FIELD(right_x, right_y))
3308 MovDir[x][y] = right_dir;
3309 else if (!ENEMY_CAN_ENTER_FIELD(move_x, move_y))
3310 MovDir[x][y] = left_dir;
3312 if (element == EL_BUG && MovDir[x][y] != old_move_dir)
3314 else if (element == EL_BD_BUTTERFLY) /* && MovDir[x][y] == left_dir) */
3317 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
3318 element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
3320 TestIfBadThingTouchesOtherBadThing(x, y);
3322 if (ENEMY_CAN_ENTER_FIELD(left_x, left_y))
3323 MovDir[x][y] = left_dir;
3324 else if (!ENEMY_CAN_ENTER_FIELD(move_x, move_y))
3325 MovDir[x][y] = right_dir;
3327 if ((element == EL_SPACESHIP ||
3328 element == EL_SP_SNIKSNAK ||
3329 element == EL_SP_ELECTRON)
3330 && MovDir[x][y] != old_move_dir)
3332 else if (element == EL_BD_FIREFLY) /* && MovDir[x][y] == right_dir) */
3335 else if (element == EL_YAMYAM)
3337 boolean can_turn_left = YAMYAM_CAN_ENTER_FIELD(left_x, left_y);
3338 boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(right_x, right_y);
3340 if (can_turn_left && can_turn_right)
3341 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
3342 else if (can_turn_left)
3343 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
3344 else if (can_turn_right)
3345 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
3347 MovDir[x][y] = back_dir;
3349 MovDelay[x][y] = 16 + 16 * RND(3);
3351 else if (element == EL_DARK_YAMYAM)
3353 boolean can_turn_left = DARK_YAMYAM_CAN_ENTER_FIELD(left_x, left_y);
3354 boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(right_x, right_y);
3356 if (can_turn_left && can_turn_right)
3357 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
3358 else if (can_turn_left)
3359 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
3360 else if (can_turn_right)
3361 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
3363 MovDir[x][y] = back_dir;
3365 MovDelay[x][y] = 16 + 16 * RND(3);
3367 else if (element == EL_PACMAN)
3369 boolean can_turn_left = PACMAN_CAN_ENTER_FIELD(left_x, left_y);
3370 boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(right_x, right_y);
3372 if (can_turn_left && can_turn_right)
3373 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
3374 else if (can_turn_left)
3375 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
3376 else if (can_turn_right)
3377 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
3379 MovDir[x][y] = back_dir;
3381 MovDelay[x][y] = 6 + RND(40);
3383 else if (element == EL_PIG)
3385 boolean can_turn_left = PIG_CAN_ENTER_FIELD(left_x, left_y);
3386 boolean can_turn_right = PIG_CAN_ENTER_FIELD(right_x, right_y);
3387 boolean can_move_on = PIG_CAN_ENTER_FIELD(move_x, move_y);
3388 boolean should_turn_left, should_turn_right, should_move_on;
3390 int rnd = RND(rnd_value);
3392 should_turn_left = (can_turn_left &&
3394 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
3395 y + back_dy + left_dy)));
3396 should_turn_right = (can_turn_right &&
3398 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
3399 y + back_dy + right_dy)));
3400 should_move_on = (can_move_on &&
3403 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
3404 y + move_dy + left_dy) ||
3405 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
3406 y + move_dy + right_dy)));
3408 if (should_turn_left || should_turn_right || should_move_on)
3410 if (should_turn_left && should_turn_right && should_move_on)
3411 MovDir[x][y] = (rnd < rnd_value / 3 ? left_dir :
3412 rnd < 2 * rnd_value / 3 ? right_dir :
3414 else if (should_turn_left && should_turn_right)
3415 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
3416 else if (should_turn_left && should_move_on)
3417 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
3418 else if (should_turn_right && should_move_on)
3419 MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
3420 else if (should_turn_left)
3421 MovDir[x][y] = left_dir;
3422 else if (should_turn_right)
3423 MovDir[x][y] = right_dir;
3424 else if (should_move_on)
3425 MovDir[x][y] = old_move_dir;
3427 else if (can_move_on && rnd > rnd_value / 8)
3428 MovDir[x][y] = old_move_dir;
3429 else if (can_turn_left && can_turn_right)
3430 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
3431 else if (can_turn_left && rnd > rnd_value / 8)
3432 MovDir[x][y] = left_dir;
3433 else if (can_turn_right && rnd > rnd_value/8)
3434 MovDir[x][y] = right_dir;
3436 MovDir[x][y] = back_dir;
3438 xx = x + move_xy[MovDir[x][y]].x;
3439 yy = y + move_xy[MovDir[x][y]].y;
3441 if (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy]))
3442 MovDir[x][y] = old_move_dir;
3446 else if (element == EL_DRAGON)
3448 boolean can_turn_left = IN_LEV_FIELD_AND_IS_FREE(left_x, left_y);
3449 boolean can_turn_right = IN_LEV_FIELD_AND_IS_FREE(right_x, right_y);
3450 boolean can_move_on = IN_LEV_FIELD_AND_IS_FREE(move_x, move_y);
3452 int rnd = RND(rnd_value);
3455 if (FrameCounter < 1 && x == 0 && y == 29)
3456 printf(":2: %d/%d: %d [%d]\n", x, y, MovDir[x][y], FrameCounter);
3459 if (can_move_on && rnd > rnd_value / 8)
3460 MovDir[x][y] = old_move_dir;
3461 else if (can_turn_left && can_turn_right)
3462 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
3463 else if (can_turn_left && rnd > rnd_value / 8)
3464 MovDir[x][y] = left_dir;
3465 else if (can_turn_right && rnd > rnd_value / 8)
3466 MovDir[x][y] = right_dir;
3468 MovDir[x][y] = back_dir;
3470 xx = x + move_xy[MovDir[x][y]].x;
3471 yy = y + move_xy[MovDir[x][y]].y;
3474 if (FrameCounter < 1 && x == 0 && y == 29)
3475 printf(":3: %d/%d: %d (%d/%d: %d) [%d]\n", x, y, MovDir[x][y],
3476 xx, yy, Feld[xx][yy],
3481 if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
3482 MovDir[x][y] = old_move_dir;
3484 if (!IS_FREE(xx, yy))
3485 MovDir[x][y] = old_move_dir;
3489 if (FrameCounter < 1 && x == 0 && y == 29)
3490 printf(":4: %d/%d: %d [%d]\n", x, y, MovDir[x][y], FrameCounter);
3495 else if (element == EL_MOLE)
3497 boolean can_move_on =
3498 (MOLE_CAN_ENTER_FIELD(move_x, move_y,
3499 IS_AMOEBOID(Feld[move_x][move_y]) ||
3500 Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
3503 boolean can_turn_left =
3504 (MOLE_CAN_ENTER_FIELD(left_x, left_y,
3505 IS_AMOEBOID(Feld[left_x][left_y])));
3507 boolean can_turn_right =
3508 (MOLE_CAN_ENTER_FIELD(right_x, right_y,
3509 IS_AMOEBOID(Feld[right_x][right_y])));
3511 if (can_turn_left && can_turn_right)
3512 MovDir[x][y] = (RND(2) ? left_dir : right_dir);
3513 else if (can_turn_left)
3514 MovDir[x][y] = left_dir;
3516 MovDir[x][y] = right_dir;
3519 if (MovDir[x][y] != old_move_dir)
3522 else if (element == EL_BALLOON)
3524 MovDir[x][y] = game.balloon_dir;
3527 else if (element == EL_SPRING)
3529 if (MovDir[x][y] & MV_HORIZONTAL &&
3530 (!IN_LEV_FIELD_AND_IS_FREE(move_x, move_y) ||
3531 IN_LEV_FIELD_AND_IS_FREE(x, y + 1)))
3532 MovDir[x][y] = MV_NO_MOVING;
3536 else if (element == EL_ROBOT ||
3537 element == EL_SATELLITE ||
3538 element == EL_PENGUIN)
3540 int attr_x = -1, attr_y = -1;
3551 for (i = 0; i < MAX_PLAYERS; i++)
3553 struct PlayerInfo *player = &stored_player[i];
3554 int jx = player->jx, jy = player->jy;
3556 if (!player->active)
3560 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
3568 if (element == EL_ROBOT && ZX >= 0 && ZY >= 0)
3574 if (element == EL_PENGUIN)
3577 static int xy[4][2] =
3585 for (i = 0; i < 4; i++)
3587 int ex = x + xy[i % 4][0];
3588 int ey = y + xy[i % 4][1];
3590 if (IN_LEV_FIELD(ex, ey) && Feld[ex][ey] == EL_EXIT_OPEN)
3599 MovDir[x][y] = MV_NO_MOVING;
3601 MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
3602 else if (attr_x > x)
3603 MovDir[x][y] |= (AllPlayersGone ? MV_LEFT : MV_RIGHT);
3605 MovDir[x][y] |= (AllPlayersGone ? MV_DOWN : MV_UP);
3606 else if (attr_y > y)
3607 MovDir[x][y] |= (AllPlayersGone ? MV_UP : MV_DOWN);
3609 if (element == EL_ROBOT)
3613 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
3614 MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
3615 Moving2Blocked(x, y, &newx, &newy);
3617 if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
3618 MovDelay[x][y] = 8 + 8 * !RND(3);
3620 MovDelay[x][y] = 16;
3622 else if (element == EL_PENGUIN)
3628 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
3630 boolean first_horiz = RND(2);
3631 int new_move_dir = MovDir[x][y];
3634 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
3635 Moving2Blocked(x, y, &newx, &newy);
3637 if (PENGUIN_CAN_ENTER_FIELD(newx, newy))
3641 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
3642 Moving2Blocked(x, y, &newx, &newy);
3644 if (PENGUIN_CAN_ENTER_FIELD(newx, newy))
3647 MovDir[x][y] = old_move_dir;
3651 else /* (element == EL_SATELLITE) */
3657 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
3659 boolean first_horiz = RND(2);
3660 int new_move_dir = MovDir[x][y];
3663 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
3664 Moving2Blocked(x, y, &newx, &newy);
3666 if (ELEMENT_CAN_ENTER_FIELD_OR_ACID_2(newx, newy))
3670 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
3671 Moving2Blocked(x, y, &newx, &newy);
3673 if (ELEMENT_CAN_ENTER_FIELD_OR_ACID_2(newx, newy))
3676 MovDir[x][y] = old_move_dir;
3681 else if (move_pattern == MV_ALL_DIRECTIONS ||
3682 move_pattern == MV_TURNING_LEFT ||
3683 move_pattern == MV_TURNING_RIGHT)
3685 boolean can_turn_left = ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
3686 boolean can_turn_right = ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
3688 if (move_pattern == MV_TURNING_LEFT)
3689 MovDir[x][y] = left_dir;
3690 else if (move_pattern == MV_TURNING_RIGHT)
3691 MovDir[x][y] = right_dir;
3692 else if (can_turn_left && can_turn_right)
3693 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
3694 else if (can_turn_left)
3695 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
3696 else if (can_turn_right)
3697 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
3699 MovDir[x][y] = back_dir;
3701 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
3703 else if (move_pattern == MV_HORIZONTAL ||
3704 move_pattern == MV_VERTICAL)
3706 if (move_pattern & old_move_dir)
3707 MovDir[x][y] = back_dir;
3708 else if (move_pattern == MV_HORIZONTAL)
3709 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
3710 else if (move_pattern == MV_VERTICAL)
3711 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
3713 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
3715 else if (move_pattern & MV_ANY_DIRECTION)
3717 MovDir[x][y] = move_pattern;
3718 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
3720 else if (move_pattern == MV_ALONG_LEFT_SIDE)
3722 if (ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
3723 MovDir[x][y] = left_dir;
3724 else if (!ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
3725 MovDir[x][y] = right_dir;
3727 if (MovDir[x][y] != old_move_dir)
3728 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
3730 else if (move_pattern == MV_ALONG_RIGHT_SIDE)
3732 if (ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
3733 MovDir[x][y] = right_dir;
3734 else if (!ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
3735 MovDir[x][y] = left_dir;
3737 if (MovDir[x][y] != old_move_dir)
3738 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
3740 else if (move_pattern == MV_TOWARDS_PLAYER ||
3741 move_pattern == MV_AWAY_FROM_PLAYER)
3743 int attr_x = -1, attr_y = -1;
3745 boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
3756 for (i = 0; i < MAX_PLAYERS; i++)
3758 struct PlayerInfo *player = &stored_player[i];
3759 int jx = player->jx, jy = player->jy;
3761 if (!player->active)
3765 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
3773 MovDir[x][y] = MV_NO_MOVING;
3775 MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
3776 else if (attr_x > x)
3777 MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
3779 MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
3780 else if (attr_y > y)
3781 MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
3783 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
3785 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
3787 boolean first_horiz = RND(2);
3788 int new_move_dir = MovDir[x][y];
3791 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
3792 Moving2Blocked(x, y, &newx, &newy);
3794 if (ELEMENT_CAN_ENTER_FIELD_OR_ACID(element, newx, newy))
3798 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
3799 Moving2Blocked(x, y, &newx, &newy);
3801 if (ELEMENT_CAN_ENTER_FIELD_OR_ACID(element, newx, newy))
3804 MovDir[x][y] = old_move_dir;
3807 else if (move_pattern == MV_WHEN_PUSHED)
3809 if (!IN_LEV_FIELD_AND_IS_FREE(move_x, move_y))
3810 MovDir[x][y] = MV_NO_MOVING;
3814 else if (move_pattern & MV_MAZE_RUNNER_STYLE ||
3815 element == EL_MAZE_RUNNER)
3817 static int test_xy[7][2] =
3827 static int test_dir[7] =
3837 boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
3838 int move_preference = -1000000; /* start with very low preference */
3839 int new_move_dir = MV_NO_MOVING;
3840 int start_test = RND(4);
3843 for (i = 0; i < 4; i++)
3845 int move_dir = test_dir[start_test + i];
3846 int move_dir_preference;
3848 xx = x + test_xy[start_test + i][0];
3849 yy = y + test_xy[start_test + i][1];
3851 if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
3852 (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
3854 new_move_dir = move_dir;
3859 if (!MAZE_RUNNER_CAN_ENTER_FIELD(xx, yy))
3862 move_dir_preference = -1 * RunnerVisit[xx][yy];
3863 if (hunter_mode && PlayerVisit[xx][yy] > 0)
3864 move_dir_preference = PlayerVisit[xx][yy];
3866 if (move_dir_preference > move_preference)
3868 /* prefer field that has not been visited for the longest time */
3869 move_preference = move_dir_preference;
3870 new_move_dir = move_dir;
3872 else if (move_dir_preference == move_preference &&
3873 move_dir == old_move_dir)
3875 /* prefer last direction when all directions are preferred equally */
3876 move_preference = move_dir_preference;
3877 new_move_dir = move_dir;
3881 MovDir[x][y] = new_move_dir;
3882 if (old_move_dir != new_move_dir)
3887 static void TurnRound(int x, int y)
3889 int direction = MovDir[x][y];
3892 GfxDir[x][y] = MovDir[x][y];
3898 GfxDir[x][y] = MovDir[x][y];
3901 if (direction != MovDir[x][y])
3906 GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_BIT(direction);
3909 GfxAction[x][y] = ACTION_WAITING;
3913 static boolean JustBeingPushed(int x, int y)
3917 for (i = 0; i < MAX_PLAYERS; i++)
3919 struct PlayerInfo *player = &stored_player[i];
3921 if (player->active && player->is_pushing && player->MovPos)
3923 int next_jx = player->jx + (player->jx - player->last_jx);
3924 int next_jy = player->jy + (player->jy - player->last_jy);
3926 if (x == next_jx && y == next_jy)
3934 void StartMoving(int x, int y)
3936 boolean use_spring_bug = (game.engine_version < VERSION_IDENT(2,2,0,0));
3937 boolean started_moving = FALSE; /* some elements can fall _and_ move */
3938 int element = Feld[x][y];
3944 if (MovDelay[x][y] == 0)
3945 GfxAction[x][y] = ACTION_DEFAULT;
3947 /* !!! this should be handled more generic (not only for mole) !!! */
3948 if (element != EL_MOLE && GfxAction[x][y] != ACTION_DIGGING)
3949 GfxAction[x][y] = ACTION_DEFAULT;
3952 if (CAN_FALL(element) && y < lev_fieldy - 1)
3954 if ((x > 0 && IS_PLAYER(x - 1, y)) ||
3955 (x < lev_fieldx-1 && IS_PLAYER(x + 1, y)))
3956 if (JustBeingPushed(x, y))
3959 if (element == EL_QUICKSAND_FULL)
3961 if (IS_FREE(x, y + 1))
3963 InitMovingField(x, y, MV_DOWN);
3964 started_moving = TRUE;
3966 Feld[x][y] = EL_QUICKSAND_EMPTYING;
3967 Store[x][y] = EL_ROCK;
3969 PlayLevelSoundAction(x, y, ACTION_EMPTYING);
3971 PlayLevelSound(x, y, SND_QUICKSAND_EMPTYING);
3974 else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
3976 if (!MovDelay[x][y])
3977 MovDelay[x][y] = TILEY + 1;
3986 Feld[x][y] = EL_QUICKSAND_EMPTY;
3987 Feld[x][y + 1] = EL_QUICKSAND_FULL;
3988 Store[x][y + 1] = Store[x][y];
3991 PlayLevelSoundAction(x, y, ACTION_FILLING);
3993 PlayLevelSound(x, y, SND_QUICKSAND_FILLING);
3997 else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
3998 Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
4000 InitMovingField(x, y, MV_DOWN);
4001 started_moving = TRUE;
4003 Feld[x][y] = EL_QUICKSAND_FILLING;
4004 Store[x][y] = element;
4006 PlayLevelSoundAction(x, y, ACTION_FILLING);
4008 PlayLevelSound(x, y, SND_QUICKSAND_FILLING);
4011 else if (element == EL_MAGIC_WALL_FULL)
4013 if (IS_FREE(x, y + 1))
4015 InitMovingField(x, y, MV_DOWN);
4016 started_moving = TRUE;
4018 Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
4019 Store[x][y] = EL_CHANGED(Store[x][y]);
4021 else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
4023 if (!MovDelay[x][y])
4024 MovDelay[x][y] = TILEY/4 + 1;
4033 Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
4034 Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
4035 Store[x][y + 1] = EL_CHANGED(Store[x][y]);
4039 else if (element == EL_BD_MAGIC_WALL_FULL)
4041 if (IS_FREE(x, y + 1))
4043 InitMovingField(x, y, MV_DOWN);
4044 started_moving = TRUE;
4046 Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
4047 Store[x][y] = EL_CHANGED2(Store[x][y]);
4049 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
4051 if (!MovDelay[x][y])
4052 MovDelay[x][y] = TILEY/4 + 1;
4061 Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
4062 Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
4063 Store[x][y + 1] = EL_CHANGED2(Store[x][y]);
4067 else if (CAN_PASS_MAGIC_WALL(element) &&
4068 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
4069 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE))
4071 InitMovingField(x, y, MV_DOWN);
4072 started_moving = TRUE;
4075 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
4076 EL_BD_MAGIC_WALL_FILLING);
4077 Store[x][y] = element;
4080 else if (CAN_SMASH(element) && Feld[x][y + 1] == EL_ACID)
4082 else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
4087 InitMovingField(x, y, MV_DOWN);
4088 started_moving = TRUE;
4090 Store[x][y] = EL_ACID;
4092 /* !!! TEST !!! better use "_FALLING" etc. !!! */
4093 GfxAction[x][y + 1] = ACTION_ACTIVE;
4097 else if ((game.engine_version < VERSION_IDENT(2,2,0,7) &&
4098 CAN_SMASH(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
4099 (Feld[x][y + 1] == EL_BLOCKED)) ||
4100 (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
4101 CAN_SMASH(element) && WasJustFalling[x][y] &&
4102 (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))))
4106 else if (game.engine_version < VERSION_IDENT(2,2,0,7) &&
4107 CAN_SMASH(element) && Feld[x][y + 1] == EL_BLOCKED &&
4108 WasJustMoving[x][y] && !Pushed[x][y + 1])
4110 else if (CAN_SMASH(element) && Feld[x][y + 1] == EL_BLOCKED &&
4111 WasJustMoving[x][y])
4116 /* this is needed for a special case not covered by calling "Impact()"
4117 from "ContinueMoving()": if an element moves to a tile directly below
4118 another element which was just falling on that tile (which was empty
4119 in the previous frame), the falling element above would just stop
4120 instead of smashing the element below (in previous version, the above
4121 element was just checked for "moving" instead of "falling", resulting
4122 in incorrect smashes caused by horizontal movement of the above
4123 element; also, the case of the player being the element to smash was
4124 simply not covered here... :-/ ) */
4128 else if (IS_FREE(x, y + 1) && element == EL_SPRING && use_spring_bug)
4130 if (MovDir[x][y] == MV_NO_MOVING)
4132 InitMovingField(x, y, MV_DOWN);
4133 started_moving = TRUE;
4136 else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
4138 if (WasJustFalling[x][y]) /* prevent animation from being restarted */
4139 MovDir[x][y] = MV_DOWN;
4141 InitMovingField(x, y, MV_DOWN);
4142 started_moving = TRUE;
4144 else if (element == EL_AMOEBA_DROP)
4146 Feld[x][y] = EL_AMOEBA_GROWING;
4147 Store[x][y] = EL_AMOEBA_WET;
4149 /* Store[x][y + 1] must be zero, because:
4150 (EL_QUICKSAND_FULL -> EL_ROCK): Store[x][y + 1] == EL_QUICKSAND_EMPTY
4153 #if OLD_GAME_BEHAVIOUR
4154 else if (IS_SLIPPERY(Feld[x][y + 1]) && !Store[x][y + 1])
4156 else if (IS_SLIPPERY(Feld[x][y + 1]) && !Store[x][y + 1] &&
4157 !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
4158 element != EL_DX_SUPABOMB)
4161 else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
4162 (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
4163 !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
4164 element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
4167 boolean can_fall_left = (x > 0 && IS_FREE(x - 1, y) &&
4168 (IS_FREE(x - 1, y + 1) ||
4169 Feld[x - 1][y + 1] == EL_ACID));
4170 boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
4171 (IS_FREE(x + 1, y + 1) ||
4172 Feld[x + 1][y + 1] == EL_ACID));
4173 boolean can_fall_any = (can_fall_left || can_fall_right);
4174 boolean can_fall_both = (can_fall_left && can_fall_right);
4176 if (can_fall_any && IS_CUSTOM_ELEMENT(Feld[x][y + 1]))
4178 int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
4180 if (slippery_type == SLIPPERY_ONLY_LEFT)
4181 can_fall_right = FALSE;
4182 else if (slippery_type == SLIPPERY_ONLY_RIGHT)
4183 can_fall_left = FALSE;
4184 else if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
4185 can_fall_right = FALSE;
4186 else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
4187 can_fall_left = FALSE;
4189 can_fall_any = (can_fall_left || can_fall_right);
4190 can_fall_both = (can_fall_left && can_fall_right);
4195 if (can_fall_both &&
4196 (game.emulation != EMU_BOULDERDASH &&
4197 element != EL_BD_ROCK && element != EL_BD_DIAMOND))
4198 can_fall_left = !(can_fall_right = RND(2));
4200 InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
4201 started_moving = TRUE;
4204 else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
4206 boolean left_is_free = (x > 0 && IS_FREE(x - 1, y));
4207 boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
4208 int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
4209 int belt_dir = game.belt_dir[belt_nr];
4211 if ((belt_dir == MV_LEFT && left_is_free) ||
4212 (belt_dir == MV_RIGHT && right_is_free))
4214 InitMovingField(x, y, belt_dir);
4215 started_moving = TRUE;
4217 GfxAction[x][y] = ACTION_DEFAULT;
4222 /* not "else if" because of elements that can fall and move (EL_SPRING) */
4223 if (CAN_MOVE(element) && !started_moving)
4225 int move_pattern = element_info[element].move_pattern;
4229 if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
4232 if ((element == EL_SATELLITE ||
4233 element == EL_BALLOON ||
4234 element == EL_SPRING)
4235 && JustBeingPushed(x, y))
4241 if (element == EL_SPRING && MovDir[x][y] == MV_DOWN)
4242 Feld[x][y + 1] = EL_EMPTY; /* was set to EL_BLOCKED above */
4244 if (element == EL_SPRING && MovDir[x][y] != MV_NO_MOVING)
4246 Moving2Blocked(x, y, &newx, &newy);
4247 if (Feld[newx][newy] == EL_BLOCKED)
4248 Feld[newx][newy] = EL_EMPTY; /* was set to EL_BLOCKED above */
4254 if (FrameCounter < 1 && x == 0 && y == 29)
4255 printf(":1: %d/%d: %d [%d]\n", x, y, MovDir[x][y], FrameCounter);
4258 if (!MovDelay[x][y]) /* start new movement phase */
4260 /* all objects that can change their move direction after each step
4261 (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall */
4263 if (element != EL_YAMYAM &&
4264 element != EL_DARK_YAMYAM &&
4265 element != EL_PACMAN &&
4266 !(move_pattern & MV_ANY_DIRECTION) &&
4267 move_pattern != MV_TURNING_LEFT &&
4268 move_pattern != MV_TURNING_RIGHT)
4273 if (FrameCounter < 1 && x == 0 && y == 29)
4274 printf(":9: %d: %d [%d]\n", y, MovDir[x][y], FrameCounter);
4277 if (MovDelay[x][y] && (element == EL_BUG ||
4278 element == EL_SPACESHIP ||
4279 element == EL_SP_SNIKSNAK ||
4280 element == EL_SP_ELECTRON ||
4281 element == EL_MOLE))
4282 DrawLevelField(x, y);
4286 if (MovDelay[x][y]) /* wait some time before next movement */
4291 if (element == EL_YAMYAM)
4294 el_act_dir2img(EL_YAMYAM, ACTION_WAITING, MV_LEFT));
4295 DrawLevelElementAnimation(x, y, element);
4299 if (MovDelay[x][y]) /* element still has to wait some time */
4302 /* !!! PLACE THIS SOMEWHERE AFTER "TurnRound()" !!! */
4303 ResetGfxAnimation(x, y);
4307 if (GfxAction[x][y] != ACTION_WAITING)
4308 printf("::: %d: %d != ACTION_WAITING\n", element, GfxAction[x][y]);
4310 GfxAction[x][y] = ACTION_WAITING;
4314 if (element == EL_ROBOT ||
4316 element == EL_PACMAN ||
4318 element == EL_YAMYAM ||
4319 element == EL_DARK_YAMYAM)
4322 DrawLevelElementAnimation(x, y, element);
4324 DrawLevelElementAnimationIfNeeded(x, y, element);
4326 PlayLevelSoundAction(x, y, ACTION_WAITING);
4328 else if (element == EL_SP_ELECTRON)
4329 DrawLevelElementAnimationIfNeeded(x, y, element);
4330 else if (element == EL_DRAGON)
4333 int dir = MovDir[x][y];
4334 int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
4335 int dy = (dir == MV_UP ? -1 : dir == MV_DOWN ? +1 : 0);
4336 int graphic = (dir == MV_LEFT ? IMG_FLAMES_1_LEFT :
4337 dir == MV_RIGHT ? IMG_FLAMES_1_RIGHT :
4338 dir == MV_UP ? IMG_FLAMES_1_UP :
4339 dir == MV_DOWN ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
4340 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
4343 printf("::: %d, %d\n", GfxAction[x][y], GfxFrame[x][y]);
4346 GfxAction[x][y] = ACTION_ATTACKING;
4348 if (IS_PLAYER(x, y))
4349 DrawPlayerField(x, y);
4351 DrawLevelField(x, y);
4353 PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
4355 for (i = 1; i <= 3; i++)
4357 int xx = x + i * dx;
4358 int yy = y + i * dy;
4359 int sx = SCREENX(xx);
4360 int sy = SCREENY(yy);
4361 int flame_graphic = graphic + (i - 1);
4363 if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
4368 int flamed = MovingOrBlocked2Element(xx, yy);
4370 if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_FIRE(flamed))
4373 RemoveMovingField(xx, yy);
4375 Feld[xx][yy] = EL_FLAMES;
4376 if (IN_SCR_FIELD(sx, sy))
4378 DrawLevelFieldCrumbledSand(xx, yy);
4379 DrawGraphic(sx, sy, flame_graphic, frame);
4384 if (Feld[xx][yy] == EL_FLAMES)
4385 Feld[xx][yy] = EL_EMPTY;
4386 DrawLevelField(xx, yy);
4391 if (MovDelay[x][y]) /* element still has to wait some time */
4393 PlayLevelSoundAction(x, y, ACTION_WAITING);
4399 /* special case of "moving" animation of waiting elements (FIX THIS !!!);
4400 for all other elements GfxAction will be set by InitMovingField() */
4401 if (element == EL_BD_BUTTERFLY || element == EL_BD_FIREFLY)
4402 GfxAction[x][y] = ACTION_MOVING;
4406 /* now make next step */
4408 Moving2Blocked(x, y, &newx, &newy); /* get next screen position */
4410 if (DONT_COLLIDE_WITH(element) &&
4411 IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
4412 !PLAYER_PROTECTED(newx, newy))
4415 TestIfBadThingRunsIntoHero(x, y, MovDir[x][y]);
4418 /* player killed by element which is deadly when colliding with */
4420 KillHero(PLAYERINFO(newx, newy));
4425 else if ((element == EL_PENGUIN ||
4426 element == EL_ROBOT ||
4427 element == EL_SATELLITE ||
4428 element == EL_BALLOON ||
4429 IS_CUSTOM_ELEMENT(element)) &&
4430 IN_LEV_FIELD(newx, newy) &&
4431 MovDir[x][y] == MV_DOWN && Feld[newx][newy] == EL_ACID)
4434 Store[x][y] = EL_ACID;
4436 else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
4438 if (Feld[newx][newy] == EL_EXIT_OPEN)
4440 Feld[x][y] = EL_EMPTY;
4441 DrawLevelField(x, y);
4443 PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
4444 if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
4445 DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
4447 local_player->friends_still_needed--;
4448 if (!local_player->friends_still_needed &&
4449 !local_player->GameOver && AllPlayersGone)
4450 local_player->LevelSolved = local_player->GameOver = TRUE;
4454 else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
4456 if (DigField(local_player, newx, newy, 0, 0, DF_DIG) == MF_MOVING)
4457 DrawLevelField(newx, newy);
4459 GfxDir[x][y] = MovDir[x][y] = MV_NO_MOVING;
4461 else if (!IS_FREE(newx, newy))
4463 GfxAction[x][y] = ACTION_WAITING;
4465 if (IS_PLAYER(x, y))
4466 DrawPlayerField(x, y);
4468 DrawLevelField(x, y);
4472 else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
4474 if (IS_FOOD_PIG(Feld[newx][newy]))
4476 if (IS_MOVING(newx, newy))
4477 RemoveMovingField(newx, newy);
4480 Feld[newx][newy] = EL_EMPTY;
4481 DrawLevelField(newx, newy);
4484 PlayLevelSound(x, y, SND_PIG_DIGGING);
4486 else if (!IS_FREE(newx, newy))
4488 if (IS_PLAYER(x, y))
4489 DrawPlayerField(x, y);
4491 DrawLevelField(x, y);
4495 else if ((move_pattern & MV_MAZE_RUNNER_STYLE ||
4496 element == EL_MAZE_RUNNER) && IN_LEV_FIELD(newx, newy))
4498 if (IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
4500 if (IS_MOVING(newx, newy))
4501 RemoveMovingField(newx, newy);
4504 Feld[newx][newy] = EL_EMPTY;
4505 DrawLevelField(newx, newy);
4508 PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
4510 else if (!IS_FREE(newx, newy))
4513 if (IS_PLAYER(x, y))
4514 DrawPlayerField(x, y);
4516 DrawLevelField(x, y);
4521 RunnerVisit[x][y] = FrameCounter;
4522 PlayerVisit[x][y] /= 8; /* expire player visit path */
4524 else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
4526 if (!IS_FREE(newx, newy))
4528 if (IS_PLAYER(x, y))
4529 DrawPlayerField(x, y);
4531 DrawLevelField(x, y);
4537 boolean wanna_flame = !RND(10);
4538 int dx = newx - x, dy = newy - y;
4539 int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
4540 int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
4541 int element1 = (IN_LEV_FIELD(newx1, newy1) ?
4542 MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
4543 int element2 = (IN_LEV_FIELD(newx2, newy2) ?
4544 MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
4547 IS_CLASSIC_ENEMY(element1) ||
4548 IS_CLASSIC_ENEMY(element2)) &&
4549 element1 != EL_DRAGON && element2 != EL_DRAGON &&
4550 element1 != EL_FLAMES && element2 != EL_FLAMES)
4553 ResetGfxAnimation(x, y);
4554 GfxAction[x][y] = ACTION_ATTACKING;
4557 if (IS_PLAYER(x, y))
4558 DrawPlayerField(x, y);
4560 DrawLevelField(x, y);
4562 PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
4564 MovDelay[x][y] = 50;
4566 Feld[newx][newy] = EL_FLAMES;
4567 if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
4568 Feld[newx1][newy1] = EL_FLAMES;
4569 if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
4570 Feld[newx2][newy2] = EL_FLAMES;
4576 else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
4577 Feld[newx][newy] == EL_DIAMOND)
4579 if (IS_MOVING(newx, newy))
4580 RemoveMovingField(newx, newy);
4583 Feld[newx][newy] = EL_EMPTY;
4584 DrawLevelField(newx, newy);
4587 PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
4589 else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
4590 IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
4592 if (AmoebaNr[newx][newy])
4594 AmoebaCnt2[AmoebaNr[newx][newy]]--;
4595 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
4596 Feld[newx][newy] == EL_BD_AMOEBA)
4597 AmoebaCnt[AmoebaNr[newx][newy]]--;
4600 if (IS_MOVING(newx, newy))
4601 RemoveMovingField(newx, newy);
4604 Feld[newx][newy] = EL_EMPTY;
4605 DrawLevelField(newx, newy);
4608 PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
4610 else if ((element == EL_PACMAN || element == EL_MOLE)
4611 && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
4613 if (AmoebaNr[newx][newy])
4615 AmoebaCnt2[AmoebaNr[newx][newy]]--;
4616 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
4617 Feld[newx][newy] == EL_BD_AMOEBA)
4618 AmoebaCnt[AmoebaNr[newx][newy]]--;
4621 if (element == EL_MOLE)
4623 Feld[newx][newy] = EL_AMOEBA_SHRINKING;
4624 PlayLevelSound(x, y, SND_MOLE_DIGGING);
4626 ResetGfxAnimation(x, y);
4627 GfxAction[x][y] = ACTION_DIGGING;
4628 DrawLevelField(x, y);
4630 MovDelay[newx][newy] = 0; /* start amoeba shrinking delay */
4631 return; /* wait for shrinking amoeba */
4633 else /* element == EL_PACMAN */
4635 Feld[newx][newy] = EL_EMPTY;
4636 DrawLevelField(newx, newy);
4637 PlayLevelSound(x, y, SND_PACMAN_DIGGING);
4640 else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
4641 (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
4642 (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
4644 /* wait for shrinking amoeba to completely disappear */
4647 else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
4649 /* object was running against a wall */
4654 if (GFX_ELEMENT(element) != EL_SAND) /* !!! FIX THIS (crumble) !!! */
4655 DrawLevelElementAnimation(x, y, element);
4657 if (element == EL_BUG ||
4658 element == EL_SPACESHIP ||
4659 element == EL_SP_SNIKSNAK)
4660 DrawLevelField(x, y);
4661 else if (element == EL_MOLE)
4662 DrawLevelField(x, y);
4663 else if (element == EL_BD_BUTTERFLY ||
4664 element == EL_BD_FIREFLY)
4665 DrawLevelElementAnimationIfNeeded(x, y, element);
4666 else if (element == EL_SATELLITE)
4667 DrawLevelElementAnimationIfNeeded(x, y, element);
4668 else if (element == EL_SP_ELECTRON)
4669 DrawLevelElementAnimationIfNeeded(x, y, element);
4672 if (DONT_TOUCH(element))
4673 TestIfBadThingTouchesHero(x, y);
4676 PlayLevelSoundAction(x, y, ACTION_WAITING);
4682 InitMovingField(x, y, MovDir[x][y]);
4684 PlayLevelSoundAction(x, y, ACTION_MOVING);
4688 ContinueMoving(x, y);
4691 void ContinueMoving(int x, int y)
4693 int element = Feld[x][y];
4694 int direction = MovDir[x][y];
4695 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
4696 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
4697 int newx = x + dx, newy = y + dy;
4698 int nextx = newx + dx, nexty = newy + dy;
4699 boolean pushed = Pushed[x][y];
4701 MovPos[x][y] += getElementMoveStepsize(x, y);
4703 if (pushed) /* special case: moving object pushed by player */
4704 MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
4706 if (ABS(MovPos[x][y]) < TILEX)
4708 DrawLevelField(x, y);
4710 return; /* element is still moving */
4713 /* element reached destination field */
4715 Feld[x][y] = EL_EMPTY;
4716 Feld[newx][newy] = element;
4717 MovPos[x][y] = 0; /* force "not moving" for "crumbled sand" */
4719 if (element == EL_MOLE)
4721 Feld[x][y] = EL_SAND;
4723 DrawLevelFieldCrumbledSandNeighbours(x, y);
4725 else if (element == EL_QUICKSAND_FILLING)
4727 element = Feld[newx][newy] = get_next_element(element);
4728 Store[newx][newy] = Store[x][y];
4730 else if (element == EL_QUICKSAND_EMPTYING)
4732 Feld[x][y] = get_next_element(element);
4733 element = Feld[newx][newy] = Store[x][y];
4735 else if (element == EL_MAGIC_WALL_FILLING)
4737 element = Feld[newx][newy] = get_next_element(element);
4738 if (!game.magic_wall_active)
4739 element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
4740 Store[newx][newy] = Store[x][y];
4742 else if (element == EL_MAGIC_WALL_EMPTYING)
4744 Feld[x][y] = get_next_element(element);
4745 if (!game.magic_wall_active)
4746 Feld[x][y] = EL_MAGIC_WALL_DEAD;
4747 element = Feld[newx][newy] = Store[x][y];
4749 else if (element == EL_BD_MAGIC_WALL_FILLING)
4751 element = Feld[newx][newy] = get_next_element(element);
4752 if (!game.magic_wall_active)
4753 element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
4754 Store[newx][newy] = Store[x][y];
4756 else if (element == EL_BD_MAGIC_WALL_EMPTYING)
4758 Feld[x][y] = get_next_element(element);
4759 if (!game.magic_wall_active)
4760 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
4761 element = Feld[newx][newy] = Store[x][y];
4763 else if (element == EL_AMOEBA_DROPPING)
4765 Feld[x][y] = get_next_element(element);
4766 element = Feld[newx][newy] = Store[x][y];
4768 else if (element == EL_SOKOBAN_OBJECT)
4771 Feld[x][y] = Back[x][y];
4773 if (Back[newx][newy])
4774 Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
4776 Back[x][y] = Back[newx][newy] = 0;
4778 else if (Store[x][y] == EL_ACID)
4780 element = Feld[newx][newy] = EL_ACID;
4784 MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
4785 MovDelay[newx][newy] = 0;
4787 /* copy element change control values to new field */
4788 ChangeDelay[newx][newy] = ChangeDelay[x][y];
4789 ChangePage[newx][newy] = ChangePage[x][y];
4790 Changed[newx][newy] = Changed[x][y];
4791 ChangeEvent[newx][newy] = ChangeEvent[x][y];
4793 ChangeDelay[x][y] = 0;
4794 ChangePage[x][y] = -1;
4795 Changed[x][y] = CE_BITMASK_DEFAULT;
4796 ChangeEvent[x][y] = CE_BITMASK_DEFAULT;
4798 /* copy animation control values to new field */
4799 GfxFrame[newx][newy] = GfxFrame[x][y];
4800 GfxRandom[newx][newy] = GfxRandom[x][y]; /* keep same random value */
4801 GfxAction[newx][newy] = GfxAction[x][y]; /* keep action one frame */
4802 GfxDir[newx][newy] = GfxDir[x][y]; /* keep element direction */
4804 Pushed[x][y] = Pushed[newx][newy] = FALSE;
4806 ResetGfxAnimation(x, y); /* reset animation values for old field */
4809 /* 2.1.1 (does not work correctly for spring) */
4810 if (!CAN_MOVE(element))
4811 MovDir[newx][newy] = 0;
4815 /* (does not work for falling objects that slide horizontally) */
4816 if (CAN_FALL(element) && MovDir[newx][newy] == MV_DOWN)
4817 MovDir[newx][newy] = 0;
4820 if (!CAN_MOVE(element) ||
4821 (element == EL_SPRING && MovDir[newx][newy] == MV_DOWN))
4822 MovDir[newx][newy] = 0;
4825 if (!CAN_MOVE(element) ||
4826 (CAN_FALL(element) && direction == MV_DOWN))
4827 GfxDir[x][y] = MovDir[newx][newy] = 0;
4832 DrawLevelField(x, y);
4833 DrawLevelField(newx, newy);
4835 Stop[newx][newy] = TRUE; /* ignore this element until the next frame */
4837 /* prevent pushed element from moving on in pushed direction */
4838 if (pushed && CAN_MOVE(element) &&
4839 element_info[element].move_pattern & MV_ANY_DIRECTION &&
4840 !(element_info[element].move_pattern & direction))
4841 TurnRound(newx, newy);
4843 if (!pushed) /* special case: moving object pushed by player */
4845 WasJustMoving[newx][newy] = 3;
4847 if (CAN_FALL(element) && direction == MV_DOWN)
4848 WasJustFalling[newx][newy] = 3;
4851 if (DONT_TOUCH(element)) /* object may be nasty to player or others */
4853 TestIfBadThingTouchesHero(newx, newy);
4854 TestIfBadThingTouchesFriend(newx, newy);
4856 if (!IS_CUSTOM_ELEMENT(element))
4857 TestIfBadThingTouchesOtherBadThing(newx, newy);
4859 else if (element == EL_PENGUIN)
4860 TestIfFriendTouchesBadThing(newx, newy);
4862 if (CAN_FALL(element) && direction == MV_DOWN &&
4863 (newy == lev_fieldy - 1 || !IS_FREE(x, newy + 1)))
4867 TestIfElementTouchesCustomElement(x, y); /* for empty space */
4871 if (ChangePage[newx][newy] != -1) /* delayed change */
4872 ChangeElement(newx, newy, ChangePage[newx][newy]);
4875 if (!IN_LEV_FIELD(nextx, nexty) || !IS_FREE(nextx, nexty))
4877 /* !!! fix side (direction) orientation here and elsewhere !!! */
4878 CheckElementSideChange(newx, newy, Feld[newx][newy],
4879 direction, CE_COLLISION_ACTIVE, -1);
4881 if (IN_LEV_FIELD(nextx, nexty))
4883 static int opposite_directions[] =
4890 int move_dir_bit = MV_DIR_BIT(direction);
4891 int opposite_direction = opposite_directions[move_dir_bit];
4892 int hitting_side = direction;
4893 int touched_side = opposite_direction;
4894 int hitting_element = Feld[newx][newy];
4895 int touched_element = MovingOrBlocked2Element(nextx, nexty);
4896 boolean object_hit = (!IS_MOVING(nextx, nexty) ||
4897 MovDir[nextx][nexty] != direction ||
4898 ABS(MovPos[nextx][nexty]) <= TILEY / 2);
4904 CheckElementSideChange(nextx, nexty, Feld[nextx][nexty],
4905 opposite_direction, CE_COLLISION_PASSIVE, -1);
4907 if (IS_CUSTOM_ELEMENT(hitting_element) &&
4908 HAS_ANY_CHANGE_EVENT(hitting_element, CE_OTHER_IS_COLL_ACTIVE))
4910 for (i = 0; i < element_info[hitting_element].num_change_pages; i++)
4912 struct ElementChangeInfo *change =
4913 &element_info[hitting_element].change_page[i];
4915 if (change->can_change &&
4916 change->events & CH_EVENT_BIT(CE_OTHER_IS_COLL_ACTIVE) &&
4917 change->sides & touched_side &&
4918 change->trigger_element == touched_element)
4920 CheckElementSideChange(newx, newy, hitting_element,
4921 CH_SIDE_ANY, CE_OTHER_IS_COLL_ACTIVE, i);
4927 if (IS_CUSTOM_ELEMENT(touched_element) &&
4928 HAS_ANY_CHANGE_EVENT(touched_element, CE_OTHER_IS_COLL_PASSIVE))
4930 for (i = 0; i < element_info[touched_element].num_change_pages; i++)
4932 struct ElementChangeInfo *change =
4933 &element_info[touched_element].change_page[i];
4935 if (change->can_change &&
4936 change->events & CH_EVENT_BIT(CE_OTHER_IS_COLL_PASSIVE) &&
4937 change->sides & hitting_side &&
4938 change->trigger_element == hitting_element)
4940 CheckElementSideChange(nextx, nexty, touched_element,
4941 CH_SIDE_ANY, CE_OTHER_IS_COLL_PASSIVE, i);
4950 TestIfPlayerTouchesCustomElement(newx, newy);
4951 TestIfElementTouchesCustomElement(newx, newy);
4954 int AmoebeNachbarNr(int ax, int ay)
4957 int element = Feld[ax][ay];
4959 static int xy[4][2] =
4967 for (i = 0; i < 4; i++)
4969 int x = ax + xy[i][0];
4970 int y = ay + xy[i][1];
4972 if (!IN_LEV_FIELD(x, y))
4975 if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
4976 group_nr = AmoebaNr[x][y];
4982 void AmoebenVereinigen(int ax, int ay)
4984 int i, x, y, xx, yy;
4985 int new_group_nr = AmoebaNr[ax][ay];
4986 static int xy[4][2] =
4994 if (new_group_nr == 0)
4997 for (i = 0; i < 4; i++)
5002 if (!IN_LEV_FIELD(x, y))
5005 if ((Feld[x][y] == EL_AMOEBA_FULL ||
5006 Feld[x][y] == EL_BD_AMOEBA ||
5007 Feld[x][y] == EL_AMOEBA_DEAD) &&
5008 AmoebaNr[x][y] != new_group_nr)
5010 int old_group_nr = AmoebaNr[x][y];
5012 if (old_group_nr == 0)
5015 AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
5016 AmoebaCnt[old_group_nr] = 0;
5017 AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
5018 AmoebaCnt2[old_group_nr] = 0;
5020 for (yy = 0; yy < lev_fieldy; yy++)
5022 for (xx = 0; xx < lev_fieldx; xx++)
5024 if (AmoebaNr[xx][yy] == old_group_nr)
5025 AmoebaNr[xx][yy] = new_group_nr;
5032 void AmoebeUmwandeln(int ax, int ay)
5036 if (Feld[ax][ay] == EL_AMOEBA_DEAD)
5038 int group_nr = AmoebaNr[ax][ay];
5043 printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
5044 printf("AmoebeUmwandeln(): This should never happen!\n");
5049 for (y = 0; y < lev_fieldy; y++)
5051 for (x = 0; x < lev_fieldx; x++)
5053 if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
5056 Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
5060 PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
5061 SND_AMOEBA_TURNING_TO_GEM :
5062 SND_AMOEBA_TURNING_TO_ROCK));
5067 static int xy[4][2] =
5075 for (i = 0; i < 4; i++)
5080 if (!IN_LEV_FIELD(x, y))
5083 if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
5085 PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
5086 SND_AMOEBA_TURNING_TO_GEM :
5087 SND_AMOEBA_TURNING_TO_ROCK));
5094 void AmoebeUmwandelnBD(int ax, int ay, int new_element)
5097 int group_nr = AmoebaNr[ax][ay];
5098 boolean done = FALSE;
5103 printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
5104 printf("AmoebeUmwandelnBD(): This should never happen!\n");
5109 for (y = 0; y < lev_fieldy; y++)
5111 for (x = 0; x < lev_fieldx; x++)
5113 if (AmoebaNr[x][y] == group_nr &&
5114 (Feld[x][y] == EL_AMOEBA_DEAD ||
5115 Feld[x][y] == EL_BD_AMOEBA ||
5116 Feld[x][y] == EL_AMOEBA_GROWING))
5119 Feld[x][y] = new_element;
5120 InitField(x, y, FALSE);
5121 DrawLevelField(x, y);
5128 PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
5129 SND_BD_AMOEBA_TURNING_TO_ROCK :
5130 SND_BD_AMOEBA_TURNING_TO_GEM));
5133 void AmoebeWaechst(int x, int y)
5135 static unsigned long sound_delay = 0;
5136 static unsigned long sound_delay_value = 0;
5138 if (!MovDelay[x][y]) /* start new growing cycle */
5142 if (DelayReached(&sound_delay, sound_delay_value))
5145 PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
5147 if (Store[x][y] == EL_BD_AMOEBA)
5148 PlayLevelSound(x, y, SND_BD_AMOEBA_GROWING);
5150 PlayLevelSound(x, y, SND_AMOEBA_GROWING);
5152 sound_delay_value = 30;
5156 if (MovDelay[x][y]) /* wait some time before growing bigger */
5159 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
5161 int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
5162 6 - MovDelay[x][y]);
5164 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
5167 if (!MovDelay[x][y])
5169 Feld[x][y] = Store[x][y];
5171 DrawLevelField(x, y);
5176 void AmoebaDisappearing(int x, int y)
5178 static unsigned long sound_delay = 0;
5179 static unsigned long sound_delay_value = 0;
5181 if (!MovDelay[x][y]) /* start new shrinking cycle */
5185 if (DelayReached(&sound_delay, sound_delay_value))
5186 sound_delay_value = 30;
5189 if (MovDelay[x][y]) /* wait some time before shrinking */
5192 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
5194 int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
5195 6 - MovDelay[x][y]);
5197 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
5200 if (!MovDelay[x][y])
5202 Feld[x][y] = EL_EMPTY;
5203 DrawLevelField(x, y);
5205 /* don't let mole enter this field in this cycle;
5206 (give priority to objects falling to this field from above) */
5212 void AmoebeAbleger(int ax, int ay)
5215 int element = Feld[ax][ay];
5216 int graphic = el2img(element);
5217 int newax = ax, neway = ay;
5218 static int xy[4][2] =
5226 if (!level.amoeba_speed)
5228 Feld[ax][ay] = EL_AMOEBA_DEAD;
5229 DrawLevelField(ax, ay);
5233 if (IS_ANIMATED(graphic))
5234 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
5236 if (!MovDelay[ax][ay]) /* start making new amoeba field */
5237 MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
5239 if (MovDelay[ax][ay]) /* wait some time before making new amoeba */
5242 if (MovDelay[ax][ay])
5246 if (element == EL_AMOEBA_WET) /* object is an acid / amoeba drop */
5249 int x = ax + xy[start][0];
5250 int y = ay + xy[start][1];
5252 if (!IN_LEV_FIELD(x, y))
5255 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
5256 if (IS_FREE(x, y) ||
5257 Feld[x][y] == EL_SAND || Feld[x][y] == EL_QUICKSAND_EMPTY)
5263 if (newax == ax && neway == ay)
5266 else /* normal or "filled" (BD style) amoeba */
5269 boolean waiting_for_player = FALSE;
5271 for (i = 0; i < 4; i++)
5273 int j = (start + i) % 4;
5274 int x = ax + xy[j][0];
5275 int y = ay + xy[j][1];
5277 if (!IN_LEV_FIELD(x, y))
5280 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
5281 if (IS_FREE(x, y) ||
5282 Feld[x][y] == EL_SAND || Feld[x][y] == EL_QUICKSAND_EMPTY)
5288 else if (IS_PLAYER(x, y))
5289 waiting_for_player = TRUE;
5292 if (newax == ax && neway == ay) /* amoeba cannot grow */
5294 if (i == 4 && (!waiting_for_player || game.emulation == EMU_BOULDERDASH))
5296 Feld[ax][ay] = EL_AMOEBA_DEAD;
5297 DrawLevelField(ax, ay);
5298 AmoebaCnt[AmoebaNr[ax][ay]]--;
5300 if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0) /* amoeba is completely dead */
5302 if (element == EL_AMOEBA_FULL)
5303 AmoebeUmwandeln(ax, ay);
5304 else if (element == EL_BD_AMOEBA)
5305 AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
5310 else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
5312 /* amoeba gets larger by growing in some direction */
5314 int new_group_nr = AmoebaNr[ax][ay];
5317 if (new_group_nr == 0)
5319 printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
5320 printf("AmoebeAbleger(): This should never happen!\n");
5325 AmoebaNr[newax][neway] = new_group_nr;
5326 AmoebaCnt[new_group_nr]++;
5327 AmoebaCnt2[new_group_nr]++;
5329 /* if amoeba touches other amoeba(s) after growing, unify them */
5330 AmoebenVereinigen(newax, neway);
5332 if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
5334 AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
5340 if (element != EL_AMOEBA_WET || neway < ay || !IS_FREE(newax, neway) ||
5341 (neway == lev_fieldy - 1 && newax != ax))
5343 Feld[newax][neway] = EL_AMOEBA_GROWING; /* creation of new amoeba */
5344 Store[newax][neway] = element;
5346 else if (neway == ay)
5348 Feld[newax][neway] = EL_AMOEBA_DROP; /* drop left/right of amoeba */
5350 PlayLevelSoundAction(newax, neway, ACTION_GROWING);
5352 PlayLevelSound(newax, neway, SND_AMOEBA_GROWING);
5357 InitMovingField(ax, ay, MV_DOWN); /* drop dripping from amoeba */
5358 Feld[ax][ay] = EL_AMOEBA_DROPPING;
5359 Store[ax][ay] = EL_AMOEBA_DROP;
5360 ContinueMoving(ax, ay);
5364 DrawLevelField(newax, neway);
5367 void Life(int ax, int ay)
5370 static int life[4] = { 2, 3, 3, 3 }; /* parameters for "game of life" */
5372 int element = Feld[ax][ay];
5373 int graphic = el2img(element);
5374 boolean changed = FALSE;
5376 if (IS_ANIMATED(graphic))
5377 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
5382 if (!MovDelay[ax][ay]) /* start new "game of life" cycle */
5383 MovDelay[ax][ay] = life_time;
5385 if (MovDelay[ax][ay]) /* wait some time before next cycle */
5388 if (MovDelay[ax][ay])
5392 for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
5394 int xx = ax+x1, yy = ay+y1;
5397 if (!IN_LEV_FIELD(xx, yy))
5400 for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
5402 int x = xx+x2, y = yy+y2;
5404 if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
5407 if (((Feld[x][y] == element ||
5408 (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y))) &&
5410 (IS_FREE(x, y) && Stop[x][y]))
5414 if (xx == ax && yy == ay) /* field in the middle */
5416 if (nachbarn < life[0] || nachbarn > life[1])
5418 Feld[xx][yy] = EL_EMPTY;
5420 DrawLevelField(xx, yy);
5421 Stop[xx][yy] = TRUE;
5425 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
5426 else if (IS_FREE(xx, yy) || Feld[xx][yy] == EL_SAND)
5427 { /* free border field */
5428 if (nachbarn >= life[2] && nachbarn <= life[3])
5430 Feld[xx][yy] = element;
5431 MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
5433 DrawLevelField(xx, yy);
5434 Stop[xx][yy] = TRUE;
5441 PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
5442 SND_GAME_OF_LIFE_GROWING);
5445 static void InitRobotWheel(int x, int y)
5447 ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
5450 static void RunRobotWheel(int x, int y)
5452 PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
5455 static void StopRobotWheel(int x, int y)
5457 if (ZX == x && ZY == y)
5461 static void InitTimegateWheel(int x, int y)
5463 ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
5466 static void RunTimegateWheel(int x, int y)
5468 PlayLevelSound(x, y, SND_TIMEGATE_SWITCH_ACTIVE);
5471 void CheckExit(int x, int y)
5473 if (local_player->gems_still_needed > 0 ||
5474 local_player->sokobanfields_still_needed > 0 ||
5475 local_player->lights_still_needed > 0)
5477 int element = Feld[x][y];
5478 int graphic = el2img(element);
5480 if (IS_ANIMATED(graphic))
5481 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
5486 if (AllPlayersGone) /* do not re-open exit door closed after last player */
5489 Feld[x][y] = EL_EXIT_OPENING;
5491 PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
5494 void CheckExitSP(int x, int y)
5496 if (local_player->gems_still_needed > 0)
5498 int element = Feld[x][y];
5499 int graphic = el2img(element);
5501 if (IS_ANIMATED(graphic))
5502 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
5507 if (AllPlayersGone) /* do not re-open exit door closed after last player */
5510 Feld[x][y] = EL_SP_EXIT_OPENING;
5512 PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
5515 static void CloseAllOpenTimegates()
5519 for (y = 0; y < lev_fieldy; y++)
5521 for (x = 0; x < lev_fieldx; x++)
5523 int element = Feld[x][y];
5525 if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
5527 Feld[x][y] = EL_TIMEGATE_CLOSING;
5529 PlayLevelSoundAction(x, y, ACTION_CLOSING);
5531 PlayLevelSound(x, y, SND_TIMEGATE_CLOSING);
5538 void EdelsteinFunkeln(int x, int y)
5540 if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
5543 if (Feld[x][y] == EL_BD_DIAMOND)
5546 if (MovDelay[x][y] == 0) /* next animation frame */
5547 MovDelay[x][y] = 11 * !SimpleRND(500);
5549 if (MovDelay[x][y] != 0) /* wait some time before next frame */
5553 if (setup.direct_draw && MovDelay[x][y])
5554 SetDrawtoField(DRAW_BUFFERED);
5556 DrawLevelElementAnimation(x, y, Feld[x][y]);
5558 if (MovDelay[x][y] != 0)
5560 int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
5561 10 - MovDelay[x][y]);
5563 DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
5565 if (setup.direct_draw)
5569 dest_x = FX + SCREENX(x) * TILEX;
5570 dest_y = FY + SCREENY(y) * TILEY;
5572 BlitBitmap(drawto_field, window,
5573 dest_x, dest_y, TILEX, TILEY, dest_x, dest_y);
5574 SetDrawtoField(DRAW_DIRECT);
5580 void MauerWaechst(int x, int y)
5584 if (!MovDelay[x][y]) /* next animation frame */
5585 MovDelay[x][y] = 3 * delay;
5587 if (MovDelay[x][y]) /* wait some time before next frame */
5591 if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
5593 int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
5594 int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
5596 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5599 if (!MovDelay[x][y])
5601 if (MovDir[x][y] == MV_LEFT)
5603 if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
5604 DrawLevelField(x - 1, y);
5606 else if (MovDir[x][y] == MV_RIGHT)
5608 if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
5609 DrawLevelField(x + 1, y);
5611 else if (MovDir[x][y] == MV_UP)
5613 if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
5614 DrawLevelField(x, y - 1);
5618 if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
5619 DrawLevelField(x, y + 1);
5622 Feld[x][y] = Store[x][y];
5624 GfxDir[x][y] = MovDir[x][y] = MV_NO_MOVING;
5625 DrawLevelField(x, y);
5630 void MauerAbleger(int ax, int ay)
5632 int element = Feld[ax][ay];
5633 int graphic = el2img(element);
5634 boolean oben_frei = FALSE, unten_frei = FALSE;
5635 boolean links_frei = FALSE, rechts_frei = FALSE;
5636 boolean oben_massiv = FALSE, unten_massiv = FALSE;
5637 boolean links_massiv = FALSE, rechts_massiv = FALSE;
5638 boolean new_wall = FALSE;
5640 if (IS_ANIMATED(graphic))
5641 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
5643 if (!MovDelay[ax][ay]) /* start building new wall */
5644 MovDelay[ax][ay] = 6;
5646 if (MovDelay[ax][ay]) /* wait some time before building new wall */
5649 if (MovDelay[ax][ay])
5653 if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
5655 if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
5657 if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
5659 if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
5662 if (element == EL_EXPANDABLE_WALL_VERTICAL ||
5663 element == EL_EXPANDABLE_WALL_ANY)
5667 Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
5668 Store[ax][ay-1] = element;
5669 GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
5670 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
5671 DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
5672 IMG_EXPANDABLE_WALL_GROWING_UP, 0);
5677 Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
5678 Store[ax][ay+1] = element;
5679 GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
5680 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
5681 DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
5682 IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
5687 if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
5688 element == EL_EXPANDABLE_WALL_ANY ||
5689 element == EL_EXPANDABLE_WALL)
5693 Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
5694 Store[ax-1][ay] = element;
5695 GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
5696 if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
5697 DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
5698 IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
5704 Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
5705 Store[ax+1][ay] = element;
5706 GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
5707 if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
5708 DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
5709 IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
5714 if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
5715 DrawLevelField(ax, ay);
5717 if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
5719 if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
5720 unten_massiv = TRUE;
5721 if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
5722 links_massiv = TRUE;
5723 if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
5724 rechts_massiv = TRUE;
5726 if (((oben_massiv && unten_massiv) ||
5727 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
5728 element == EL_EXPANDABLE_WALL) &&
5729 ((links_massiv && rechts_massiv) ||
5730 element == EL_EXPANDABLE_WALL_VERTICAL))
5731 Feld[ax][ay] = EL_WALL;
5735 PlayLevelSoundAction(ax, ay, ACTION_GROWING);
5737 PlayLevelSound(ax, ay, SND_EXPANDABLE_WALL_GROWING);
5741 void CheckForDragon(int x, int y)
5744 boolean dragon_found = FALSE;
5745 static int xy[4][2] =
5753 for (i = 0; i < 4; i++)
5755 for (j = 0; j < 4; j++)
5757 int xx = x + j*xy[i][0], yy = y + j*xy[i][1];
5759 if (IN_LEV_FIELD(xx, yy) &&
5760 (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
5762 if (Feld[xx][yy] == EL_DRAGON)
5763 dragon_found = TRUE;
5772 for (i = 0; i < 4; i++)
5774 for (j = 0; j < 3; j++)
5776 int xx = x + j*xy[i][0], yy = y + j*xy[i][1];
5778 if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
5780 Feld[xx][yy] = EL_EMPTY;
5781 DrawLevelField(xx, yy);
5790 static void InitBuggyBase(int x, int y)
5792 int element = Feld[x][y];
5793 int activating_delay = FRAMES_PER_SECOND / 4;
5796 (element == EL_SP_BUGGY_BASE ?
5797 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
5798 element == EL_SP_BUGGY_BASE_ACTIVATING ?
5800 element == EL_SP_BUGGY_BASE_ACTIVE ?
5801 1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
5804 static void WarnBuggyBase(int x, int y)
5807 static int xy[4][2] =
5815 for (i = 0; i < 4; i++)
5817 int xx = x + xy[i][0], yy = y + xy[i][1];
5819 if (IS_PLAYER(xx, yy))
5821 PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
5828 static void InitTrap(int x, int y)
5830 ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
5833 static void ActivateTrap(int x, int y)
5835 PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
5838 static void ChangeActiveTrap(int x, int y)
5840 int graphic = IMG_TRAP_ACTIVE;
5842 /* if new animation frame was drawn, correct crumbled sand border */
5843 if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
5844 DrawLevelFieldCrumbledSand(x, y);
5847 static void ChangeElementNowExt(int x, int y, int target_element)
5849 /* check if element under player changes from accessible to unaccessible
5850 (needed for special case of dropping element which then changes) */
5851 if (IS_PLAYER(x, y) && !PLAYER_PROTECTED(x, y) &&
5852 IS_ACCESSIBLE(Feld[x][y]) && !IS_ACCESSIBLE(target_element))
5859 Feld[x][y] = target_element;
5861 Changed[x][y] |= ChangeEvent[x][y]; /* ignore same changes in this frame */
5863 ResetGfxAnimation(x, y);
5864 ResetRandomAnimationValue(x, y);
5866 InitField(x, y, FALSE);
5867 if (CAN_MOVE(Feld[x][y]))
5870 DrawLevelField(x, y);
5872 if (GFX_CRUMBLED(Feld[x][y]))
5873 DrawLevelFieldCrumbledSandNeighbours(x, y);
5875 TestIfBadThingTouchesHero(x, y);
5876 TestIfPlayerTouchesCustomElement(x, y);
5877 TestIfElementTouchesCustomElement(x, y);
5879 if (ELEM_IS_PLAYER(target_element))
5880 RelocatePlayer(x, y, target_element);
5883 static boolean ChangeElementNow(int x, int y, int element, int page)
5885 struct ElementChangeInfo *change = &element_info[element].change_page[page];
5887 /* always use default change event to prevent running into a loop */
5888 if (ChangeEvent[x][y] == CE_BITMASK_DEFAULT)
5889 ChangeEvent[x][y] = CH_EVENT_BIT(CE_DELAY);
5891 /* do not change already changed elements with same change event */
5893 if (Changed[x][y] & ChangeEvent[x][y])
5900 Changed[x][y] |= ChangeEvent[x][y]; /* ignore same changes in this frame */
5902 CheckTriggeredElementChange(x, y, Feld[x][y], CE_OTHER_IS_CHANGING);
5904 if (change->explode)
5911 if (change->use_content)
5913 boolean complete_change = TRUE;
5914 boolean can_change[3][3];
5917 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
5919 boolean half_destructible;
5920 int ex = x + xx - 1;
5921 int ey = y + yy - 1;
5924 can_change[xx][yy] = TRUE;
5926 if (ex == x && ey == y) /* do not check changing element itself */
5929 if (change->content[xx][yy] == EL_EMPTY_SPACE)
5931 can_change[xx][yy] = FALSE; /* do not change empty borders */
5936 if (!IN_LEV_FIELD(ex, ey))
5938 can_change[xx][yy] = FALSE;
5939 complete_change = FALSE;
5946 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
5947 e = MovingOrBlocked2Element(ex, ey);
5949 half_destructible = (IS_FREE(ex, ey) || IS_DIGGABLE(e));
5951 if ((change->power <= CP_NON_DESTRUCTIVE && !IS_FREE(ex, ey)) ||
5952 (change->power <= CP_HALF_DESTRUCTIVE && !half_destructible) ||
5953 (change->power <= CP_FULL_DESTRUCTIVE && IS_INDESTRUCTIBLE(e)))
5955 can_change[xx][yy] = FALSE;
5956 complete_change = FALSE;
5960 if (!change->only_complete || complete_change)
5962 boolean something_has_changed = FALSE;
5964 if (change->only_complete && change->use_random_change &&
5965 RND(100) < change->random)
5968 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
5970 int ex = x + xx - 1;
5971 int ey = y + yy - 1;
5973 if (can_change[xx][yy] && (!change->use_random_change ||
5974 RND(100) < change->random))
5976 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
5977 RemoveMovingField(ex, ey);
5979 ChangeEvent[ex][ey] = ChangeEvent[x][y];
5981 ChangeElementNowExt(ex, ey, change->content[xx][yy]);
5983 something_has_changed = TRUE;
5985 /* for symmetry reasons, freeze newly created border elements */
5986 if (ex != x || ey != y)
5987 Stop[ex][ey] = TRUE; /* no more moving in this frame */
5991 if (something_has_changed)
5992 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
5997 ChangeElementNowExt(x, y, change->target_element);
5999 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
6005 static void ChangeElement(int x, int y, int page)
6007 int element = MovingOrBlocked2Element(x, y);
6008 struct ElementInfo *ei = &element_info[element];
6009 struct ElementChangeInfo *change = &ei->change_page[page];
6013 if (!CAN_CHANGE(element))
6016 printf("ChangeElement(): %d,%d: element = %d ('%s')\n",
6017 x, y, element, element_info[element].token_name);
6018 printf("ChangeElement(): This should never happen!\n");
6024 if (ChangeDelay[x][y] == 0) /* initialize element change */
6026 ChangeDelay[x][y] = ( change->delay_fixed * change->delay_frames +
6027 RND(change->delay_random * change->delay_frames)) + 1;
6029 ResetGfxAnimation(x, y);
6030 ResetRandomAnimationValue(x, y);
6032 if (change->pre_change_function)
6033 change->pre_change_function(x, y);
6036 ChangeDelay[x][y]--;
6038 if (ChangeDelay[x][y] != 0) /* continue element change */
6040 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
6042 if (IS_ANIMATED(graphic))
6043 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
6045 if (change->change_function)
6046 change->change_function(x, y);
6048 else /* finish element change */
6050 if (ChangePage[x][y] != -1) /* remember page from delayed change */
6052 page = ChangePage[x][y];
6053 ChangePage[x][y] = -1;
6056 if (IS_MOVING(x, y)) /* never change a running system ;-) */
6058 ChangeDelay[x][y] = 1; /* try change after next move step */
6059 ChangePage[x][y] = page; /* remember page to use for change */
6064 if (ChangeElementNow(x, y, element, page))
6066 if (change->post_change_function)
6067 change->post_change_function(x, y);
6072 static boolean CheckTriggeredElementSideChange(int lx, int ly,
6073 int trigger_element,
6079 if (!(trigger_events[trigger_element] & CH_EVENT_BIT(trigger_event)))
6082 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6084 int element = EL_CUSTOM_START + i;
6086 boolean change_element = FALSE;
6089 if (!CAN_CHANGE(element) || !HAS_ANY_CHANGE_EVENT(element, trigger_event))
6092 for (j = 0; j < element_info[element].num_change_pages; j++)
6094 struct ElementChangeInfo *change = &element_info[element].change_page[j];
6096 if (change->can_change &&
6098 change->events & CH_EVENT_BIT(trigger_event) &&
6100 change->sides & trigger_side &&
6101 change->trigger_element == trigger_element)
6104 if (!(change->events & CH_EVENT_BIT(trigger_event)))
6105 printf("::: !!! %d triggers %d: using wrong page %d [event %d]\n",
6106 trigger_element-EL_CUSTOM_START+1, i+1, j, trigger_event);
6109 change_element = TRUE;
6116 if (!change_element)
6119 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
6122 if (x == lx && y == ly) /* do not change trigger element itself */
6126 if (Feld[x][y] == element)
6128 ChangeDelay[x][y] = 1;
6129 ChangeEvent[x][y] = CH_EVENT_BIT(trigger_event);
6130 ChangeElement(x, y, page);
6138 static boolean CheckTriggeredElementChange(int lx, int ly, int trigger_element,
6141 return CheckTriggeredElementSideChange(lx, ly, trigger_element, CH_SIDE_ANY,
6145 static boolean CheckElementSideChange(int x, int y, int element, int side,
6146 int trigger_event, int page)
6148 if (!CAN_CHANGE(element) || !HAS_ANY_CHANGE_EVENT(element, trigger_event))
6151 if (Feld[x][y] == EL_BLOCKED)
6153 Blocked2Moving(x, y, &x, &y);
6154 element = Feld[x][y];
6158 page = element_info[element].event_page_nr[trigger_event];
6160 if (!(element_info[element].change_page[page].sides & side))
6163 ChangeDelay[x][y] = 1;
6164 ChangeEvent[x][y] = CH_EVENT_BIT(trigger_event);
6165 ChangeElement(x, y, page);
6170 static boolean CheckElementChange(int x, int y, int element, int trigger_event)
6172 return CheckElementSideChange(x, y, element, CH_SIDE_ANY, trigger_event, -1);
6175 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
6177 int jx = player->jx, jy = player->jy;
6178 int element = player->element_nr;
6179 boolean was_waiting = player->is_waiting;
6185 if (!was_waiting) /* not waiting -> waiting */
6187 player->is_waiting = TRUE;
6189 player->frame_counter_bored =
6191 game.player_boring_delay_fixed +
6192 SimpleRND(game.player_boring_delay_random);
6193 player->frame_counter_sleeping =
6195 game.player_sleeping_delay_fixed +
6196 SimpleRND(game.player_sleeping_delay_random);
6198 InitPlayerGfxAnimation(player, ACTION_WAITING, player->MovDir);
6201 if (game.player_sleeping_delay_fixed != -1 &&
6202 game.player_sleeping_delay_random != -1 &&
6203 player->anim_delay_counter == 0 &&
6204 player->post_delay_counter == 0 &&
6205 FrameCounter >= player->frame_counter_sleeping)
6206 player->is_sleeping = TRUE;
6207 else if (game.player_boring_delay_fixed != -1 &&
6208 game.player_boring_delay_random != -1 &&
6209 FrameCounter >= player->frame_counter_bored)
6210 player->is_bored = TRUE;
6212 action = (player->is_sleeping ? ACTION_SLEEPING :
6213 player->is_bored ? ACTION_BORING : ACTION_WAITING);
6216 PlayLevelSoundElementAction(jx, jy, element, action);
6218 PlayLevelSoundElementActionIfLoop(jx, jy, element, action);
6220 else if (was_waiting) /* waiting -> not waiting */
6222 if (player->is_sleeping)
6223 PlayLevelSoundElementAction(jx, jy, element, ACTION_AWAKENING);
6225 player->is_waiting = FALSE;
6226 player->is_bored = FALSE;
6227 player->is_sleeping = FALSE;
6229 player->frame_counter_bored = -1;
6230 player->frame_counter_sleeping = -1;
6232 player->anim_delay_counter = 0;
6233 player->post_delay_counter = 0;
6235 player->special_action_bored = ACTION_DEFAULT;
6236 player->special_action_sleeping = ACTION_DEFAULT;
6241 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
6244 static byte stored_player_action[MAX_PLAYERS];
6245 static int num_stored_actions = 0;
6247 boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
6248 int left = player_action & JOY_LEFT;
6249 int right = player_action & JOY_RIGHT;
6250 int up = player_action & JOY_UP;
6251 int down = player_action & JOY_DOWN;
6252 int button1 = player_action & JOY_BUTTON_1;
6253 int button2 = player_action & JOY_BUTTON_2;
6254 int dx = (left ? -1 : right ? 1 : 0);
6255 int dy = (up ? -1 : down ? 1 : 0);
6258 stored_player_action[player->index_nr] = 0;
6259 num_stored_actions++;
6263 printf("::: player %d [%d]\n", player->index_nr, FrameCounter);
6266 if (!player->active || tape.pausing)
6272 printf("::: player %d acts [%d]\n", player->index_nr, FrameCounter);
6276 snapped = SnapField(player, dx, dy);
6280 dropped = DropElement(player);
6282 moved = MovePlayer(player, dx, dy);
6285 if (tape.single_step && tape.recording && !tape.pausing)
6287 if (button1 || (dropped && !moved))
6289 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
6290 SnapField(player, 0, 0); /* stop snapping */
6294 SetPlayerWaiting(player, FALSE);
6297 return player_action;
6299 stored_player_action[player->index_nr] = player_action;
6305 printf("::: player %d waits [%d]\n", player->index_nr, FrameCounter);
6308 /* no actions for this player (no input at player's configured device) */
6310 DigField(player, 0, 0, 0, 0, DF_NO_PUSH);
6311 SnapField(player, 0, 0);
6312 CheckGravityMovement(player);
6314 if (player->MovPos == 0)
6315 SetPlayerWaiting(player, TRUE);
6317 if (player->MovPos == 0) /* needed for tape.playing */
6318 player->is_moving = FALSE;
6324 if (tape.recording && num_stored_actions >= MAX_PLAYERS)
6326 printf("::: player %d recorded [%d]\n", player->index_nr, FrameCounter);
6328 TapeRecordAction(stored_player_action);
6329 num_stored_actions = 0;
6336 static void PlayerActions(struct PlayerInfo *player, byte player_action)
6338 static byte stored_player_action[MAX_PLAYERS];
6339 static int num_stored_actions = 0;
6340 boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
6341 int left = player_action & JOY_LEFT;
6342 int right = player_action & JOY_RIGHT;
6343 int up = player_action & JOY_UP;
6344 int down = player_action & JOY_DOWN;
6345 int button1 = player_action & JOY_BUTTON_1;
6346 int button2 = player_action & JOY_BUTTON_2;
6347 int dx = (left ? -1 : right ? 1 : 0);
6348 int dy = (up ? -1 : down ? 1 : 0);
6350 stored_player_action[player->index_nr] = 0;
6351 num_stored_actions++;
6353 printf("::: player %d [%d]\n", player->index_nr, FrameCounter);
6355 if (!player->active || tape.pausing)
6360 printf("::: player %d acts [%d]\n", player->index_nr, FrameCounter);
6363 snapped = SnapField(player, dx, dy);
6367 dropped = DropElement(player);
6369 moved = MovePlayer(player, dx, dy);
6372 if (tape.single_step && tape.recording && !tape.pausing)
6374 if (button1 || (dropped && !moved))
6376 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
6377 SnapField(player, 0, 0); /* stop snapping */
6381 stored_player_action[player->index_nr] = player_action;
6385 printf("::: player %d waits [%d]\n", player->index_nr, FrameCounter);
6387 /* no actions for this player (no input at player's configured device) */
6389 DigField(player, 0, 0, 0, 0, DF_NO_PUSH);
6390 SnapField(player, 0, 0);
6391 CheckGravityMovement(player);
6393 if (player->MovPos == 0)
6394 InitPlayerGfxAnimation(player, ACTION_DEFAULT, player->MovDir);
6396 if (player->MovPos == 0) /* needed for tape.playing */
6397 player->is_moving = FALSE;
6400 if (tape.recording && num_stored_actions >= MAX_PLAYERS)
6402 printf("::: player %d recorded [%d]\n", player->index_nr, FrameCounter);
6404 TapeRecordAction(stored_player_action);
6405 num_stored_actions = 0;
6412 static unsigned long action_delay = 0;
6413 unsigned long action_delay_value;
6414 int magic_wall_x = 0, magic_wall_y = 0;
6415 int i, x, y, element, graphic;
6416 byte *recorded_player_action;
6417 byte summarized_player_action = 0;
6419 byte tape_action[MAX_PLAYERS];
6422 if (game_status != GAME_MODE_PLAYING)
6425 action_delay_value =
6426 (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
6428 if (tape.playing && tape.index_search && !tape.pausing)
6429 action_delay_value = 0;
6431 /* ---------- main game synchronization point ---------- */
6433 WaitUntilDelayReached(&action_delay, action_delay_value);
6435 if (network_playing && !network_player_action_received)
6439 printf("DEBUG: try to get network player actions in time\n");
6443 #if defined(PLATFORM_UNIX)
6444 /* last chance to get network player actions without main loop delay */
6448 if (game_status != GAME_MODE_PLAYING)
6451 if (!network_player_action_received)
6455 printf("DEBUG: failed to get network player actions in time\n");
6466 printf("::: getting new tape action [%d]\n", FrameCounter);
6469 recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
6471 for (i = 0; i < MAX_PLAYERS; i++)
6473 summarized_player_action |= stored_player[i].action;
6475 if (!network_playing)
6476 stored_player[i].effective_action = stored_player[i].action;
6479 #if defined(PLATFORM_UNIX)
6480 if (network_playing)
6481 SendToServer_MovePlayer(summarized_player_action);
6484 if (!options.network && !setup.team_mode)
6485 local_player->effective_action = summarized_player_action;
6487 for (i = 0; i < MAX_PLAYERS; i++)
6489 int actual_player_action = stored_player[i].effective_action;
6491 if (stored_player[i].programmed_action)
6492 actual_player_action = stored_player[i].programmed_action;
6494 if (recorded_player_action)
6495 actual_player_action = recorded_player_action[i];
6497 tape_action[i] = PlayerActions(&stored_player[i], actual_player_action);
6499 if (tape.recording && tape_action[i] && !tape.player_participates[i])
6500 tape.player_participates[i] = TRUE; /* player just appeared from CE */
6502 ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
6507 TapeRecordAction(tape_action);
6510 network_player_action_received = FALSE;
6512 ScrollScreen(NULL, SCROLL_GO_ON);
6518 for (i = 0; i < MAX_PLAYERS; i++)
6519 stored_player[i].Frame++;
6523 if (game.engine_version < VERSION_IDENT(2,2,0,7))
6525 for (i = 0; i < MAX_PLAYERS; i++)
6527 struct PlayerInfo *player = &stored_player[i];
6531 if (player->active && player->is_pushing && player->is_moving &&
6534 ContinueMoving(x, y);
6536 /* continue moving after pushing (this is actually a bug) */
6537 if (!IS_MOVING(x, y))
6546 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
6548 Changed[x][y] = CE_BITMASK_DEFAULT;
6549 ChangeEvent[x][y] = CE_BITMASK_DEFAULT;
6552 if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
6554 printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
6555 printf("GameActions(): This should never happen!\n");
6557 ChangePage[x][y] = -1;
6562 if (WasJustMoving[x][y] > 0)
6563 WasJustMoving[x][y]--;
6564 if (WasJustFalling[x][y] > 0)
6565 WasJustFalling[x][y]--;
6570 /* reset finished pushing action (not done in ContinueMoving() to allow
6571 continous pushing animation for elements with zero push delay) */
6572 if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
6574 ResetGfxAnimation(x, y);
6575 DrawLevelField(x, y);
6580 if (IS_BLOCKED(x, y))
6584 Blocked2Moving(x, y, &oldx, &oldy);
6585 if (!IS_MOVING(oldx, oldy))
6587 printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
6588 printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
6589 printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
6590 printf("GameActions(): This should never happen!\n");
6596 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
6598 element = Feld[x][y];
6600 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
6602 graphic = el2img(element);
6608 printf("::: %d,%d: %d [%d]\n", x, y, element, FrameCounter);
6610 element = graphic = 0;
6614 if (graphic_info[graphic].anim_global_sync)
6615 GfxFrame[x][y] = FrameCounter;
6617 if (ANIM_MODE(graphic) == ANIM_RANDOM &&
6618 IS_NEXT_FRAME(GfxFrame[x][y], graphic))
6619 ResetRandomAnimationValue(x, y);
6621 SetRandomAnimationValue(x, y);
6624 PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
6627 if (IS_INACTIVE(element))
6629 if (IS_ANIMATED(graphic))
6630 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
6636 /* this may take place after moving, so 'element' may have changed */
6638 if (IS_CHANGING(x, y))
6640 if (IS_CHANGING(x, y) &&
6641 (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
6645 ChangeElement(x, y, ChangePage[x][y] != -1 ? ChangePage[x][y] :
6646 element_info[element].event_page_nr[CE_DELAY]);
6648 ChangeElement(x, y, element_info[element].event_page_nr[CE_DELAY]);
6651 element = Feld[x][y];
6652 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
6656 if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
6661 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
6663 if (element == EL_MOLE)
6664 printf("::: %d, %d, %d [%d]\n",
6665 IS_ANIMATED(graphic), IS_MOVING(x, y), Stop[x][y],
6669 if (element == EL_YAMYAM)
6670 printf("::: %d, %d, %d\n",
6671 IS_ANIMATED(graphic), IS_MOVING(x, y), Stop[x][y]);
6675 if (IS_ANIMATED(graphic) &&
6679 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
6682 if (element == EL_BUG)
6683 printf("::: %d, %d\n", graphic, GfxFrame[x][y]);
6687 if (element == EL_MOLE)
6688 printf("::: %d, %d\n", graphic, GfxFrame[x][y]);
6692 if (IS_GEM(element) || element == EL_SP_INFOTRON)
6693 EdelsteinFunkeln(x, y);
6695 else if ((element == EL_ACID ||
6696 element == EL_EXIT_OPEN ||
6697 element == EL_SP_EXIT_OPEN ||
6698 element == EL_SP_TERMINAL ||
6699 element == EL_SP_TERMINAL_ACTIVE ||
6700 element == EL_EXTRA_TIME ||
6701 element == EL_SHIELD_NORMAL ||
6702 element == EL_SHIELD_DEADLY) &&
6703 IS_ANIMATED(graphic))
6704 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
6705 else if (IS_MOVING(x, y))
6706 ContinueMoving(x, y);
6707 else if (IS_ACTIVE_BOMB(element))
6708 CheckDynamite(x, y);
6710 else if (element == EL_EXPLOSION && !game.explosions_delayed)
6711 Explode(x, y, ExplodePhase[x][y], EX_NORMAL);
6713 else if (element == EL_AMOEBA_GROWING)
6714 AmoebeWaechst(x, y);
6715 else if (element == EL_AMOEBA_SHRINKING)
6716 AmoebaDisappearing(x, y);
6718 #if !USE_NEW_AMOEBA_CODE
6719 else if (IS_AMOEBALIVE(element))
6720 AmoebeAbleger(x, y);
6723 else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
6725 else if (element == EL_EXIT_CLOSED)
6727 else if (element == EL_SP_EXIT_CLOSED)
6729 else if (element == EL_EXPANDABLE_WALL_GROWING)
6731 else if (element == EL_EXPANDABLE_WALL ||
6732 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
6733 element == EL_EXPANDABLE_WALL_VERTICAL ||
6734 element == EL_EXPANDABLE_WALL_ANY)
6736 else if (element == EL_FLAMES)
6737 CheckForDragon(x, y);
6739 else if (IS_AUTO_CHANGING(element))
6740 ChangeElement(x, y);
6742 else if (element == EL_EXPLOSION)
6743 ; /* drawing of correct explosion animation is handled separately */
6744 else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
6745 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
6748 /* this may take place after moving, so 'element' may have changed */
6749 if (IS_AUTO_CHANGING(Feld[x][y]))
6750 ChangeElement(x, y);
6753 if (IS_BELT_ACTIVE(element))
6754 PlayLevelSoundAction(x, y, ACTION_ACTIVE);
6756 if (game.magic_wall_active)
6758 int jx = local_player->jx, jy = local_player->jy;
6760 /* play the element sound at the position nearest to the player */
6761 if ((element == EL_MAGIC_WALL_FULL ||
6762 element == EL_MAGIC_WALL_ACTIVE ||
6763 element == EL_MAGIC_WALL_EMPTYING ||
6764 element == EL_BD_MAGIC_WALL_FULL ||
6765 element == EL_BD_MAGIC_WALL_ACTIVE ||
6766 element == EL_BD_MAGIC_WALL_EMPTYING) &&
6767 ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
6775 #if USE_NEW_AMOEBA_CODE
6776 /* new experimental amoeba growth stuff */
6778 if (!(FrameCounter % 8))
6781 static unsigned long random = 1684108901;
6783 for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
6786 x = (random >> 10) % lev_fieldx;
6787 y = (random >> 20) % lev_fieldy;
6789 x = RND(lev_fieldx);
6790 y = RND(lev_fieldy);
6792 element = Feld[x][y];
6794 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
6795 if (!IS_PLAYER(x,y) &&
6796 (element == EL_EMPTY ||
6797 element == EL_SAND ||
6798 element == EL_QUICKSAND_EMPTY ||
6799 element == EL_ACID_SPLASH_LEFT ||
6800 element == EL_ACID_SPLASH_RIGHT))
6802 if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
6803 (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
6804 (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
6805 (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
6806 Feld[x][y] = EL_AMOEBA_DROP;
6809 random = random * 129 + 1;
6815 if (game.explosions_delayed)
6818 game.explosions_delayed = FALSE;
6820 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
6822 element = Feld[x][y];
6824 if (ExplodeField[x][y])
6825 Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
6826 else if (element == EL_EXPLOSION)
6827 Explode(x, y, ExplodePhase[x][y], EX_NORMAL);
6829 ExplodeField[x][y] = EX_NO_EXPLOSION;
6832 game.explosions_delayed = TRUE;
6835 if (game.magic_wall_active)
6837 if (!(game.magic_wall_time_left % 4))
6839 int element = Feld[magic_wall_x][magic_wall_y];
6841 if (element == EL_BD_MAGIC_WALL_FULL ||
6842 element == EL_BD_MAGIC_WALL_ACTIVE ||
6843 element == EL_BD_MAGIC_WALL_EMPTYING)
6844 PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
6846 PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
6849 if (game.magic_wall_time_left > 0)
6851 game.magic_wall_time_left--;
6852 if (!game.magic_wall_time_left)
6854 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
6856 element = Feld[x][y];
6858 if (element == EL_MAGIC_WALL_ACTIVE ||
6859 element == EL_MAGIC_WALL_FULL)
6861 Feld[x][y] = EL_MAGIC_WALL_DEAD;
6862 DrawLevelField(x, y);
6864 else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
6865 element == EL_BD_MAGIC_WALL_FULL)
6867 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
6868 DrawLevelField(x, y);
6872 game.magic_wall_active = FALSE;
6877 if (game.light_time_left > 0)
6879 game.light_time_left--;
6881 if (game.light_time_left == 0)
6882 RedrawAllLightSwitchesAndInvisibleElements();
6885 if (game.timegate_time_left > 0)
6887 game.timegate_time_left--;
6889 if (game.timegate_time_left == 0)
6890 CloseAllOpenTimegates();
6893 for (i = 0; i < MAX_PLAYERS; i++)
6895 struct PlayerInfo *player = &stored_player[i];
6897 if (SHIELD_ON(player))
6899 if (player->shield_deadly_time_left)
6900 PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
6901 else if (player->shield_normal_time_left)
6902 PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
6906 if (TimeFrames >= FRAMES_PER_SECOND)
6911 for (i = 0; i < MAX_PLAYERS; i++)
6913 struct PlayerInfo *player = &stored_player[i];
6915 if (SHIELD_ON(player))
6917 player->shield_normal_time_left--;
6919 if (player->shield_deadly_time_left > 0)
6920 player->shield_deadly_time_left--;
6924 if (tape.recording || tape.playing)
6925 DrawVideoDisplay(VIDEO_STATE_TIME_ON, TimePlayed);
6931 if (TimeLeft <= 10 && setup.time_limit)
6932 PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, SOUND_MIDDLE);
6934 DrawText(DX_TIME, DY_TIME, int2str(TimeLeft, 3), FONT_TEXT_2);
6936 if (!TimeLeft && setup.time_limit)
6937 for (i = 0; i < MAX_PLAYERS; i++)
6938 KillHero(&stored_player[i]);
6940 else if (level.time == 0 && !AllPlayersGone) /* level without time limit */
6941 DrawText(DX_TIME, DY_TIME, int2str(TimePlayed, 3), FONT_TEXT_2);
6946 if (options.debug) /* calculate frames per second */
6948 static unsigned long fps_counter = 0;
6949 static int fps_frames = 0;
6950 unsigned long fps_delay_ms = Counter() - fps_counter;
6954 if (fps_delay_ms >= 500) /* calculate fps every 0.5 seconds */
6956 global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
6959 fps_counter = Counter();
6962 redraw_mask |= REDRAW_FPS;
6966 if (stored_player[0].jx != stored_player[0].last_jx ||
6967 stored_player[0].jy != stored_player[0].last_jy)
6968 printf("::: %d, %d, %d, %d, %d\n",
6969 stored_player[0].MovDir,
6970 stored_player[0].MovPos,
6971 stored_player[0].GfxPos,
6972 stored_player[0].Frame,
6973 stored_player[0].StepFrame);
6980 for (i = 0; i < MAX_PLAYERS; i++)
6983 MOVE_DELAY_NORMAL_SPEED / stored_player[i].move_delay_value;
6985 stored_player[i].Frame += move_frames;
6987 if (stored_player[i].MovPos != 0)
6988 stored_player[i].StepFrame += move_frames;
6993 if (local_player->show_envelope != 0 && local_player->MovPos == 0)
6995 ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
6997 local_player->show_envelope = 0;
7002 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
7004 int min_x = x, min_y = y, max_x = x, max_y = y;
7007 for (i = 0; i < MAX_PLAYERS; i++)
7009 int jx = stored_player[i].jx, jy = stored_player[i].jy;
7011 if (!stored_player[i].active || &stored_player[i] == player)
7014 min_x = MIN(min_x, jx);
7015 min_y = MIN(min_y, jy);
7016 max_x = MAX(max_x, jx);
7017 max_y = MAX(max_y, jy);
7020 return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
7023 static boolean AllPlayersInVisibleScreen()
7027 for (i = 0; i < MAX_PLAYERS; i++)
7029 int jx = stored_player[i].jx, jy = stored_player[i].jy;
7031 if (!stored_player[i].active)
7034 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
7041 void ScrollLevel(int dx, int dy)
7043 int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
7046 BlitBitmap(drawto_field, drawto_field,
7047 FX + TILEX * (dx == -1) - softscroll_offset,
7048 FY + TILEY * (dy == -1) - softscroll_offset,
7049 SXSIZE - TILEX * (dx!=0) + 2 * softscroll_offset,
7050 SYSIZE - TILEY * (dy!=0) + 2 * softscroll_offset,
7051 FX + TILEX * (dx == 1) - softscroll_offset,
7052 FY + TILEY * (dy == 1) - softscroll_offset);
7056 x = (dx == 1 ? BX1 : BX2);
7057 for (y = BY1; y <= BY2; y++)
7058 DrawScreenField(x, y);
7063 y = (dy == 1 ? BY1 : BY2);
7064 for (x = BX1; x <= BX2; x++)
7065 DrawScreenField(x, y);
7068 redraw_mask |= REDRAW_FIELD;
7071 static void CheckGravityMovement(struct PlayerInfo *player)
7073 if (game.gravity && !player->programmed_action)
7075 int move_dir_vertical = player->action & (MV_UP | MV_DOWN);
7076 int move_dir_horizontal = player->action & (MV_LEFT | MV_RIGHT);
7078 (player->last_move_dir & (MV_LEFT | MV_RIGHT) ?
7079 (move_dir_vertical ? move_dir_vertical : move_dir_horizontal) :
7080 (move_dir_horizontal ? move_dir_horizontal : move_dir_vertical));
7081 int jx = player->jx, jy = player->jy;
7082 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
7083 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
7084 int new_jx = jx + dx, new_jy = jy + dy;
7085 boolean field_under_player_is_free =
7086 (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
7087 boolean player_is_moving_to_valid_field =
7088 (IN_LEV_FIELD(new_jx, new_jy) &&
7089 (Feld[new_jx][new_jy] == EL_SP_BASE ||
7090 Feld[new_jx][new_jy] == EL_SAND));
7091 /* !!! extend EL_SAND to anything diggable !!! */
7093 if (field_under_player_is_free &&
7094 !player_is_moving_to_valid_field &&
7095 !IS_WALKABLE_INSIDE(Feld[jx][jy]))
7096 player->programmed_action = MV_DOWN;
7102 -----------------------------------------------------------------------------
7103 dx, dy: direction (non-diagonal) to try to move the player to
7104 real_dx, real_dy: direction as read from input device (can be diagonal)
7107 boolean MovePlayerOneStep(struct PlayerInfo *player,
7108 int dx, int dy, int real_dx, int real_dy)
7111 static int change_sides[4][2] =
7113 /* enter side leave side */
7114 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* moving left */
7115 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* moving right */
7116 { CH_SIDE_BOTTOM, CH_SIDE_TOP }, /* moving up */
7117 { CH_SIDE_TOP, CH_SIDE_BOTTOM } /* moving down */
7119 int move_direction = (dx == -1 ? MV_LEFT :
7120 dx == +1 ? MV_RIGHT :
7122 dy == +1 ? MV_DOWN : MV_NO_MOVING);
7123 int enter_side = change_sides[MV_DIR_BIT(move_direction)][0];
7124 int leave_side = change_sides[MV_DIR_BIT(move_direction)][1];
7126 int jx = player->jx, jy = player->jy;
7127 int new_jx = jx + dx, new_jy = jy + dy;
7131 if (!player->active || (!dx && !dy))
7132 return MF_NO_ACTION;
7134 player->MovDir = (dx < 0 ? MV_LEFT :
7137 dy > 0 ? MV_DOWN : MV_NO_MOVING);
7139 if (!IN_LEV_FIELD(new_jx, new_jy))
7140 return MF_NO_ACTION;
7142 if (!options.network && !AllPlayersInSight(player, new_jx, new_jy))
7143 return MF_NO_ACTION;
7146 element = MovingOrBlocked2Element(new_jx, new_jy);
7148 element = MovingOrBlocked2ElementIfNotLeaving(new_jx, new_jy);
7151 if (DONT_RUN_INTO(element))
7153 if (element == EL_ACID && dx == 0 && dy == 1)
7156 Feld[jx][jy] = EL_PLAYER_1;
7157 InitMovingField(jx, jy, MV_DOWN);
7158 Store[jx][jy] = EL_ACID;
7159 ContinueMoving(jx, jy);
7163 TestIfHeroRunsIntoBadThing(jx, jy, player->MovDir);
7168 can_move = DigField(player, new_jx, new_jy, real_dx, real_dy, DF_DIG);
7169 if (can_move != MF_MOVING)
7172 /* check if DigField() has caused relocation of the player */
7173 if (player->jx != jx || player->jy != jy)
7174 return MF_NO_ACTION;
7176 StorePlayer[jx][jy] = 0;
7177 player->last_jx = jx;
7178 player->last_jy = jy;
7179 player->jx = new_jx;
7180 player->jy = new_jy;
7181 StorePlayer[new_jx][new_jy] = player->element_nr;
7184 (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
7186 player->step_counter++;
7188 PlayerVisit[jx][jy] = FrameCounter;
7190 ScrollPlayer(player, SCROLL_INIT);
7193 if (IS_CUSTOM_ELEMENT(Feld[jx][jy]))
7195 CheckTriggeredElementSideChange(jx, jy, Feld[jx][jy], leave_side,
7196 CE_OTHER_GETS_LEFT);
7197 CheckElementSideChange(jx, jy, Feld[jx][jy], leave_side,
7198 CE_LEFT_BY_PLAYER, -1);
7201 if (IS_CUSTOM_ELEMENT(Feld[new_jx][new_jy]))
7203 CheckTriggeredElementSideChange(new_jx, new_jy, Feld[new_jx][new_jy],
7204 enter_side, CE_OTHER_GETS_ENTERED);
7205 CheckElementSideChange(new_jx, new_jy, Feld[new_jx][new_jy], enter_side,
7206 CE_ENTERED_BY_PLAYER, -1);
7213 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
7215 int jx = player->jx, jy = player->jy;
7216 int old_jx = jx, old_jy = jy;
7217 int moved = MF_NO_ACTION;
7219 if (!player->active || (!dx && !dy))
7223 if (!FrameReached(&player->move_delay, player->move_delay_value) &&
7227 if (!FrameReached(&player->move_delay, player->move_delay_value) &&
7228 !(tape.playing && tape.file_version < FILE_VERSION_2_0))
7232 /* remove the last programmed player action */
7233 player->programmed_action = 0;
7237 /* should only happen if pre-1.2 tape recordings are played */
7238 /* this is only for backward compatibility */
7240 int original_move_delay_value = player->move_delay_value;
7243 printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%ld]\n",
7247 /* scroll remaining steps with finest movement resolution */
7248 player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
7250 while (player->MovPos)
7252 ScrollPlayer(player, SCROLL_GO_ON);
7253 ScrollScreen(NULL, SCROLL_GO_ON);
7259 player->move_delay_value = original_move_delay_value;
7262 if (player->last_move_dir & (MV_LEFT | MV_RIGHT))
7264 if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
7265 moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
7269 if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
7270 moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
7276 if (moved & MF_MOVING && !ScreenMovPos &&
7277 (player == local_player || !options.network))
7279 int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
7280 int offset = (setup.scroll_delay ? 3 : 0);
7282 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
7284 /* actual player has left the screen -- scroll in that direction */
7285 if (jx != old_jx) /* player has moved horizontally */
7286 scroll_x += (jx - old_jx);
7287 else /* player has moved vertically */
7288 scroll_y += (jy - old_jy);
7292 if (jx != old_jx) /* player has moved horizontally */
7294 if ((player->MovDir == MV_LEFT && scroll_x > jx - MIDPOSX + offset) ||
7295 (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
7296 scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
7298 /* don't scroll over playfield boundaries */
7299 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
7300 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
7302 /* don't scroll more than one field at a time */
7303 scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
7305 /* don't scroll against the player's moving direction */
7306 if ((player->MovDir == MV_LEFT && scroll_x > old_scroll_x) ||
7307 (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
7308 scroll_x = old_scroll_x;
7310 else /* player has moved vertically */
7312 if ((player->MovDir == MV_UP && scroll_y > jy - MIDPOSY + offset) ||
7313 (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
7314 scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
7316 /* don't scroll over playfield boundaries */
7317 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
7318 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
7320 /* don't scroll more than one field at a time */
7321 scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
7323 /* don't scroll against the player's moving direction */
7324 if ((player->MovDir == MV_UP && scroll_y > old_scroll_y) ||
7325 (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
7326 scroll_y = old_scroll_y;
7330 if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
7332 if (!options.network && !AllPlayersInVisibleScreen())
7334 scroll_x = old_scroll_x;
7335 scroll_y = old_scroll_y;
7339 ScrollScreen(player, SCROLL_INIT);
7340 ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
7347 InitPlayerGfxAnimation(player, ACTION_DEFAULT);
7349 if (!(moved & MF_MOVING) && !player->is_pushing)
7354 player->StepFrame = 0;
7356 if (moved & MF_MOVING)
7358 if (old_jx != jx && old_jy == jy)
7359 player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
7360 else if (old_jx == jx && old_jy != jy)
7361 player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
7363 DrawLevelField(jx, jy); /* for "crumbled sand" */
7365 player->last_move_dir = player->MovDir;
7366 player->is_moving = TRUE;
7368 player->is_snapping = FALSE;
7372 player->is_switching = FALSE;
7378 static int change_sides[4][2] =
7380 /* enter side leave side */
7381 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* moving left */
7382 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* moving right */
7383 { CH_SIDE_BOTTOM, CH_SIDE_TOP }, /* moving up */
7384 { CH_SIDE_TOP, CH_SIDE_BOTTOM } /* moving down */
7386 int move_direction = player->MovDir;
7387 int enter_side = change_sides[MV_DIR_BIT(move_direction)][0];
7388 int leave_side = change_sides[MV_DIR_BIT(move_direction)][1];
7391 if (IS_CUSTOM_ELEMENT(Feld[old_jx][old_jy]))
7393 CheckTriggeredElementSideChange(old_jx, old_jy, Feld[old_jx][old_jy],
7394 leave_side, CE_OTHER_GETS_LEFT);
7395 CheckElementSideChange(old_jx, old_jy, Feld[old_jx][old_jy],
7396 leave_side, CE_LEFT_BY_PLAYER, -1);
7399 if (IS_CUSTOM_ELEMENT(Feld[jx][jy]))
7401 CheckTriggeredElementSideChange(jx, jy, Feld[jx][jy],
7402 enter_side, CE_OTHER_GETS_ENTERED);
7403 CheckElementSideChange(jx, jy, Feld[jx][jy],
7404 enter_side, CE_ENTERED_BY_PLAYER, -1);
7415 CheckGravityMovement(player);
7418 player->last_move_dir = MV_NO_MOVING;
7420 player->is_moving = FALSE;
7423 if (game.engine_version < VERSION_IDENT(3,0,7,0))
7425 TestIfHeroTouchesBadThing(jx, jy);
7426 TestIfPlayerTouchesCustomElement(jx, jy);
7429 if (!player->active)
7435 void ScrollPlayer(struct PlayerInfo *player, int mode)
7437 int jx = player->jx, jy = player->jy;
7438 int last_jx = player->last_jx, last_jy = player->last_jy;
7439 int move_stepsize = TILEX / player->move_delay_value;
7441 if (!player->active || !player->MovPos)
7444 if (mode == SCROLL_INIT)
7446 player->actual_frame_counter = FrameCounter;
7447 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
7449 if (Feld[last_jx][last_jy] == EL_EMPTY)
7450 Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
7457 else if (!FrameReached(&player->actual_frame_counter, 1))
7460 player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
7461 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
7463 if (Feld[last_jx][last_jy] == EL_PLAYER_IS_LEAVING)
7464 Feld[last_jx][last_jy] = EL_EMPTY;
7466 /* before DrawPlayer() to draw correct player graphic for this case */
7467 if (player->MovPos == 0)
7468 CheckGravityMovement(player);
7471 DrawPlayer(player); /* needed here only to cleanup last field */
7474 if (player->MovPos == 0) /* player reached destination field */
7476 if (IS_PASSABLE(Feld[last_jx][last_jy]))
7478 /* continue with normal speed after quickly moving through gate */
7479 HALVE_PLAYER_SPEED(player);
7481 /* be able to make the next move without delay */
7482 player->move_delay = 0;
7485 player->last_jx = jx;
7486 player->last_jy = jy;
7488 if (Feld[jx][jy] == EL_EXIT_OPEN ||
7489 Feld[jx][jy] == EL_SP_EXIT_OPEN ||
7490 Feld[jx][jy] == EL_SP_EXIT_OPENING) /* <-- special case */
7492 DrawPlayer(player); /* needed here only to cleanup last field */
7495 if (local_player->friends_still_needed == 0 ||
7496 IS_SP_ELEMENT(Feld[jx][jy]))
7497 player->LevelSolved = player->GameOver = TRUE;
7500 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
7502 TestIfHeroTouchesBadThing(jx, jy);
7503 TestIfPlayerTouchesCustomElement(jx, jy);
7505 TestIfElementTouchesCustomElement(jx, jy); /* for empty space */
7508 if (!player->active)
7512 if (tape.single_step && tape.recording && !tape.pausing &&
7513 !player->programmed_action)
7514 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
7518 void ScrollScreen(struct PlayerInfo *player, int mode)
7520 static unsigned long screen_frame_counter = 0;
7522 if (mode == SCROLL_INIT)
7524 /* set scrolling step size according to actual player's moving speed */
7525 ScrollStepSize = TILEX / player->move_delay_value;
7527 screen_frame_counter = FrameCounter;
7528 ScreenMovDir = player->MovDir;
7529 ScreenMovPos = player->MovPos;
7530 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
7533 else if (!FrameReached(&screen_frame_counter, 1))
7538 ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
7539 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
7540 redraw_mask |= REDRAW_FIELD;
7543 ScreenMovDir = MV_NO_MOVING;
7546 void TestIfPlayerTouchesCustomElement(int x, int y)
7548 static int xy[4][2] =
7555 static int change_sides[4][2] =
7557 /* center side border side */
7558 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
7559 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
7560 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
7561 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
7563 static int touch_dir[4] =
7570 int center_element = Feld[x][y]; /* should always be non-moving! */
7573 for (i = 0; i < 4; i++)
7575 int xx = x + xy[i][0];
7576 int yy = y + xy[i][1];
7577 int center_side = change_sides[i][0];
7578 int border_side = change_sides[i][1];
7581 if (!IN_LEV_FIELD(xx, yy))
7584 if (IS_PLAYER(x, y))
7586 if (game.engine_version < VERSION_IDENT(3,0,7,0))
7587 border_element = Feld[xx][yy]; /* may be moving! */
7588 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
7589 border_element = Feld[xx][yy];
7590 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
7591 border_element = MovingOrBlocked2Element(xx, yy);
7593 continue; /* center and border element do not touch */
7595 CheckTriggeredElementSideChange(xx, yy, border_element, border_side,
7596 CE_OTHER_GETS_TOUCHED);
7597 CheckElementSideChange(xx, yy, border_element, border_side,
7598 CE_TOUCHED_BY_PLAYER, -1);
7600 else if (IS_PLAYER(xx, yy))
7602 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
7604 struct PlayerInfo *player = PLAYERINFO(xx, yy);
7606 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
7607 continue; /* center and border element do not touch */
7610 CheckTriggeredElementSideChange(x, y, center_element, center_side,
7611 CE_OTHER_GETS_TOUCHED);
7612 CheckElementSideChange(x, y, center_element, center_side,
7613 CE_TOUCHED_BY_PLAYER, -1);
7620 void TestIfElementTouchesCustomElement(int x, int y)
7622 static int xy[4][2] =
7629 static int change_sides[4][2] =
7631 /* center side border side */
7632 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
7633 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
7634 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
7635 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
7637 static int touch_dir[4] =
7644 boolean change_center_element = FALSE;
7645 int center_element_change_page = 0;
7646 int center_element = Feld[x][y]; /* should always be non-moving! */
7649 for (i = 0; i < 4; i++)
7651 int xx = x + xy[i][0];
7652 int yy = y + xy[i][1];
7653 int center_side = change_sides[i][0];
7654 int border_side = change_sides[i][1];
7657 if (!IN_LEV_FIELD(xx, yy))
7660 if (game.engine_version < VERSION_IDENT(3,0,7,0))
7661 border_element = Feld[xx][yy]; /* may be moving! */
7662 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
7663 border_element = Feld[xx][yy];
7664 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
7665 border_element = MovingOrBlocked2Element(xx, yy);
7667 continue; /* center and border element do not touch */
7669 /* check for change of center element (but change it only once) */
7670 if (IS_CUSTOM_ELEMENT(center_element) &&
7671 HAS_ANY_CHANGE_EVENT(center_element, CE_OTHER_IS_TOUCHING) &&
7672 !change_center_element)
7674 for (j = 0; j < element_info[center_element].num_change_pages; j++)
7676 struct ElementChangeInfo *change =
7677 &element_info[center_element].change_page[j];
7679 if (change->can_change &&
7680 change->events & CH_EVENT_BIT(CE_OTHER_IS_TOUCHING) &&
7681 change->sides & border_side &&
7682 change->trigger_element == border_element)
7684 change_center_element = TRUE;
7685 center_element_change_page = j;
7692 /* check for change of border element */
7693 if (IS_CUSTOM_ELEMENT(border_element) &&
7694 HAS_ANY_CHANGE_EVENT(border_element, CE_OTHER_IS_TOUCHING))
7696 for (j = 0; j < element_info[border_element].num_change_pages; j++)
7698 struct ElementChangeInfo *change =
7699 &element_info[border_element].change_page[j];
7701 if (change->can_change &&
7702 change->events & CH_EVENT_BIT(CE_OTHER_IS_TOUCHING) &&
7703 change->sides & center_side &&
7704 change->trigger_element == center_element)
7706 CheckElementSideChange(xx, yy, border_element, CH_SIDE_ANY,
7707 CE_OTHER_IS_TOUCHING, j);
7714 if (change_center_element)
7715 CheckElementSideChange(x, y, center_element, CH_SIDE_ANY,
7716 CE_OTHER_IS_TOUCHING, center_element_change_page);
7719 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
7721 int i, kill_x = -1, kill_y = -1;
7722 static int test_xy[4][2] =
7729 static int test_dir[4] =
7737 for (i = 0; i < 4; i++)
7739 int test_x, test_y, test_move_dir, test_element;
7741 test_x = good_x + test_xy[i][0];
7742 test_y = good_y + test_xy[i][1];
7743 if (!IN_LEV_FIELD(test_x, test_y))
7747 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NO_MOVING);
7750 test_element = Feld[test_x][test_y];
7752 test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
7755 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
7756 2nd case: DONT_TOUCH style bad thing does not move away from good thing
7758 if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
7759 (DONT_TOUCH(test_element) && test_move_dir != test_dir[i]))
7767 if (kill_x != -1 || kill_y != -1)
7769 if (IS_PLAYER(good_x, good_y))
7771 struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
7773 if (player->shield_deadly_time_left > 0)
7774 Bang(kill_x, kill_y);
7775 else if (!PLAYER_PROTECTED(good_x, good_y))
7779 Bang(good_x, good_y);
7783 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
7785 int i, kill_x = -1, kill_y = -1;
7786 int bad_element = Feld[bad_x][bad_y];
7787 static int test_xy[4][2] =
7794 static int touch_dir[4] =
7801 static int test_dir[4] =
7809 if (bad_element == EL_EXPLOSION) /* skip just exploding bad things */
7812 for (i = 0; i < 4; i++)
7814 int test_x, test_y, test_move_dir, test_element;
7816 test_x = bad_x + test_xy[i][0];
7817 test_y = bad_y + test_xy[i][1];
7818 if (!IN_LEV_FIELD(test_x, test_y))
7822 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NO_MOVING);
7824 test_element = Feld[test_x][test_y];
7826 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
7827 2nd case: DONT_TOUCH style bad thing does not move away from good thing
7829 if ((DONT_RUN_INTO(bad_element) && bad_move_dir == test_dir[i]) ||
7830 (DONT_TOUCH(bad_element) && test_move_dir != test_dir[i]))
7832 /* good thing is player or penguin that does not move away */
7833 if (IS_PLAYER(test_x, test_y))
7835 struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
7837 if (bad_element == EL_ROBOT && player->is_moving)
7838 continue; /* robot does not kill player if he is moving */
7840 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
7842 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
7843 continue; /* center and border element do not touch */
7850 else if (test_element == EL_PENGUIN)
7859 if (kill_x != -1 || kill_y != -1)
7861 if (IS_PLAYER(kill_x, kill_y))
7863 struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
7865 if (player->shield_deadly_time_left > 0)
7867 else if (!PLAYER_PROTECTED(kill_x, kill_y))
7871 Bang(kill_x, kill_y);
7875 void TestIfHeroTouchesBadThing(int x, int y)
7877 TestIfGoodThingHitsBadThing(x, y, MV_NO_MOVING);
7880 void TestIfHeroRunsIntoBadThing(int x, int y, int move_dir)
7882 TestIfGoodThingHitsBadThing(x, y, move_dir);
7885 void TestIfBadThingTouchesHero(int x, int y)
7887 TestIfBadThingHitsGoodThing(x, y, MV_NO_MOVING);
7890 void TestIfBadThingRunsIntoHero(int x, int y, int move_dir)
7892 TestIfBadThingHitsGoodThing(x, y, move_dir);
7895 void TestIfFriendTouchesBadThing(int x, int y)
7897 TestIfGoodThingHitsBadThing(x, y, MV_NO_MOVING);
7900 void TestIfBadThingTouchesFriend(int x, int y)
7902 TestIfBadThingHitsGoodThing(x, y, MV_NO_MOVING);
7905 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
7907 int i, kill_x = bad_x, kill_y = bad_y;
7908 static int xy[4][2] =
7916 for (i = 0; i < 4; i++)
7920 x = bad_x + xy[i][0];
7921 y = bad_y + xy[i][1];
7922 if (!IN_LEV_FIELD(x, y))
7925 element = Feld[x][y];
7926 if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
7927 element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
7935 if (kill_x != bad_x || kill_y != bad_y)
7939 void KillHero(struct PlayerInfo *player)
7941 int jx = player->jx, jy = player->jy;
7943 if (!player->active)
7946 /* remove accessible field at the player's position */
7947 Feld[jx][jy] = EL_EMPTY;
7949 /* deactivate shield (else Bang()/Explode() would not work right) */
7950 player->shield_normal_time_left = 0;
7951 player->shield_deadly_time_left = 0;
7957 static void KillHeroUnlessProtected(int x, int y)
7959 if (!PLAYER_PROTECTED(x, y))
7960 KillHero(PLAYERINFO(x, y));
7963 void BuryHero(struct PlayerInfo *player)
7965 int jx = player->jx, jy = player->jy;
7967 if (!player->active)
7971 PlayLevelSoundElementAction(jx, jy, player->element_nr, ACTION_DYING);
7973 PlayLevelSound(jx, jy, SND_CLASS_PLAYER_DYING);
7975 PlayLevelSound(jx, jy, SND_GAME_LOSING);
7977 player->GameOver = TRUE;
7981 void RemoveHero(struct PlayerInfo *player)
7983 int jx = player->jx, jy = player->jy;
7984 int i, found = FALSE;
7986 player->present = FALSE;
7987 player->active = FALSE;
7989 if (!ExplodeField[jx][jy])
7990 StorePlayer[jx][jy] = 0;
7992 for (i = 0; i < MAX_PLAYERS; i++)
7993 if (stored_player[i].active)
7997 AllPlayersGone = TRUE;
8004 =============================================================================
8005 checkDiagonalPushing()
8006 -----------------------------------------------------------------------------
8007 check if diagonal input device direction results in pushing of object
8008 (by checking if the alternative direction is walkable, diggable, ...)
8009 =============================================================================
8012 static boolean checkDiagonalPushing(struct PlayerInfo *player,
8013 int x, int y, int real_dx, int real_dy)
8015 int jx, jy, dx, dy, xx, yy;
8017 if (real_dx == 0 || real_dy == 0) /* no diagonal direction => push */
8020 /* diagonal direction: check alternative direction */
8025 xx = jx + (dx == 0 ? real_dx : 0);
8026 yy = jy + (dy == 0 ? real_dy : 0);
8028 return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
8032 =============================================================================
8034 -----------------------------------------------------------------------------
8035 x, y: field next to player (non-diagonal) to try to dig to
8036 real_dx, real_dy: direction as read from input device (can be diagonal)
8037 =============================================================================
8040 int DigField(struct PlayerInfo *player,
8041 int x, int y, int real_dx, int real_dy, int mode)
8043 static int change_sides[4] =
8045 CH_SIDE_RIGHT, /* moving left */
8046 CH_SIDE_LEFT, /* moving right */
8047 CH_SIDE_BOTTOM, /* moving up */
8048 CH_SIDE_TOP, /* moving down */
8050 boolean use_spring_bug = (game.engine_version < VERSION_IDENT(2,2,0,0));
8051 int jx = player->jx, jy = player->jy;
8052 int dx = x - jx, dy = y - jy;
8053 int nextx = x + dx, nexty = y + dy;
8054 int move_direction = (dx == -1 ? MV_LEFT :
8055 dx == +1 ? MV_RIGHT :
8057 dy == +1 ? MV_DOWN : MV_NO_MOVING);
8058 int dig_side = change_sides[MV_DIR_BIT(move_direction)];
8061 if (player->MovPos == 0)
8063 player->is_digging = FALSE;
8064 player->is_collecting = FALSE;
8067 if (player->MovPos == 0) /* last pushing move finished */
8068 player->is_pushing = FALSE;
8070 if (mode == DF_NO_PUSH) /* player just stopped pushing */
8072 player->is_switching = FALSE;
8073 player->push_delay = 0;
8075 return MF_NO_ACTION;
8078 if (IS_MOVING(x, y) || IS_PLAYER(x, y))
8079 return MF_NO_ACTION;
8082 if (IS_TUBE(Feld[jx][jy]) || IS_TUBE(Back[jx][jy]))
8084 if (IS_TUBE(Feld[jx][jy]) ||
8085 (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0)))
8089 int tube_element = (IS_TUBE(Feld[jx][jy]) ? Feld[jx][jy] : Back[jx][jy]);
8090 int tube_leave_directions[][2] =
8092 { EL_TUBE_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
8093 { EL_TUBE_VERTICAL, MV_UP | MV_DOWN },
8094 { EL_TUBE_HORIZONTAL, MV_LEFT | MV_RIGHT },
8095 { EL_TUBE_VERTICAL_LEFT, MV_LEFT | MV_UP | MV_DOWN },
8096 { EL_TUBE_VERTICAL_RIGHT, MV_RIGHT | MV_UP | MV_DOWN },
8097 { EL_TUBE_HORIZONTAL_UP, MV_LEFT | MV_RIGHT | MV_UP },
8098 { EL_TUBE_HORIZONTAL_DOWN, MV_LEFT | MV_RIGHT | MV_DOWN },
8099 { EL_TUBE_LEFT_UP, MV_LEFT | MV_UP },
8100 { EL_TUBE_LEFT_DOWN, MV_LEFT | MV_DOWN },
8101 { EL_TUBE_RIGHT_UP, MV_RIGHT | MV_UP },
8102 { EL_TUBE_RIGHT_DOWN, MV_RIGHT | MV_DOWN },
8103 { -1, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN }
8106 while (tube_leave_directions[i][0] != tube_element)
8109 if (tube_leave_directions[i][0] == -1) /* should not happen */
8113 if (!(tube_leave_directions[i][1] & move_direction))
8114 return MF_NO_ACTION; /* tube has no opening in this direction */
8117 element = Feld[x][y];
8119 if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
8120 game.engine_version >= VERSION_IDENT(2,2,0,0))
8121 return MF_NO_ACTION;
8125 case EL_SP_PORT_LEFT:
8126 case EL_SP_PORT_RIGHT:
8128 case EL_SP_PORT_DOWN:
8129 case EL_SP_PORT_HORIZONTAL:
8130 case EL_SP_PORT_VERTICAL:
8131 case EL_SP_PORT_ANY:
8132 case EL_SP_GRAVITY_PORT_LEFT:
8133 case EL_SP_GRAVITY_PORT_RIGHT:
8134 case EL_SP_GRAVITY_PORT_UP:
8135 case EL_SP_GRAVITY_PORT_DOWN:
8137 element != EL_SP_PORT_LEFT &&
8138 element != EL_SP_GRAVITY_PORT_LEFT &&
8139 element != EL_SP_PORT_HORIZONTAL &&
8140 element != EL_SP_PORT_ANY) ||
8142 element != EL_SP_PORT_RIGHT &&
8143 element != EL_SP_GRAVITY_PORT_RIGHT &&
8144 element != EL_SP_PORT_HORIZONTAL &&
8145 element != EL_SP_PORT_ANY) ||
8147 element != EL_SP_PORT_UP &&
8148 element != EL_SP_GRAVITY_PORT_UP &&
8149 element != EL_SP_PORT_VERTICAL &&
8150 element != EL_SP_PORT_ANY) ||
8152 element != EL_SP_PORT_DOWN &&
8153 element != EL_SP_GRAVITY_PORT_DOWN &&
8154 element != EL_SP_PORT_VERTICAL &&
8155 element != EL_SP_PORT_ANY) ||
8156 !IN_LEV_FIELD(nextx, nexty) ||
8157 !IS_FREE(nextx, nexty))
8158 return MF_NO_ACTION;
8160 if (element == EL_SP_GRAVITY_PORT_LEFT ||
8161 element == EL_SP_GRAVITY_PORT_RIGHT ||
8162 element == EL_SP_GRAVITY_PORT_UP ||
8163 element == EL_SP_GRAVITY_PORT_DOWN)
8164 game.gravity = !game.gravity;
8166 /* automatically move to the next field with double speed */
8167 player->programmed_action = move_direction;
8168 DOUBLE_PLAYER_SPEED(player);
8170 PlayLevelSound(x, y, SND_CLASS_SP_PORT_PASSING);
8174 case EL_TUBE_VERTICAL:
8175 case EL_TUBE_HORIZONTAL:
8176 case EL_TUBE_VERTICAL_LEFT:
8177 case EL_TUBE_VERTICAL_RIGHT:
8178 case EL_TUBE_HORIZONTAL_UP:
8179 case EL_TUBE_HORIZONTAL_DOWN:
8180 case EL_TUBE_LEFT_UP:
8181 case EL_TUBE_LEFT_DOWN:
8182 case EL_TUBE_RIGHT_UP:
8183 case EL_TUBE_RIGHT_DOWN:
8186 int tube_enter_directions[][2] =
8188 { EL_TUBE_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
8189 { EL_TUBE_VERTICAL, MV_UP | MV_DOWN },
8190 { EL_TUBE_HORIZONTAL, MV_LEFT | MV_RIGHT },
8191 { EL_TUBE_VERTICAL_LEFT, MV_RIGHT | MV_UP | MV_DOWN },
8192 { EL_TUBE_VERTICAL_RIGHT, MV_LEFT | MV_UP | MV_DOWN },
8193 { EL_TUBE_HORIZONTAL_UP, MV_LEFT | MV_RIGHT | MV_DOWN },
8194 { EL_TUBE_HORIZONTAL_DOWN, MV_LEFT | MV_RIGHT | MV_UP },
8195 { EL_TUBE_LEFT_UP, MV_RIGHT | MV_DOWN },
8196 { EL_TUBE_LEFT_DOWN, MV_RIGHT | MV_UP },
8197 { EL_TUBE_RIGHT_UP, MV_LEFT | MV_DOWN },
8198 { EL_TUBE_RIGHT_DOWN, MV_LEFT | MV_UP },
8199 { -1, MV_NO_MOVING }
8202 while (tube_enter_directions[i][0] != element)
8205 if (tube_enter_directions[i][0] == -1) /* should not happen */
8209 if (!(tube_enter_directions[i][1] & move_direction))
8210 return MF_NO_ACTION; /* tube has no opening in this direction */
8212 PlayLevelSound(x, y, SND_CLASS_TUBE_WALKING);
8218 if (IS_WALKABLE(element))
8220 int sound_action = ACTION_WALKING;
8222 if (element >= EL_GATE_1 && element <= EL_GATE_4)
8224 if (!player->key[element - EL_GATE_1])
8225 return MF_NO_ACTION;
8227 else if (element >= EL_GATE_1_GRAY && element <= EL_GATE_4_GRAY)
8229 if (!player->key[element - EL_GATE_1_GRAY])
8230 return MF_NO_ACTION;
8232 else if (element == EL_EXIT_OPEN ||
8233 element == EL_SP_EXIT_OPEN ||
8234 element == EL_SP_EXIT_OPENING)
8236 sound_action = ACTION_PASSING; /* player is passing exit */
8238 else if (element == EL_EMPTY)
8240 sound_action = ACTION_MOVING; /* nothing to walk on */
8243 /* play sound from background or player, whatever is available */
8244 if (element_info[element].sound[sound_action] != SND_UNDEFINED)
8245 PlayLevelSoundElementAction(x, y, element, sound_action);
8247 PlayLevelSoundElementAction(x, y, player->element_nr, sound_action);
8251 else if (IS_PASSABLE(element))
8253 if (!IN_LEV_FIELD(nextx, nexty) || !IS_FREE(nextx, nexty))
8254 return MF_NO_ACTION;
8257 if (CAN_MOVE(element)) /* only fixed elements can be passed! */
8258 return MF_NO_ACTION;
8261 if (element >= EL_EM_GATE_1 && element <= EL_EM_GATE_4)
8263 if (!player->key[element - EL_EM_GATE_1])
8264 return MF_NO_ACTION;
8266 else if (element >= EL_EM_GATE_1_GRAY && element <= EL_EM_GATE_4_GRAY)
8268 if (!player->key[element - EL_EM_GATE_1_GRAY])
8269 return MF_NO_ACTION;
8272 /* automatically move to the next field with double speed */
8273 player->programmed_action = move_direction;
8274 DOUBLE_PLAYER_SPEED(player);
8276 PlayLevelSoundAction(x, y, ACTION_PASSING);
8280 else if (IS_DIGGABLE(element))
8284 if (mode != DF_SNAP)
8287 GfxElement[x][y] = GFX_ELEMENT(element);
8290 (GFX_CRUMBLED(element) ? EL_SAND : GFX_ELEMENT(element));
8292 player->is_digging = TRUE;
8295 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
8297 CheckTriggeredElementChange(x, y, element, CE_OTHER_GETS_DIGGED);
8300 if (mode == DF_SNAP)
8301 TestIfElementTouchesCustomElement(x, y); /* for empty space */
8306 else if (IS_COLLECTIBLE(element))
8310 if (mode != DF_SNAP)
8312 GfxElement[x][y] = element;
8313 player->is_collecting = TRUE;
8316 if (element == EL_SPEED_PILL)
8317 player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
8318 else if (element == EL_EXTRA_TIME && level.time > 0)
8321 DrawText(DX_TIME, DY_TIME, int2str(TimeLeft, 3), FONT_TEXT_2);
8323 else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
8325 player->shield_normal_time_left += 10;
8326 if (element == EL_SHIELD_DEADLY)
8327 player->shield_deadly_time_left += 10;
8329 else if (element == EL_DYNAMITE || element == EL_SP_DISK_RED)
8331 if (player->inventory_size < MAX_INVENTORY_SIZE)
8332 player->inventory_element[player->inventory_size++] = element;
8334 DrawText(DX_DYNAMITE, DY_DYNAMITE,
8335 int2str(local_player->inventory_size, 3), FONT_TEXT_2);
8337 else if (element == EL_DYNABOMB_INCREASE_NUMBER)
8339 player->dynabomb_count++;
8340 player->dynabombs_left++;
8342 else if (element == EL_DYNABOMB_INCREASE_SIZE)
8344 player->dynabomb_size++;
8346 else if (element == EL_DYNABOMB_INCREASE_POWER)
8348 player->dynabomb_xl = TRUE;
8350 else if ((element >= EL_KEY_1 && element <= EL_KEY_4) ||
8351 (element >= EL_EM_KEY_1 && element <= EL_EM_KEY_4))
8353 int key_nr = (element >= EL_KEY_1 && element <= EL_KEY_4 ?
8354 element - EL_KEY_1 : element - EL_EM_KEY_1);
8356 player->key[key_nr] = TRUE;
8358 DrawMiniGraphicExt(drawto, DX_KEYS + key_nr * MINI_TILEX, DY_KEYS,
8359 el2edimg(EL_KEY_1 + key_nr));
8360 redraw_mask |= REDRAW_DOOR_1;
8362 else if (IS_ENVELOPE(element))
8365 player->show_envelope = element;
8367 ShowEnvelope(element - EL_ENVELOPE_1);
8370 else if (IS_DROPPABLE(element)) /* can be collected and dropped */
8374 for (i = 0; i < element_info[element].collect_count; i++)
8375 if (player->inventory_size < MAX_INVENTORY_SIZE)
8376 player->inventory_element[player->inventory_size++] = element;
8378 DrawText(DX_DYNAMITE, DY_DYNAMITE,
8379 int2str(local_player->inventory_size, 3), FONT_TEXT_2);
8381 else if (element_info[element].collect_count > 0)
8383 local_player->gems_still_needed -=
8384 element_info[element].collect_count;
8385 if (local_player->gems_still_needed < 0)
8386 local_player->gems_still_needed = 0;
8388 DrawText(DX_EMERALDS, DY_EMERALDS,
8389 int2str(local_player->gems_still_needed, 3), FONT_TEXT_2);
8392 RaiseScoreElement(element);
8393 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
8395 CheckTriggeredElementChange(x, y, element, CE_OTHER_GETS_COLLECTED);
8398 if (mode == DF_SNAP)
8399 TestIfElementTouchesCustomElement(x, y); /* for empty space */
8404 else if (IS_PUSHABLE(element))
8406 if (mode == DF_SNAP && element != EL_BD_ROCK)
8407 return MF_NO_ACTION;
8409 if (CAN_FALL(element) && dy)
8410 return MF_NO_ACTION;
8412 if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
8413 !(element == EL_SPRING && use_spring_bug))
8414 return MF_NO_ACTION;
8417 if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
8418 ((move_direction & MV_VERTICAL &&
8419 ((element_info[element].move_pattern & MV_LEFT &&
8420 IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
8421 (element_info[element].move_pattern & MV_RIGHT &&
8422 IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
8423 (move_direction & MV_HORIZONTAL &&
8424 ((element_info[element].move_pattern & MV_UP &&
8425 IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
8426 (element_info[element].move_pattern & MV_DOWN &&
8427 IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
8428 return MF_NO_ACTION;
8432 /* do not push elements already moving away faster than player */
8433 if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
8434 ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
8435 return MF_NO_ACTION;
8437 if (element == EL_SPRING && MovDir[x][y] != MV_NO_MOVING)
8438 return MF_NO_ACTION;
8442 if (game.engine_version >= VERSION_IDENT(3,0,7,1))
8444 if (player->push_delay_value == -1)
8445 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
8447 else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
8449 if (!player->is_pushing)
8450 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
8454 if (game.engine_version >= VERSION_IDENT(2,2,0,7) &&
8455 (game.engine_version < VERSION_IDENT(3,0,7,1) ||
8456 !player_is_pushing))
8457 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
8460 if (!player->is_pushing &&
8461 game.engine_version >= VERSION_IDENT(2,2,0,7))
8462 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
8466 printf("::: push delay: %ld [%d, %d] [%d]\n",
8467 player->push_delay_value, FrameCounter, game.engine_version,
8468 player->is_pushing);
8471 player->is_pushing = TRUE;
8473 if (!(IN_LEV_FIELD(nextx, nexty) &&
8474 (IS_FREE(nextx, nexty) ||
8475 (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY &&
8476 IS_SB_ELEMENT(element)))))
8477 return MF_NO_ACTION;
8479 if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
8480 return MF_NO_ACTION;
8482 if (player->push_delay == 0) /* new pushing; restart delay */
8483 player->push_delay = FrameCounter;
8485 if (!FrameReached(&player->push_delay, player->push_delay_value) &&
8486 !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
8487 element != EL_SPRING && element != EL_BALLOON)
8489 /* make sure that there is no move delay before next try to push */
8490 if (game.engine_version >= VERSION_IDENT(3,0,7,1))
8491 player->move_delay = INITIAL_MOVE_DELAY_OFF;
8493 return MF_NO_ACTION;
8497 printf("::: NOW PUSHING... [%d]\n", FrameCounter);
8500 if (IS_SB_ELEMENT(element))
8502 if (element == EL_SOKOBAN_FIELD_FULL)
8504 Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
8505 local_player->sokobanfields_still_needed++;
8508 if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
8510 Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
8511 local_player->sokobanfields_still_needed--;
8514 Feld[x][y] = EL_SOKOBAN_OBJECT;
8516 if (Back[x][y] == Back[nextx][nexty])
8517 PlayLevelSoundAction(x, y, ACTION_PUSHING);
8518 else if (Back[x][y] != 0)
8519 PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
8522 PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
8525 if (local_player->sokobanfields_still_needed == 0 &&
8526 game.emulation == EMU_SOKOBAN)
8528 player->LevelSolved = player->GameOver = TRUE;
8529 PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
8533 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
8535 InitMovingField(x, y, move_direction);
8536 GfxAction[x][y] = ACTION_PUSHING;
8538 if (mode == DF_SNAP)
8539 ContinueMoving(x, y);
8541 MovPos[x][y] = (dx != 0 ? dx : dy);
8543 Pushed[x][y] = TRUE;
8544 Pushed[nextx][nexty] = TRUE;
8546 if (game.engine_version < VERSION_IDENT(2,2,0,7))
8547 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
8549 player->push_delay_value = -1; /* get new value later */
8551 CheckTriggeredElementSideChange(x, y, element, dig_side,
8552 CE_OTHER_GETS_PUSHED);
8553 CheckElementSideChange(x, y, element, dig_side,
8554 CE_PUSHED_BY_PLAYER, -1);
8558 else if (IS_SWITCHABLE(element))
8560 if (PLAYER_SWITCHING(player, x, y))
8563 player->is_switching = TRUE;
8564 player->switch_x = x;
8565 player->switch_y = y;
8567 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
8569 if (element == EL_ROBOT_WHEEL)
8571 Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
8575 DrawLevelField(x, y);
8577 else if (element == EL_SP_TERMINAL)
8581 for (yy = 0; yy < lev_fieldy; yy++) for (xx=0; xx < lev_fieldx; xx++)
8583 if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
8585 else if (Feld[xx][yy] == EL_SP_TERMINAL)
8586 Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
8589 else if (IS_BELT_SWITCH(element))
8591 ToggleBeltSwitch(x, y);
8593 else if (element == EL_SWITCHGATE_SWITCH_UP ||
8594 element == EL_SWITCHGATE_SWITCH_DOWN)
8596 ToggleSwitchgateSwitch(x, y);
8598 else if (element == EL_LIGHT_SWITCH ||
8599 element == EL_LIGHT_SWITCH_ACTIVE)
8601 ToggleLightSwitch(x, y);
8604 PlayLevelSound(x, y, element == EL_LIGHT_SWITCH ?
8605 SND_LIGHT_SWITCH_ACTIVATING :
8606 SND_LIGHT_SWITCH_DEACTIVATING);
8609 else if (element == EL_TIMEGATE_SWITCH)
8611 ActivateTimegateSwitch(x, y);
8613 else if (element == EL_BALLOON_SWITCH_LEFT ||
8614 element == EL_BALLOON_SWITCH_RIGHT ||
8615 element == EL_BALLOON_SWITCH_UP ||
8616 element == EL_BALLOON_SWITCH_DOWN ||
8617 element == EL_BALLOON_SWITCH_ANY)
8619 if (element == EL_BALLOON_SWITCH_ANY)
8620 game.balloon_dir = move_direction;
8622 game.balloon_dir = (element == EL_BALLOON_SWITCH_LEFT ? MV_LEFT :
8623 element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
8624 element == EL_BALLOON_SWITCH_UP ? MV_UP :
8625 element == EL_BALLOON_SWITCH_DOWN ? MV_DOWN :
8628 else if (element == EL_LAMP)
8630 Feld[x][y] = EL_LAMP_ACTIVE;
8631 local_player->lights_still_needed--;
8633 DrawLevelField(x, y);
8635 else if (element == EL_TIME_ORB_FULL)
8637 Feld[x][y] = EL_TIME_ORB_EMPTY;
8639 DrawText(DX_TIME, DY_TIME, int2str(TimeLeft, 3), FONT_TEXT_2);
8641 DrawLevelField(x, y);
8644 PlaySoundStereo(SND_TIME_ORB_FULL_COLLECTING, SOUND_MIDDLE);
8652 if (!PLAYER_SWITCHING(player, x, y))
8654 player->is_switching = TRUE;
8655 player->switch_x = x;
8656 player->switch_y = y;
8658 CheckTriggeredElementSideChange(x, y, element, dig_side,
8659 CE_OTHER_IS_SWITCHING);
8660 CheckElementSideChange(x, y, element, dig_side, CE_SWITCHED, -1);
8663 CheckTriggeredElementSideChange(x, y, element, dig_side,
8664 CE_OTHER_GETS_PRESSED);
8665 CheckElementSideChange(x, y, element, dig_side,
8666 CE_PRESSED_BY_PLAYER, -1);
8669 return MF_NO_ACTION;
8672 player->push_delay = 0;
8674 if (Feld[x][y] != element) /* really digged/collected something */
8675 player->is_collecting = !player->is_digging;
8680 boolean SnapField(struct PlayerInfo *player, int dx, int dy)
8682 int jx = player->jx, jy = player->jy;
8683 int x = jx + dx, y = jy + dy;
8684 int snap_direction = (dx == -1 ? MV_LEFT :
8685 dx == +1 ? MV_RIGHT :
8687 dy == +1 ? MV_DOWN : MV_NO_MOVING);
8689 if (player->MovPos && game.engine_version >= VERSION_IDENT(2,2,0,0))
8692 if (!player->active || !IN_LEV_FIELD(x, y))
8700 if (player->MovPos == 0)
8701 player->is_pushing = FALSE;
8703 player->is_snapping = FALSE;
8705 if (player->MovPos == 0)
8707 player->is_moving = FALSE;
8708 player->is_digging = FALSE;
8709 player->is_collecting = FALSE;
8715 if (player->is_snapping)
8718 player->MovDir = snap_direction;
8720 player->is_moving = FALSE;
8721 player->is_digging = FALSE;
8722 player->is_collecting = FALSE;
8724 if (DigField(player, x, y, 0, 0, DF_SNAP) == MF_NO_ACTION)
8727 player->is_snapping = TRUE;
8729 player->is_moving = FALSE;
8730 player->is_digging = FALSE;
8731 player->is_collecting = FALSE;
8733 DrawLevelField(x, y);
8739 boolean DropElement(struct PlayerInfo *player)
8741 int jx = player->jx, jy = player->jy;
8744 if (!player->active || player->MovPos)
8747 old_element = Feld[jx][jy];
8749 /* check if player has anything that can be dropped */
8750 if (player->inventory_size == 0 && player->dynabombs_left == 0)
8753 /* check if anything can be dropped at the current position */
8754 if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
8757 /* collected custom elements can only be dropped on empty fields */
8758 if (player->inventory_size > 0 &&
8759 IS_CUSTOM_ELEMENT(player->inventory_element[player->inventory_size - 1])
8760 && old_element != EL_EMPTY)
8763 if (old_element != EL_EMPTY)
8764 Back[jx][jy] = old_element; /* store old element on this field */
8766 MovDelay[jx][jy] = 96;
8768 ResetGfxAnimation(jx, jy);
8769 ResetRandomAnimationValue(jx, jy);
8771 if (player->inventory_size > 0)
8773 int new_element = player->inventory_element[--player->inventory_size];
8775 Feld[jx][jy] = (new_element == EL_DYNAMITE ? EL_DYNAMITE_ACTIVE :
8776 new_element == EL_SP_DISK_RED ? EL_SP_DISK_RED_ACTIVE :
8779 DrawText(DX_DYNAMITE, DY_DYNAMITE,
8780 int2str(local_player->inventory_size, 3), FONT_TEXT_2);
8782 if (IN_SCR_FIELD(SCREENX(jx), SCREENY(jy)))
8783 DrawGraphicThruMask(SCREENX(jx), SCREENY(jy), el2img(Feld[jx][jy]), 0);
8785 PlayLevelSoundAction(jx, jy, ACTION_DROPPING);
8787 CheckTriggeredElementChange(jx, jy, new_element, CE_OTHER_GETS_DROPPED);
8788 CheckElementChange(jx, jy, new_element, CE_DROPPED_BY_PLAYER);
8790 TestIfElementTouchesCustomElement(jx, jy);
8792 else /* player is dropping a dyna bomb */
8794 player->dynabombs_left--;
8797 EL_DYNABOMB_PLAYER_1_ACTIVE + (player->element_nr - EL_PLAYER_1);
8799 if (IN_SCR_FIELD(SCREENX(jx), SCREENY(jy)))
8800 DrawGraphicThruMask(SCREENX(jx), SCREENY(jy), el2img(Feld[jx][jy]), 0);
8802 PlayLevelSoundAction(jx, jy, ACTION_DROPPING);
8808 /* ------------------------------------------------------------------------- */
8809 /* game sound playing functions */
8810 /* ------------------------------------------------------------------------- */
8812 static int *loop_sound_frame = NULL;
8813 static int *loop_sound_volume = NULL;
8815 void InitPlayLevelSound()
8817 int num_sounds = getSoundListSize();
8819 if (loop_sound_frame != NULL)
8820 free(loop_sound_frame);
8822 if (loop_sound_volume != NULL)
8823 free(loop_sound_volume);
8825 loop_sound_frame = checked_calloc(num_sounds * sizeof(int));
8826 loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
8829 static void PlayLevelSound(int x, int y, int nr)
8831 int sx = SCREENX(x), sy = SCREENY(y);
8832 int volume, stereo_position;
8833 int max_distance = 8;
8834 int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
8836 if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
8837 (!setup.sound_loops && IS_LOOP_SOUND(nr)))
8840 if (!IN_LEV_FIELD(x, y) ||
8841 sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
8842 sy < -max_distance || sy >= SCR_FIELDY + max_distance)
8845 volume = SOUND_MAX_VOLUME;
8847 if (!IN_SCR_FIELD(sx, sy))
8849 int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
8850 int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
8852 volume -= volume * (dx > dy ? dx : dy) / max_distance;
8855 stereo_position = (SOUND_MAX_LEFT +
8856 (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
8857 (SCR_FIELDX + 2 * max_distance));
8859 if (IS_LOOP_SOUND(nr))
8861 /* This assures that quieter loop sounds do not overwrite louder ones,
8862 while restarting sound volume comparison with each new game frame. */
8864 if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
8867 loop_sound_volume[nr] = volume;
8868 loop_sound_frame[nr] = FrameCounter;
8871 PlaySoundExt(nr, volume, stereo_position, type);
8874 static void PlayLevelSoundNearest(int x, int y, int sound_action)
8876 PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
8877 x > LEVELX(BX2) ? LEVELX(BX2) : x,
8878 y < LEVELY(BY1) ? LEVELY(BY1) :
8879 y > LEVELY(BY2) ? LEVELY(BY2) : y,
8883 static void PlayLevelSoundAction(int x, int y, int action)
8885 PlayLevelSoundElementAction(x, y, Feld[x][y], action);
8888 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
8890 int sound_effect = element_info[element].sound[action];
8892 if (sound_effect != SND_UNDEFINED)
8893 PlayLevelSound(x, y, sound_effect);
8896 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
8899 int sound_effect = element_info[element].sound[action];
8901 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
8902 PlayLevelSound(x, y, sound_effect);
8905 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
8907 int sound_effect = element_info[Feld[x][y]].sound[action];
8909 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
8910 PlayLevelSound(x, y, sound_effect);
8913 static void StopLevelSoundActionIfLoop(int x, int y, int action)
8915 int sound_effect = element_info[Feld[x][y]].sound[action];
8917 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
8918 StopSoundExt(sound_effect, SND_CTRL_STOP_SOUND);
8921 static void PlayLevelMusic()
8923 if (levelset.music[level_nr] != MUS_UNDEFINED)
8924 PlayMusic(levelset.music[level_nr]); /* from config file */
8926 PlayMusic(MAP_NOCONF_MUSIC(level_nr)); /* from music dir */
8929 void RaiseScore(int value)
8931 local_player->score += value;
8932 DrawText(DX_SCORE, DY_SCORE, int2str(local_player->score, 5), FONT_TEXT_2);
8935 void RaiseScoreElement(int element)
8941 case EL_EMERALD_YELLOW:
8942 case EL_EMERALD_RED:
8943 case EL_EMERALD_PURPLE:
8944 case EL_SP_INFOTRON:
8945 RaiseScore(level.score[SC_EMERALD]);
8948 RaiseScore(level.score[SC_DIAMOND]);
8951 RaiseScore(level.score[SC_CRYSTAL]);
8954 RaiseScore(level.score[SC_PEARL]);
8957 case EL_BD_BUTTERFLY:
8958 case EL_SP_ELECTRON:
8959 RaiseScore(level.score[SC_BUG]);
8963 case EL_SP_SNIKSNAK:
8964 RaiseScore(level.score[SC_SPACESHIP]);
8967 case EL_DARK_YAMYAM:
8968 RaiseScore(level.score[SC_YAMYAM]);
8971 RaiseScore(level.score[SC_ROBOT]);
8974 RaiseScore(level.score[SC_PACMAN]);
8977 RaiseScore(level.score[SC_NUT]);
8980 case EL_SP_DISK_RED:
8981 case EL_DYNABOMB_INCREASE_NUMBER:
8982 case EL_DYNABOMB_INCREASE_SIZE:
8983 case EL_DYNABOMB_INCREASE_POWER:
8984 RaiseScore(level.score[SC_DYNAMITE]);
8986 case EL_SHIELD_NORMAL:
8987 case EL_SHIELD_DEADLY:
8988 RaiseScore(level.score[SC_SHIELD]);
8991 RaiseScore(level.score[SC_TIME_BONUS]);
8997 RaiseScore(level.score[SC_KEY]);
9000 RaiseScore(element_info[element].collect_score);
9005 void RequestQuitGame(boolean ask_if_really_quit)
9007 if (AllPlayersGone ||
9008 !ask_if_really_quit ||
9009 level_editor_test_game ||
9010 Request("Do you really want to quit the game ?",
9011 REQ_ASK | REQ_STAY_CLOSED))
9013 #if defined(PLATFORM_UNIX)
9014 if (options.network)
9015 SendToServer_StopPlaying();
9019 game_status = GAME_MODE_MAIN;
9025 OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
9030 /* ---------- new game button stuff ---------------------------------------- */
9032 /* graphic position values for game buttons */
9033 #define GAME_BUTTON_XSIZE 30
9034 #define GAME_BUTTON_YSIZE 30
9035 #define GAME_BUTTON_XPOS 5
9036 #define GAME_BUTTON_YPOS 215
9037 #define SOUND_BUTTON_XPOS 5
9038 #define SOUND_BUTTON_YPOS (GAME_BUTTON_YPOS + GAME_BUTTON_YSIZE)
9040 #define GAME_BUTTON_STOP_XPOS (GAME_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
9041 #define GAME_BUTTON_PAUSE_XPOS (GAME_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
9042 #define GAME_BUTTON_PLAY_XPOS (GAME_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
9043 #define SOUND_BUTTON_MUSIC_XPOS (SOUND_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
9044 #define SOUND_BUTTON_LOOPS_XPOS (SOUND_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
9045 #define SOUND_BUTTON_SIMPLE_XPOS (SOUND_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
9052 } gamebutton_info[NUM_GAME_BUTTONS] =
9055 GAME_BUTTON_STOP_XPOS, GAME_BUTTON_YPOS,
9060 GAME_BUTTON_PAUSE_XPOS, GAME_BUTTON_YPOS,
9065 GAME_BUTTON_PLAY_XPOS, GAME_BUTTON_YPOS,
9070 SOUND_BUTTON_MUSIC_XPOS, SOUND_BUTTON_YPOS,
9071 SOUND_CTRL_ID_MUSIC,
9072 "background music on/off"
9075 SOUND_BUTTON_LOOPS_XPOS, SOUND_BUTTON_YPOS,
9076 SOUND_CTRL_ID_LOOPS,
9077 "sound loops on/off"
9080 SOUND_BUTTON_SIMPLE_XPOS, SOUND_BUTTON_YPOS,
9081 SOUND_CTRL_ID_SIMPLE,
9082 "normal sounds on/off"
9086 void CreateGameButtons()
9090 for (i = 0; i < NUM_GAME_BUTTONS; i++)
9092 Bitmap *gd_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
9093 struct GadgetInfo *gi;
9096 unsigned long event_mask;
9097 int gd_xoffset, gd_yoffset;
9098 int gd_x1, gd_x2, gd_y1, gd_y2;
9101 gd_xoffset = gamebutton_info[i].x;
9102 gd_yoffset = gamebutton_info[i].y;
9103 gd_x1 = DOOR_GFX_PAGEX4 + gd_xoffset;
9104 gd_x2 = DOOR_GFX_PAGEX3 + gd_xoffset;
9106 if (id == GAME_CTRL_ID_STOP ||
9107 id == GAME_CTRL_ID_PAUSE ||
9108 id == GAME_CTRL_ID_PLAY)
9110 button_type = GD_TYPE_NORMAL_BUTTON;
9112 event_mask = GD_EVENT_RELEASED;
9113 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
9114 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
9118 button_type = GD_TYPE_CHECK_BUTTON;
9120 ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
9121 (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
9122 (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
9123 event_mask = GD_EVENT_PRESSED;
9124 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset;
9125 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
9128 gi = CreateGadget(GDI_CUSTOM_ID, id,
9129 GDI_INFO_TEXT, gamebutton_info[i].infotext,
9130 GDI_X, DX + gd_xoffset,
9131 GDI_Y, DY + gd_yoffset,
9132 GDI_WIDTH, GAME_BUTTON_XSIZE,
9133 GDI_HEIGHT, GAME_BUTTON_YSIZE,
9134 GDI_TYPE, button_type,
9135 GDI_STATE, GD_BUTTON_UNPRESSED,
9136 GDI_CHECKED, checked,
9137 GDI_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y1,
9138 GDI_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y1,
9139 GDI_ALT_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y2,
9140 GDI_ALT_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y2,
9141 GDI_EVENT_MASK, event_mask,
9142 GDI_CALLBACK_ACTION, HandleGameButtons,
9146 Error(ERR_EXIT, "cannot create gadget");
9148 game_gadget[id] = gi;
9152 void FreeGameButtons()
9156 for (i = 0; i < NUM_GAME_BUTTONS; i++)
9157 FreeGadget(game_gadget[i]);
9160 static void MapGameButtons()
9164 for (i = 0; i < NUM_GAME_BUTTONS; i++)
9165 MapGadget(game_gadget[i]);
9168 void UnmapGameButtons()
9172 for (i = 0; i < NUM_GAME_BUTTONS; i++)
9173 UnmapGadget(game_gadget[i]);
9176 static void HandleGameButtons(struct GadgetInfo *gi)
9178 int id = gi->custom_id;
9180 if (game_status != GAME_MODE_PLAYING)
9185 case GAME_CTRL_ID_STOP:
9186 RequestQuitGame(TRUE);
9189 case GAME_CTRL_ID_PAUSE:
9190 if (options.network)
9192 #if defined(PLATFORM_UNIX)
9194 SendToServer_ContinuePlaying();
9196 SendToServer_PausePlaying();
9200 TapeTogglePause(TAPE_TOGGLE_MANUAL);
9203 case GAME_CTRL_ID_PLAY:
9206 #if defined(PLATFORM_UNIX)
9207 if (options.network)
9208 SendToServer_ContinuePlaying();
9212 tape.pausing = FALSE;
9213 DrawVideoDisplay(VIDEO_STATE_PAUSE_OFF,0);
9218 case SOUND_CTRL_ID_MUSIC:
9219 if (setup.sound_music)
9221 setup.sound_music = FALSE;
9224 else if (audio.music_available)
9226 setup.sound = setup.sound_music = TRUE;
9228 SetAudioMode(setup.sound);
9234 case SOUND_CTRL_ID_LOOPS:
9235 if (setup.sound_loops)
9236 setup.sound_loops = FALSE;
9237 else if (audio.loops_available)
9239 setup.sound = setup.sound_loops = TRUE;
9240 SetAudioMode(setup.sound);
9244 case SOUND_CTRL_ID_SIMPLE:
9245 if (setup.sound_simple)
9246 setup.sound_simple = FALSE;
9247 else if (audio.sound_available)
9249 setup.sound = setup.sound_simple = TRUE;
9250 SetAudioMode(setup.sound);