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->action_waiting = ACTION_DEFAULT;
1079 player->last_action_waiting = ACTION_DEFAULT;
1080 player->special_action_bored = ACTION_DEFAULT;
1081 player->special_action_sleeping = ACTION_DEFAULT;
1083 player->num_special_action_bored = 0;
1084 player->num_special_action_sleeping = 0;
1086 /* determine number of special actions for bored and sleeping animation */
1087 for (j = ACTION_BORING_1; j <= ACTION_BORING_LAST; j++)
1089 boolean found = FALSE;
1091 for (k = 0; k < NUM_DIRECTIONS; k++)
1092 if (el_act_dir2img(player->element_nr, j, k) !=
1093 el_act_dir2img(player->element_nr, ACTION_DEFAULT, k))
1097 player->num_special_action_bored++;
1101 for (j = ACTION_SLEEPING_1; j <= ACTION_SLEEPING_LAST; j++)
1103 boolean found = FALSE;
1105 for (k = 0; k < NUM_DIRECTIONS; k++)
1106 if (el_act_dir2img(player->element_nr, j, k) !=
1107 el_act_dir2img(player->element_nr, ACTION_DEFAULT, k))
1111 player->num_special_action_sleeping++;
1116 player->switch_x = -1;
1117 player->switch_y = -1;
1119 player->show_envelope = 0;
1121 player->move_delay = game.initial_move_delay;
1122 player->move_delay_value = game.initial_move_delay_value;
1124 player->push_delay = 0;
1125 player->push_delay_value = game.initial_push_delay_value;
1127 player->last_jx = player->last_jy = 0;
1128 player->jx = player->jy = 0;
1130 player->shield_normal_time_left = 0;
1131 player->shield_deadly_time_left = 0;
1133 player->inventory_size = 0;
1135 DigField(player, 0, 0, 0, 0, DF_NO_PUSH);
1136 SnapField(player, 0, 0);
1138 player->LevelSolved = FALSE;
1139 player->GameOver = FALSE;
1142 network_player_action_received = FALSE;
1144 #if defined(PLATFORM_UNIX)
1145 /* initial null action */
1146 if (network_playing)
1147 SendToServer_MovePlayer(MV_NO_MOVING);
1155 TimeLeft = level.time;
1157 ScreenMovDir = MV_NO_MOVING;
1161 ScrollStepSize = 0; /* will be correctly initialized by ScrollScreen() */
1163 AllPlayersGone = FALSE;
1165 game.yamyam_content_nr = 0;
1166 game.magic_wall_active = FALSE;
1167 game.magic_wall_time_left = 0;
1168 game.light_time_left = 0;
1169 game.timegate_time_left = 0;
1170 game.switchgate_pos = 0;
1171 game.balloon_dir = MV_NO_MOVING;
1172 game.gravity = level.initial_gravity;
1173 game.explosions_delayed = TRUE;
1175 game.envelope_active = FALSE;
1177 for (i = 0; i < 4; i++)
1179 game.belt_dir[i] = MV_NO_MOVING;
1180 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
1183 for (i = 0; i < MAX_NUM_AMOEBA; i++)
1184 AmoebaCnt[i] = AmoebaCnt2[i] = 0;
1186 for (x = 0; x < lev_fieldx; x++)
1188 for (y = 0; y < lev_fieldy; y++)
1190 Feld[x][y] = level.field[x][y];
1191 MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
1192 ChangeDelay[x][y] = 0;
1193 ChangePage[x][y] = -1;
1194 Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
1196 WasJustMoving[x][y] = 0;
1197 WasJustFalling[x][y] = 0;
1199 Pushed[x][y] = FALSE;
1201 Changed[x][y] = CE_BITMASK_DEFAULT;
1202 ChangeEvent[x][y] = CE_BITMASK_DEFAULT;
1204 ExplodePhase[x][y] = 0;
1205 ExplodeField[x][y] = EX_NO_EXPLOSION;
1207 RunnerVisit[x][y] = 0;
1208 PlayerVisit[x][y] = 0;
1211 GfxRandom[x][y] = INIT_GFX_RANDOM();
1212 GfxElement[x][y] = EL_UNDEFINED;
1213 GfxAction[x][y] = ACTION_DEFAULT;
1214 GfxDir[x][y] = MV_NO_MOVING;
1218 for (y = 0; y < lev_fieldy; y++)
1220 for (x = 0; x < lev_fieldx; x++)
1222 if (emulate_bd && !IS_BD_ELEMENT(Feld[x][y]))
1224 if (emulate_sb && !IS_SB_ELEMENT(Feld[x][y]))
1226 if (emulate_sp && !IS_SP_ELEMENT(Feld[x][y]))
1229 InitField(x, y, TRUE);
1235 game.emulation = (emulate_bd ? EMU_BOULDERDASH :
1236 emulate_sb ? EMU_SOKOBAN :
1237 emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
1239 /* correct non-moving belts to start moving left */
1240 for (i = 0; i < 4; i++)
1241 if (game.belt_dir[i] == MV_NO_MOVING)
1242 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
1244 /* check if any connected player was not found in playfield */
1245 for (i = 0; i < MAX_PLAYERS; i++)
1247 struct PlayerInfo *player = &stored_player[i];
1249 if (player->connected && !player->present)
1251 for (j = 0; j < MAX_PLAYERS; j++)
1253 struct PlayerInfo *some_player = &stored_player[j];
1254 int jx = some_player->jx, jy = some_player->jy;
1256 /* assign first free player found that is present in the playfield */
1257 if (some_player->present && !some_player->connected)
1259 player->present = TRUE;
1260 player->active = TRUE;
1261 some_player->present = FALSE;
1263 StorePlayer[jx][jy] = player->element_nr;
1264 player->jx = player->last_jx = jx;
1265 player->jy = player->last_jy = jy;
1275 /* when playing a tape, eliminate all players who do not participate */
1277 for (i = 0; i < MAX_PLAYERS; i++)
1279 if (stored_player[i].active && !tape.player_participates[i])
1281 struct PlayerInfo *player = &stored_player[i];
1282 int jx = player->jx, jy = player->jy;
1284 player->active = FALSE;
1285 StorePlayer[jx][jy] = 0;
1286 Feld[jx][jy] = EL_EMPTY;
1290 else if (!options.network && !setup.team_mode) /* && !tape.playing */
1292 /* when in single player mode, eliminate all but the first active player */
1294 for (i = 0; i < MAX_PLAYERS; i++)
1296 if (stored_player[i].active)
1298 for (j = i + 1; j < MAX_PLAYERS; j++)
1300 if (stored_player[j].active)
1302 struct PlayerInfo *player = &stored_player[j];
1303 int jx = player->jx, jy = player->jy;
1305 player->active = FALSE;
1306 StorePlayer[jx][jy] = 0;
1307 Feld[jx][jy] = EL_EMPTY;
1314 /* when recording the game, store which players take part in the game */
1317 for (i = 0; i < MAX_PLAYERS; i++)
1318 if (stored_player[i].active)
1319 tape.player_participates[i] = TRUE;
1324 for (i = 0; i < MAX_PLAYERS; i++)
1326 struct PlayerInfo *player = &stored_player[i];
1328 printf("Player %d: present == %d, connected == %d, active == %d.\n",
1333 if (local_player == player)
1334 printf("Player %d is local player.\n", i+1);
1338 if (BorderElement == EL_EMPTY)
1341 SBX_Right = lev_fieldx - SCR_FIELDX;
1343 SBY_Lower = lev_fieldy - SCR_FIELDY;
1348 SBX_Right = lev_fieldx - SCR_FIELDX + 1;
1350 SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
1353 if (lev_fieldx + (SBX_Left == -1 ? 2 : 0) <= SCR_FIELDX)
1354 SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
1356 if (lev_fieldy + (SBY_Upper == -1 ? 2 : 0) <= SCR_FIELDY)
1357 SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
1359 /* if local player not found, look for custom element that might create
1360 the player (make some assumptions about the right custom element) */
1361 if (!local_player->present)
1363 int start_x = 0, start_y = 0;
1364 int found_rating = 0;
1365 int found_element = EL_UNDEFINED;
1367 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
1369 int element = Feld[x][y];
1374 if (!IS_CUSTOM_ELEMENT(element))
1377 if (CAN_CHANGE(element))
1379 for (i = 0; i < element_info[element].num_change_pages; i++)
1381 content = element_info[element].change_page[i].target_element;
1382 is_player = ELEM_IS_PLAYER(content);
1384 if (is_player && (found_rating < 3 || element < found_element))
1390 found_element = element;
1395 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
1397 content = element_info[element].content[xx][yy];
1398 is_player = ELEM_IS_PLAYER(content);
1400 if (is_player && (found_rating < 2 || element < found_element))
1402 start_x = x + xx - 1;
1403 start_y = y + yy - 1;
1406 found_element = element;
1409 if (!CAN_CHANGE(element))
1412 for (i = 0; i < element_info[element].num_change_pages; i++)
1414 content = element_info[element].change_page[i].content[xx][yy];
1415 is_player = ELEM_IS_PLAYER(content);
1417 if (is_player && (found_rating < 1 || element < found_element))
1419 start_x = x + xx - 1;
1420 start_y = y + yy - 1;
1423 found_element = element;
1429 scroll_x = (start_x < SBX_Left + MIDPOSX ? SBX_Left :
1430 start_x > SBX_Right + MIDPOSX ? SBX_Right :
1433 scroll_y = (start_y < SBY_Upper + MIDPOSY ? SBY_Upper :
1434 start_y > SBY_Lower + MIDPOSY ? SBY_Lower :
1440 scroll_x = (local_player->jx < SBX_Left + MIDPOSX ? SBX_Left :
1441 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
1442 local_player->jx - MIDPOSX);
1444 scroll_y = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
1445 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
1446 local_player->jy - MIDPOSY);
1448 scroll_x = SBX_Left;
1449 scroll_y = SBY_Upper;
1450 if (local_player->jx >= SBX_Left + MIDPOSX)
1451 scroll_x = (local_player->jx <= SBX_Right + MIDPOSX ?
1452 local_player->jx - MIDPOSX :
1454 if (local_player->jy >= SBY_Upper + MIDPOSY)
1455 scroll_y = (local_player->jy <= SBY_Lower + MIDPOSY ?
1456 local_player->jy - MIDPOSY :
1461 CloseDoor(DOOR_CLOSE_1);
1466 /* after drawing the level, correct some elements */
1467 if (game.timegate_time_left == 0)
1468 CloseAllOpenTimegates();
1470 if (setup.soft_scrolling)
1471 BlitBitmap(fieldbuffer, backbuffer, FX, FY, SXSIZE, SYSIZE, SX, SY);
1473 redraw_mask |= REDRAW_FROM_BACKBUFFER;
1476 /* copy default game door content to main double buffer */
1477 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
1478 DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
1481 DrawText(DX + XX_LEVEL, DY + YY_LEVEL, int2str(level_nr, 2), FONT_TEXT_2);
1484 DrawTextExt(drawto, DX + XX_EMERALDS, DY + YY_EMERALDS,
1485 int2str(level_nr, 3), FONT_LEVEL_NUMBER, BLIT_OPAQUE);
1486 BlitBitmap(drawto, drawto,
1487 DX + XX_EMERALDS, DY + YY_EMERALDS + 1,
1488 getFontWidth(FONT_LEVEL_NUMBER) * 3,
1489 getFontHeight(FONT_LEVEL_NUMBER) - 1,
1490 DX + XX_LEVEL - 1, DY + YY_LEVEL + 1);
1493 DrawGameDoorValues();
1497 game_gadget[SOUND_CTRL_ID_MUSIC]->checked = setup.sound_music;
1498 game_gadget[SOUND_CTRL_ID_LOOPS]->checked = setup.sound_loops;
1499 game_gadget[SOUND_CTRL_ID_SIMPLE]->checked = setup.sound_simple;
1503 /* copy actual game door content to door double buffer for OpenDoor() */
1504 BlitBitmap(drawto, bitmap_db_door,
1505 DX, DY, DXSIZE, DYSIZE, DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1);
1507 OpenDoor(DOOR_OPEN_ALL);
1509 PlaySoundStereo(SND_GAME_STARTING, SOUND_MIDDLE);
1511 if (setup.sound_music)
1514 KeyboardAutoRepeatOffUnlessAutoplay();
1518 for (i = 0; i < 4; i++)
1519 printf("Player %d %sactive.\n",
1520 i + 1, (stored_player[i].active ? "" : "not "));
1524 printf("::: starting game [%d]\n", FrameCounter);
1528 void InitMovDir(int x, int y)
1530 int i, element = Feld[x][y];
1531 static int xy[4][2] =
1538 static int direction[3][4] =
1540 { MV_RIGHT, MV_UP, MV_LEFT, MV_DOWN },
1541 { MV_LEFT, MV_DOWN, MV_RIGHT, MV_UP },
1542 { MV_LEFT, MV_RIGHT, MV_UP, MV_DOWN }
1551 Feld[x][y] = EL_BUG;
1552 MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
1555 case EL_SPACESHIP_RIGHT:
1556 case EL_SPACESHIP_UP:
1557 case EL_SPACESHIP_LEFT:
1558 case EL_SPACESHIP_DOWN:
1559 Feld[x][y] = EL_SPACESHIP;
1560 MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
1563 case EL_BD_BUTTERFLY_RIGHT:
1564 case EL_BD_BUTTERFLY_UP:
1565 case EL_BD_BUTTERFLY_LEFT:
1566 case EL_BD_BUTTERFLY_DOWN:
1567 Feld[x][y] = EL_BD_BUTTERFLY;
1568 MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
1571 case EL_BD_FIREFLY_RIGHT:
1572 case EL_BD_FIREFLY_UP:
1573 case EL_BD_FIREFLY_LEFT:
1574 case EL_BD_FIREFLY_DOWN:
1575 Feld[x][y] = EL_BD_FIREFLY;
1576 MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
1579 case EL_PACMAN_RIGHT:
1581 case EL_PACMAN_LEFT:
1582 case EL_PACMAN_DOWN:
1583 Feld[x][y] = EL_PACMAN;
1584 MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
1587 case EL_SP_SNIKSNAK:
1588 MovDir[x][y] = MV_UP;
1591 case EL_SP_ELECTRON:
1592 MovDir[x][y] = MV_LEFT;
1599 Feld[x][y] = EL_MOLE;
1600 MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
1604 if (IS_CUSTOM_ELEMENT(element))
1606 if (element_info[element].move_direction_initial != MV_NO_MOVING)
1607 MovDir[x][y] = element_info[element].move_direction_initial;
1608 else if (element_info[element].move_pattern == MV_ALL_DIRECTIONS ||
1609 element_info[element].move_pattern == MV_TURNING_LEFT ||
1610 element_info[element].move_pattern == MV_TURNING_RIGHT)
1611 MovDir[x][y] = 1 << RND(4);
1612 else if (element_info[element].move_pattern == MV_HORIZONTAL)
1613 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
1614 else if (element_info[element].move_pattern == MV_VERTICAL)
1615 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
1616 else if (element_info[element].move_pattern & MV_ANY_DIRECTION)
1617 MovDir[x][y] = element_info[element].move_pattern;
1618 else if (element_info[element].move_pattern == MV_ALONG_LEFT_SIDE ||
1619 element_info[element].move_pattern == MV_ALONG_RIGHT_SIDE)
1621 for (i = 0; i < 4; i++)
1623 int x1 = x + xy[i][0];
1624 int y1 = y + xy[i][1];
1626 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
1628 if (element_info[element].move_pattern == MV_ALONG_RIGHT_SIDE)
1629 MovDir[x][y] = direction[0][i];
1631 MovDir[x][y] = direction[1][i];
1640 MovDir[x][y] = 1 << RND(4);
1642 if (element != EL_BUG &&
1643 element != EL_SPACESHIP &&
1644 element != EL_BD_BUTTERFLY &&
1645 element != EL_BD_FIREFLY)
1648 for (i = 0; i < 4; i++)
1650 int x1 = x + xy[i][0];
1651 int y1 = y + xy[i][1];
1653 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
1655 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
1657 MovDir[x][y] = direction[0][i];
1660 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
1661 element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
1663 MovDir[x][y] = direction[1][i];
1672 GfxDir[x][y] = MovDir[x][y];
1675 void InitAmoebaNr(int x, int y)
1678 int group_nr = AmoebeNachbarNr(x, y);
1682 for (i = 1; i < MAX_NUM_AMOEBA; i++)
1684 if (AmoebaCnt[i] == 0)
1692 AmoebaNr[x][y] = group_nr;
1693 AmoebaCnt[group_nr]++;
1694 AmoebaCnt2[group_nr]++;
1700 boolean raise_level = FALSE;
1702 if (local_player->MovPos)
1706 if (tape.auto_play) /* tape might already be stopped here */
1707 tape.auto_play_level_solved = TRUE;
1709 if (tape.playing && tape.auto_play)
1710 tape.auto_play_level_solved = TRUE;
1713 local_player->LevelSolved = FALSE;
1715 PlaySoundStereo(SND_GAME_WINNING, SOUND_MIDDLE);
1719 if (!tape.playing && setup.sound_loops)
1720 PlaySoundExt(SND_GAME_LEVELTIME_BONUS, SOUND_MAX_VOLUME, SOUND_MIDDLE,
1721 SND_CTRL_PLAY_LOOP);
1723 while (TimeLeft > 0)
1725 if (!tape.playing && !setup.sound_loops)
1726 PlaySoundStereo(SND_GAME_LEVELTIME_BONUS, SOUND_MIDDLE);
1727 if (TimeLeft > 0 && !(TimeLeft % 10))
1728 RaiseScore(level.score[SC_TIME_BONUS]);
1729 if (TimeLeft > 100 && !(TimeLeft % 10))
1733 DrawText(DX_TIME, DY_TIME, int2str(TimeLeft, 3), FONT_TEXT_2);
1740 if (!tape.playing && setup.sound_loops)
1741 StopSound(SND_GAME_LEVELTIME_BONUS);
1743 else if (level.time == 0) /* level without time limit */
1745 if (!tape.playing && setup.sound_loops)
1746 PlaySoundExt(SND_GAME_LEVELTIME_BONUS, SOUND_MAX_VOLUME, SOUND_MIDDLE,
1747 SND_CTRL_PLAY_LOOP);
1749 while (TimePlayed < 999)
1751 if (!tape.playing && !setup.sound_loops)
1752 PlaySoundStereo(SND_GAME_LEVELTIME_BONUS, SOUND_MIDDLE);
1753 if (TimePlayed < 999 && !(TimePlayed % 10))
1754 RaiseScore(level.score[SC_TIME_BONUS]);
1755 if (TimePlayed < 900 && !(TimePlayed % 10))
1759 DrawText(DX_TIME, DY_TIME, int2str(TimePlayed, 3), FONT_TEXT_2);
1766 if (!tape.playing && setup.sound_loops)
1767 StopSound(SND_GAME_LEVELTIME_BONUS);
1770 /* close exit door after last player */
1771 if ((Feld[ExitX][ExitY] == EL_EXIT_OPEN ||
1772 Feld[ExitX][ExitY] == EL_SP_EXIT_OPEN) && AllPlayersGone)
1774 int element = Feld[ExitX][ExitY];
1776 Feld[ExitX][ExitY] = (element == EL_EXIT_OPEN ? EL_EXIT_CLOSING :
1777 EL_SP_EXIT_CLOSING);
1779 PlayLevelSoundElementAction(ExitX, ExitY, element, ACTION_CLOSING);
1782 /* Hero disappears */
1783 DrawLevelField(ExitX, ExitY);
1789 CloseDoor(DOOR_CLOSE_1);
1794 SaveTape(tape.level_nr); /* Ask to save tape */
1797 if (level_nr == leveldir_current->handicap_level)
1799 leveldir_current->handicap_level++;
1800 SaveLevelSetup_SeriesInfo();
1803 if (level_editor_test_game)
1804 local_player->score = -1; /* no highscore when playing from editor */
1805 else if (level_nr < leveldir_current->last_level)
1806 raise_level = TRUE; /* advance to next level */
1808 if ((hi_pos = NewHiScore()) >= 0)
1810 game_status = GAME_MODE_SCORES;
1811 DrawHallOfFame(hi_pos);
1820 game_status = GAME_MODE_MAIN;
1837 LoadScore(level_nr);
1839 if (strcmp(setup.player_name, EMPTY_PLAYER_NAME) == 0 ||
1840 local_player->score < highscore[MAX_SCORE_ENTRIES - 1].Score)
1843 for (k = 0; k < MAX_SCORE_ENTRIES; k++)
1845 if (local_player->score > highscore[k].Score)
1847 /* player has made it to the hall of fame */
1849 if (k < MAX_SCORE_ENTRIES - 1)
1851 int m = MAX_SCORE_ENTRIES - 1;
1854 for (l = k; l < MAX_SCORE_ENTRIES; l++)
1855 if (!strcmp(setup.player_name, highscore[l].Name))
1857 if (m == k) /* player's new highscore overwrites his old one */
1861 for (l = m; l > k; l--)
1863 strcpy(highscore[l].Name, highscore[l - 1].Name);
1864 highscore[l].Score = highscore[l - 1].Score;
1871 strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
1872 highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
1873 highscore[k].Score = local_player->score;
1879 else if (!strncmp(setup.player_name, highscore[k].Name,
1880 MAX_PLAYER_NAME_LEN))
1881 break; /* player already there with a higher score */
1887 SaveScore(level_nr);
1892 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
1894 if (player->GfxAction != action || player->GfxDir != dir)
1897 printf("Player frame reset! (%d => %d, %d => %d)\n",
1898 player->GfxAction, action, player->GfxDir, dir);
1901 player->GfxAction = action;
1902 player->GfxDir = dir;
1904 player->StepFrame = 0;
1908 static void ResetRandomAnimationValue(int x, int y)
1910 GfxRandom[x][y] = INIT_GFX_RANDOM();
1913 static void ResetGfxAnimation(int x, int y)
1916 GfxAction[x][y] = ACTION_DEFAULT;
1917 GfxDir[x][y] = MovDir[x][y];
1920 void InitMovingField(int x, int y, int direction)
1922 int element = Feld[x][y];
1923 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
1924 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
1928 if (!WasJustMoving[x][y] || direction != MovDir[x][y])
1929 ResetGfxAnimation(x, y);
1931 MovDir[newx][newy] = MovDir[x][y] = direction;
1932 GfxDir[x][y] = direction;
1934 if (Feld[newx][newy] == EL_EMPTY)
1935 Feld[newx][newy] = EL_BLOCKED;
1937 if (direction == MV_DOWN && CAN_FALL(element))
1938 GfxAction[x][y] = ACTION_FALLING;
1940 GfxAction[x][y] = ACTION_MOVING;
1942 GfxFrame[newx][newy] = GfxFrame[x][y];
1943 GfxRandom[newx][newy] = GfxRandom[x][y];
1944 GfxAction[newx][newy] = GfxAction[x][y];
1945 GfxDir[newx][newy] = GfxDir[x][y];
1948 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
1950 int direction = MovDir[x][y];
1951 int newx = x + (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
1952 int newy = y + (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
1958 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
1960 int oldx = x, oldy = y;
1961 int direction = MovDir[x][y];
1963 if (direction == MV_LEFT)
1965 else if (direction == MV_RIGHT)
1967 else if (direction == MV_UP)
1969 else if (direction == MV_DOWN)
1972 *comes_from_x = oldx;
1973 *comes_from_y = oldy;
1976 int MovingOrBlocked2Element(int x, int y)
1978 int element = Feld[x][y];
1980 if (element == EL_BLOCKED)
1984 Blocked2Moving(x, y, &oldx, &oldy);
1985 return Feld[oldx][oldy];
1991 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
1993 /* like MovingOrBlocked2Element(), but if element is moving
1994 and (x,y) is the field the moving element is just leaving,
1995 return EL_BLOCKED instead of the element value */
1996 int element = Feld[x][y];
1998 if (IS_MOVING(x, y))
2000 if (element == EL_BLOCKED)
2004 Blocked2Moving(x, y, &oldx, &oldy);
2005 return Feld[oldx][oldy];
2014 static void RemoveField(int x, int y)
2016 Feld[x][y] = EL_EMPTY;
2023 ChangeDelay[x][y] = 0;
2024 ChangePage[x][y] = -1;
2025 Pushed[x][y] = FALSE;
2027 GfxElement[x][y] = EL_UNDEFINED;
2028 GfxAction[x][y] = ACTION_DEFAULT;
2029 GfxDir[x][y] = MV_NO_MOVING;
2032 void RemoveMovingField(int x, int y)
2034 int oldx = x, oldy = y, newx = x, newy = y;
2035 int element = Feld[x][y];
2036 int next_element = EL_UNDEFINED;
2038 if (element != EL_BLOCKED && !IS_MOVING(x, y))
2041 if (IS_MOVING(x, y))
2043 Moving2Blocked(x, y, &newx, &newy);
2044 if (Feld[newx][newy] != EL_BLOCKED)
2047 else if (element == EL_BLOCKED)
2049 Blocked2Moving(x, y, &oldx, &oldy);
2050 if (!IS_MOVING(oldx, oldy))
2054 if (element == EL_BLOCKED &&
2055 (Feld[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
2056 Feld[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
2057 Feld[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
2058 Feld[oldx][oldy] == EL_AMOEBA_DROPPING))
2059 next_element = get_next_element(Feld[oldx][oldy]);
2061 RemoveField(oldx, oldy);
2062 RemoveField(newx, newy);
2064 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
2066 if (next_element != EL_UNDEFINED)
2067 Feld[oldx][oldy] = next_element;
2069 DrawLevelField(oldx, oldy);
2070 DrawLevelField(newx, newy);
2073 void DrawDynamite(int x, int y)
2075 int sx = SCREENX(x), sy = SCREENY(y);
2076 int graphic = el2img(Feld[x][y]);
2079 if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
2082 if (IS_WALKABLE_INSIDE(Back[x][y]))
2086 DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
2087 else if (Store[x][y])
2088 DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
2090 frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
2093 if (Back[x][y] || Store[x][y])
2094 DrawGraphicThruMask(sx, sy, graphic, frame);
2096 DrawGraphic(sx, sy, graphic, frame);
2098 if (game.emulation == EMU_SUPAPLEX)
2099 DrawGraphic(sx, sy, IMG_SP_DISK_RED, frame);
2100 else if (Store[x][y])
2101 DrawGraphicThruMask(sx, sy, graphic, frame);
2103 DrawGraphic(sx, sy, graphic, frame);
2107 void CheckDynamite(int x, int y)
2109 if (MovDelay[x][y] != 0) /* dynamite is still waiting to explode */
2113 if (MovDelay[x][y] != 0)
2116 PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
2123 StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
2125 if (Feld[x][y] == EL_DYNAMITE_ACTIVE ||
2126 Feld[x][y] == EL_SP_DISK_RED_ACTIVE)
2127 StopSound(SND_DYNAMITE_ACTIVE);
2129 StopSound(SND_DYNABOMB_ACTIVE);
2135 void RelocatePlayer(int x, int y, int element)
2137 struct PlayerInfo *player = &stored_player[element - EL_PLAYER_1];
2140 RemoveField(x, y); /* temporarily remove newly placed player */
2141 DrawLevelField(x, y);
2144 if (player->present)
2146 while (player->MovPos)
2148 ScrollPlayer(player, SCROLL_GO_ON);
2149 ScrollScreen(NULL, SCROLL_GO_ON);
2155 Delay(GAME_FRAME_DELAY);
2158 DrawPlayer(player); /* needed here only to cleanup last field */
2159 DrawLevelField(player->jx, player->jy); /* remove player graphic */
2161 player->is_moving = FALSE;
2164 Feld[x][y] = element;
2165 InitPlayerField(x, y, element, TRUE);
2167 if (player == local_player)
2169 int scroll_xx = -999, scroll_yy = -999;
2171 while (scroll_xx != scroll_x || scroll_yy != scroll_y)
2174 int fx = FX, fy = FY;
2176 scroll_xx = (local_player->jx < SBX_Left + MIDPOSX ? SBX_Left :
2177 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
2178 local_player->jx - MIDPOSX);
2180 scroll_yy = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
2181 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
2182 local_player->jy - MIDPOSY);
2184 dx = (scroll_xx < scroll_x ? +1 : scroll_xx > scroll_x ? -1 : 0);
2185 dy = (scroll_yy < scroll_y ? +1 : scroll_yy > scroll_y ? -1 : 0);
2190 fx += dx * TILEX / 2;
2191 fy += dy * TILEY / 2;
2193 ScrollLevel(dx, dy);
2196 /* scroll in two steps of half tile size to make things smoother */
2197 BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
2199 Delay(GAME_FRAME_DELAY);
2201 /* scroll second step to align at full tile size */
2203 Delay(GAME_FRAME_DELAY);
2208 void Explode(int ex, int ey, int phase, int mode)
2212 int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
2213 int last_phase = num_phase * delay;
2214 int half_phase = (num_phase / 2) * delay;
2215 int first_phase_after_start = EX_PHASE_START + 1;
2217 if (game.explosions_delayed)
2219 ExplodeField[ex][ey] = mode;
2223 if (phase == EX_PHASE_START) /* initialize 'Store[][]' field */
2225 int center_element = Feld[ex][ey];
2228 /* --- This is only really needed (and now handled) in "Impact()". --- */
2229 /* do not explode moving elements that left the explode field in time */
2230 if (game.engine_version >= VERSION_IDENT(2,2,0,7) &&
2231 center_element == EL_EMPTY && (mode == EX_NORMAL || mode == EX_CENTER))
2235 if (mode == EX_NORMAL || mode == EX_CENTER)
2236 PlayLevelSoundAction(ex, ey, ACTION_EXPLODING);
2238 /* remove things displayed in background while burning dynamite */
2239 if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
2242 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
2244 /* put moving element to center field (and let it explode there) */
2245 center_element = MovingOrBlocked2Element(ex, ey);
2246 RemoveMovingField(ex, ey);
2247 Feld[ex][ey] = center_element;
2250 for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
2252 int xx = x - ex + 1;
2253 int yy = y - ey + 1;
2256 if (!IN_LEV_FIELD(x, y) ||
2257 ((mode != EX_NORMAL || center_element == EL_AMOEBA_TO_DIAMOND) &&
2258 (x != ex || y != ey)))
2261 element = Feld[x][y];
2263 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
2265 element = MovingOrBlocked2Element(x, y);
2267 if (!IS_EXPLOSION_PROOF(element))
2268 RemoveMovingField(x, y);
2274 if (IS_EXPLOSION_PROOF(element))
2277 /* indestructible elements can only explode in center (but not flames) */
2278 if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey)) ||
2279 element == EL_FLAMES)
2284 if ((IS_INDESTRUCTIBLE(element) &&
2285 (game.engine_version < VERSION_IDENT(2,2,0,0) ||
2286 (!IS_WALKABLE_OVER(element) && !IS_WALKABLE_UNDER(element)))) ||
2287 element == EL_FLAMES)
2291 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
2293 if (IS_ACTIVE_BOMB(element))
2295 /* re-activate things under the bomb like gate or penguin */
2296 Feld[x][y] = (Store[x][y] ? Store[x][y] : EL_EMPTY);
2303 /* save walkable background elements while explosion on same tile */
2305 if (IS_INDESTRUCTIBLE(element))
2306 Back[x][y] = element;
2308 if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element))
2309 Back[x][y] = element;
2312 /* ignite explodable elements reached by other explosion */
2313 if (element == EL_EXPLOSION)
2314 element = Store2[x][y];
2317 if (AmoebaNr[x][y] &&
2318 (element == EL_AMOEBA_FULL ||
2319 element == EL_BD_AMOEBA ||
2320 element == EL_AMOEBA_GROWING))
2322 AmoebaCnt[AmoebaNr[x][y]]--;
2323 AmoebaCnt2[AmoebaNr[x][y]]--;
2329 if (IS_PLAYER(ex, ey) && !PLAYER_PROTECTED(ex, ey))
2331 switch(StorePlayer[ex][ey])
2334 Store[x][y] = EL_EMERALD_RED;
2337 Store[x][y] = EL_EMERALD;
2340 Store[x][y] = EL_EMERALD_PURPLE;
2344 Store[x][y] = EL_EMERALD_YELLOW;
2348 if (game.emulation == EMU_SUPAPLEX)
2349 Store[x][y] = EL_EMPTY;
2351 else if (center_element == EL_MOLE)
2352 Store[x][y] = EL_EMERALD_RED;
2353 else if (center_element == EL_PENGUIN)
2354 Store[x][y] = EL_EMERALD_PURPLE;
2355 else if (center_element == EL_BUG)
2356 Store[x][y] = ((x == ex && y == ey) ? EL_DIAMOND : EL_EMERALD);
2357 else if (center_element == EL_BD_BUTTERFLY)
2358 Store[x][y] = EL_BD_DIAMOND;
2359 else if (center_element == EL_SP_ELECTRON)
2360 Store[x][y] = EL_SP_INFOTRON;
2361 else if (center_element == EL_AMOEBA_TO_DIAMOND)
2362 Store[x][y] = level.amoeba_content;
2363 else if (center_element == EL_YAMYAM)
2364 Store[x][y] = level.yamyam_content[game.yamyam_content_nr][xx][yy];
2365 else if (IS_CUSTOM_ELEMENT(center_element) &&
2366 element_info[center_element].content[xx][yy] != EL_EMPTY)
2367 Store[x][y] = element_info[center_element].content[xx][yy];
2368 else if (element == EL_WALL_EMERALD)
2369 Store[x][y] = EL_EMERALD;
2370 else if (element == EL_WALL_DIAMOND)
2371 Store[x][y] = EL_DIAMOND;
2372 else if (element == EL_WALL_BD_DIAMOND)
2373 Store[x][y] = EL_BD_DIAMOND;
2374 else if (element == EL_WALL_EMERALD_YELLOW)
2375 Store[x][y] = EL_EMERALD_YELLOW;
2376 else if (element == EL_WALL_EMERALD_RED)
2377 Store[x][y] = EL_EMERALD_RED;
2378 else if (element == EL_WALL_EMERALD_PURPLE)
2379 Store[x][y] = EL_EMERALD_PURPLE;
2380 else if (element == EL_WALL_PEARL)
2381 Store[x][y] = EL_PEARL;
2382 else if (element == EL_WALL_CRYSTAL)
2383 Store[x][y] = EL_CRYSTAL;
2384 else if (IS_CUSTOM_ELEMENT(element) && !CAN_EXPLODE(element))
2385 Store[x][y] = element_info[element].content[1][1];
2387 Store[x][y] = EL_EMPTY;
2389 if (x != ex || y != ey ||
2390 center_element == EL_AMOEBA_TO_DIAMOND || mode == EX_BORDER)
2391 Store2[x][y] = element;
2394 if (AmoebaNr[x][y] &&
2395 (element == EL_AMOEBA_FULL ||
2396 element == EL_BD_AMOEBA ||
2397 element == EL_AMOEBA_GROWING))
2399 AmoebaCnt[AmoebaNr[x][y]]--;
2400 AmoebaCnt2[AmoebaNr[x][y]]--;
2406 MovDir[x][y] = MovPos[x][y] = 0;
2407 GfxDir[x][y] = MovDir[x][y];
2412 Feld[x][y] = EL_EXPLOSION;
2414 GfxElement[x][y] = center_element;
2416 GfxElement[x][y] = EL_UNDEFINED;
2419 ExplodePhase[x][y] = 1;
2423 if (center_element == EL_YAMYAM)
2424 game.yamyam_content_nr =
2425 (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
2436 ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
2440 /* activate this even in non-DEBUG version until cause for crash in
2441 getGraphicAnimationFrame() (see below) is found and eliminated */
2445 if (GfxElement[x][y] == EL_UNDEFINED)
2448 printf("Explode(): x = %d, y = %d: GfxElement == EL_UNDEFINED\n", x, y);
2449 printf("Explode(): This should never happen!\n");
2452 GfxElement[x][y] = EL_EMPTY;
2456 if (phase == first_phase_after_start)
2458 int element = Store2[x][y];
2460 if (element == EL_BLACK_ORB)
2462 Feld[x][y] = Store2[x][y];
2467 else if (phase == half_phase)
2469 int element = Store2[x][y];
2471 if (IS_PLAYER(x, y))
2472 KillHeroUnlessProtected(x, y);
2473 else if (CAN_EXPLODE_BY_FIRE(element))
2475 Feld[x][y] = Store2[x][y];
2479 else if (element == EL_AMOEBA_TO_DIAMOND)
2480 AmoebeUmwandeln(x, y);
2483 if (phase == last_phase)
2487 element = Feld[x][y] = Store[x][y];
2488 Store[x][y] = Store2[x][y] = 0;
2489 GfxElement[x][y] = EL_UNDEFINED;
2491 if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
2492 element = Feld[x][y] = Back[x][y];
2495 MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
2496 GfxDir[x][y] = MV_NO_MOVING;
2497 ChangeDelay[x][y] = 0;
2498 ChangePage[x][y] = -1;
2500 InitField(x, y, FALSE);
2501 if (CAN_MOVE(element))
2503 DrawLevelField(x, y);
2505 TestIfElementTouchesCustomElement(x, y);
2507 if (GFX_CRUMBLED(element))
2508 DrawLevelFieldCrumbledSandNeighbours(x, y);
2510 if (IS_PLAYER(x, y) && !PLAYERINFO(x,y)->present)
2511 StorePlayer[x][y] = 0;
2513 if (ELEM_IS_PLAYER(element))
2514 RelocatePlayer(x, y, element);
2516 else if (phase >= delay && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
2519 int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
2521 int stored = Store[x][y];
2522 int graphic = (game.emulation != EMU_SUPAPLEX ? IMG_EXPLOSION :
2523 stored == EL_SP_INFOTRON ? IMG_SP_EXPLOSION_INFOTRON :
2526 int frame = getGraphicAnimationFrame(graphic, phase - delay);
2529 DrawLevelFieldCrumbledSand(x, y);
2531 if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
2533 DrawLevelElement(x, y, Back[x][y]);
2534 DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
2536 else if (IS_WALKABLE_UNDER(Back[x][y]))
2538 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
2539 DrawLevelElementThruMask(x, y, Back[x][y]);
2541 else if (!IS_WALKABLE_INSIDE(Back[x][y]))
2542 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
2546 void DynaExplode(int ex, int ey)
2549 int dynabomb_size = 1;
2550 boolean dynabomb_xl = FALSE;
2551 struct PlayerInfo *player;
2552 static int xy[4][2] =
2560 if (IS_ACTIVE_BOMB(Feld[ex][ey]))
2562 player = &stored_player[Feld[ex][ey] - EL_DYNABOMB_PLAYER_1_ACTIVE];
2563 dynabomb_size = player->dynabomb_size;
2564 dynabomb_xl = player->dynabomb_xl;
2565 player->dynabombs_left++;
2568 Explode(ex, ey, EX_PHASE_START, EX_CENTER);
2570 for (i = 0; i < 4; i++)
2572 for (j = 1; j <= dynabomb_size; j++)
2574 int x = ex + j * xy[i % 4][0];
2575 int y = ey + j * xy[i % 4][1];
2578 if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
2581 element = Feld[x][y];
2583 /* do not restart explosions of fields with active bombs */
2584 if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
2587 Explode(x, y, EX_PHASE_START, EX_BORDER);
2589 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
2590 if (element != EL_EMPTY &&
2591 element != EL_SAND &&
2592 element != EL_EXPLOSION &&
2599 void Bang(int x, int y)
2602 int element = MovingOrBlocked2Element(x, y);
2604 int element = Feld[x][y];
2608 if (IS_PLAYER(x, y) && !PLAYER_PROTECTED(x, y))
2610 if (IS_PLAYER(x, y))
2613 struct PlayerInfo *player = PLAYERINFO(x, y);
2615 element = Feld[x][y] = (player->use_murphy_graphic ? EL_SP_MURPHY :
2616 player->element_nr);
2621 PlayLevelSoundAction(x, y, ACTION_EXPLODING);
2623 if (game.emulation == EMU_SUPAPLEX)
2624 PlayLevelSound(x, y, SND_SP_ELEMENT_EXPLODING);
2626 PlayLevelSound(x, y, SND_ELEMENT_EXPLODING);
2631 if (IS_PLAYER(x, y)) /* remove objects that might cause smaller explosion */
2639 case EL_BD_BUTTERFLY:
2642 case EL_DARK_YAMYAM:
2646 RaiseScoreElement(element);
2647 Explode(x, y, EX_PHASE_START, EX_NORMAL);
2649 case EL_DYNABOMB_PLAYER_1_ACTIVE:
2650 case EL_DYNABOMB_PLAYER_2_ACTIVE:
2651 case EL_DYNABOMB_PLAYER_3_ACTIVE:
2652 case EL_DYNABOMB_PLAYER_4_ACTIVE:
2653 case EL_DYNABOMB_INCREASE_NUMBER:
2654 case EL_DYNABOMB_INCREASE_SIZE:
2655 case EL_DYNABOMB_INCREASE_POWER:
2660 case EL_LAMP_ACTIVE:
2661 if (IS_PLAYER(x, y))
2662 Explode(x, y, EX_PHASE_START, EX_NORMAL);
2664 Explode(x, y, EX_PHASE_START, EX_CENTER);
2667 if (CAN_EXPLODE_1X1(element))
2668 Explode(x, y, EX_PHASE_START, EX_CENTER);
2670 Explode(x, y, EX_PHASE_START, EX_NORMAL);
2674 CheckTriggeredElementChange(x, y, element, CE_OTHER_IS_EXPLODING);
2677 void SplashAcid(int x, int y)
2679 int element = Feld[x][y];
2681 if (element != EL_ACID_SPLASH_LEFT &&
2682 element != EL_ACID_SPLASH_RIGHT)
2684 PlayLevelSound(x, y, SND_ACID_SPLASHING);
2686 if (IN_LEV_FIELD(x-1, y) && IS_FREE(x-1, y) &&
2687 (!IN_LEV_FIELD(x-1, y-1) ||
2688 !CAN_FALL(MovingOrBlocked2Element(x-1, y-1))))
2689 Feld[x-1][y] = EL_ACID_SPLASH_LEFT;
2691 if (IN_LEV_FIELD(x+1, y) && IS_FREE(x+1, y) &&
2692 (!IN_LEV_FIELD(x+1, y-1) ||
2693 !CAN_FALL(MovingOrBlocked2Element(x+1, y-1))))
2694 Feld[x+1][y] = EL_ACID_SPLASH_RIGHT;
2698 static void InitBeltMovement()
2700 static int belt_base_element[4] =
2702 EL_CONVEYOR_BELT_1_LEFT,
2703 EL_CONVEYOR_BELT_2_LEFT,
2704 EL_CONVEYOR_BELT_3_LEFT,
2705 EL_CONVEYOR_BELT_4_LEFT
2707 static int belt_base_active_element[4] =
2709 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
2710 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
2711 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
2712 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
2717 /* set frame order for belt animation graphic according to belt direction */
2718 for (i = 0; i < 4; i++)
2722 for (j = 0; j < 3; j++)
2724 int element = belt_base_active_element[belt_nr] + j;
2725 int graphic = el2img(element);
2727 if (game.belt_dir[i] == MV_LEFT)
2728 graphic_info[graphic].anim_mode &= ~ANIM_REVERSE;
2730 graphic_info[graphic].anim_mode |= ANIM_REVERSE;
2734 for (y = 0; y < lev_fieldy; y++)
2736 for (x = 0; x < lev_fieldx; x++)
2738 int element = Feld[x][y];
2740 for (i = 0; i < 4; i++)
2742 if (IS_BELT(element) && game.belt_dir[i] != MV_NO_MOVING)
2744 int e_belt_nr = getBeltNrFromBeltElement(element);
2747 if (e_belt_nr == belt_nr)
2749 int belt_part = Feld[x][y] - belt_base_element[belt_nr];
2751 Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
2759 static void ToggleBeltSwitch(int x, int y)
2761 static int belt_base_element[4] =
2763 EL_CONVEYOR_BELT_1_LEFT,
2764 EL_CONVEYOR_BELT_2_LEFT,
2765 EL_CONVEYOR_BELT_3_LEFT,
2766 EL_CONVEYOR_BELT_4_LEFT
2768 static int belt_base_active_element[4] =
2770 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
2771 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
2772 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
2773 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
2775 static int belt_base_switch_element[4] =
2777 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
2778 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
2779 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
2780 EL_CONVEYOR_BELT_4_SWITCH_LEFT
2782 static int belt_move_dir[4] =
2790 int element = Feld[x][y];
2791 int belt_nr = getBeltNrFromBeltSwitchElement(element);
2792 int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
2793 int belt_dir = belt_move_dir[belt_dir_nr];
2796 if (!IS_BELT_SWITCH(element))
2799 game.belt_dir_nr[belt_nr] = belt_dir_nr;
2800 game.belt_dir[belt_nr] = belt_dir;
2802 if (belt_dir_nr == 3)
2805 /* set frame order for belt animation graphic according to belt direction */
2806 for (i = 0; i < 3; i++)
2808 int element = belt_base_active_element[belt_nr] + i;
2809 int graphic = el2img(element);
2811 if (belt_dir == MV_LEFT)
2812 graphic_info[graphic].anim_mode &= ~ANIM_REVERSE;
2814 graphic_info[graphic].anim_mode |= ANIM_REVERSE;
2817 for (yy = 0; yy < lev_fieldy; yy++)
2819 for (xx = 0; xx < lev_fieldx; xx++)
2821 int element = Feld[xx][yy];
2823 if (IS_BELT_SWITCH(element))
2825 int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
2827 if (e_belt_nr == belt_nr)
2829 Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
2830 DrawLevelField(xx, yy);
2833 else if (IS_BELT(element) && belt_dir != MV_NO_MOVING)
2835 int e_belt_nr = getBeltNrFromBeltElement(element);
2837 if (e_belt_nr == belt_nr)
2839 int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
2841 Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
2842 DrawLevelField(xx, yy);
2845 else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NO_MOVING)
2847 int e_belt_nr = getBeltNrFromBeltActiveElement(element);
2849 if (e_belt_nr == belt_nr)
2851 int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
2853 Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
2854 DrawLevelField(xx, yy);
2861 static void ToggleSwitchgateSwitch(int x, int y)
2865 game.switchgate_pos = !game.switchgate_pos;
2867 for (yy = 0; yy < lev_fieldy; yy++)
2869 for (xx = 0; xx < lev_fieldx; xx++)
2871 int element = Feld[xx][yy];
2873 if (element == EL_SWITCHGATE_SWITCH_UP ||
2874 element == EL_SWITCHGATE_SWITCH_DOWN)
2876 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
2877 DrawLevelField(xx, yy);
2879 else if (element == EL_SWITCHGATE_OPEN ||
2880 element == EL_SWITCHGATE_OPENING)
2882 Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
2884 PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
2886 PlayLevelSound(xx, yy, SND_SWITCHGATE_CLOSING);
2889 else if (element == EL_SWITCHGATE_CLOSED ||
2890 element == EL_SWITCHGATE_CLOSING)
2892 Feld[xx][yy] = EL_SWITCHGATE_OPENING;
2894 PlayLevelSoundAction(xx, yy, ACTION_OPENING);
2896 PlayLevelSound(xx, yy, SND_SWITCHGATE_OPENING);
2903 static int getInvisibleActiveFromInvisibleElement(int element)
2905 return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
2906 element == EL_INVISIBLE_WALL ? EL_INVISIBLE_WALL_ACTIVE :
2907 element == EL_INVISIBLE_SAND ? EL_INVISIBLE_SAND_ACTIVE :
2911 static int getInvisibleFromInvisibleActiveElement(int element)
2913 return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
2914 element == EL_INVISIBLE_WALL_ACTIVE ? EL_INVISIBLE_WALL :
2915 element == EL_INVISIBLE_SAND_ACTIVE ? EL_INVISIBLE_SAND :
2919 static void RedrawAllLightSwitchesAndInvisibleElements()
2923 for (y = 0; y < lev_fieldy; y++)
2925 for (x = 0; x < lev_fieldx; x++)
2927 int element = Feld[x][y];
2929 if (element == EL_LIGHT_SWITCH &&
2930 game.light_time_left > 0)
2932 Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
2933 DrawLevelField(x, y);
2935 else if (element == EL_LIGHT_SWITCH_ACTIVE &&
2936 game.light_time_left == 0)
2938 Feld[x][y] = EL_LIGHT_SWITCH;
2939 DrawLevelField(x, y);
2941 else if (element == EL_INVISIBLE_STEELWALL ||
2942 element == EL_INVISIBLE_WALL ||
2943 element == EL_INVISIBLE_SAND)
2945 if (game.light_time_left > 0)
2946 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
2948 DrawLevelField(x, y);
2950 else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
2951 element == EL_INVISIBLE_WALL_ACTIVE ||
2952 element == EL_INVISIBLE_SAND_ACTIVE)
2954 if (game.light_time_left == 0)
2955 Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
2957 DrawLevelField(x, y);
2963 static void ToggleLightSwitch(int x, int y)
2965 int element = Feld[x][y];
2967 game.light_time_left =
2968 (element == EL_LIGHT_SWITCH ?
2969 level.time_light * FRAMES_PER_SECOND : 0);
2971 RedrawAllLightSwitchesAndInvisibleElements();
2974 static void ActivateTimegateSwitch(int x, int y)
2978 game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
2980 for (yy = 0; yy < lev_fieldy; yy++)
2982 for (xx = 0; xx < lev_fieldx; xx++)
2984 int element = Feld[xx][yy];
2986 if (element == EL_TIMEGATE_CLOSED ||
2987 element == EL_TIMEGATE_CLOSING)
2989 Feld[xx][yy] = EL_TIMEGATE_OPENING;
2990 PlayLevelSound(xx, yy, SND_TIMEGATE_OPENING);
2994 else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
2996 Feld[xx][yy] = EL_TIMEGATE_SWITCH;
2997 DrawLevelField(xx, yy);
3004 Feld[x][y] = EL_TIMEGATE_SWITCH_ACTIVE;
3007 inline static int getElementMoveStepsize(int x, int y)
3009 int element = Feld[x][y];
3010 int direction = MovDir[x][y];
3011 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
3012 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
3013 int horiz_move = (dx != 0);
3014 int sign = (horiz_move ? dx : dy);
3015 int step = sign * element_info[element].move_stepsize;
3017 /* special values for move stepsize for spring and things on conveyor belt */
3020 if (CAN_FALL(element) &&
3021 y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
3022 step = sign * MOVE_STEPSIZE_NORMAL / 2;
3023 else if (element == EL_SPRING)
3024 step = sign * MOVE_STEPSIZE_NORMAL * 2;
3030 void Impact(int x, int y)
3032 boolean lastline = (y == lev_fieldy-1);
3033 boolean object_hit = FALSE;
3034 boolean impact = (lastline || object_hit);
3035 int element = Feld[x][y];
3036 int smashed = EL_UNDEFINED;
3038 if (!lastline) /* check if element below was hit */
3040 if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
3043 object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
3044 MovDir[x][y + 1] != MV_DOWN ||
3045 MovPos[x][y + 1] <= TILEY / 2));
3047 /* do not smash moving elements that left the smashed field in time */
3048 if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
3049 ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
3053 smashed = MovingOrBlocked2Element(x, y + 1);
3055 impact = (lastline || object_hit);
3058 if (!lastline && smashed == EL_ACID) /* element falls into acid */
3064 /* only reset graphic animation if graphic really changes after impact */
3066 el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
3068 ResetGfxAnimation(x, y);
3069 DrawLevelField(x, y);
3072 if (impact && CAN_EXPLODE_IMPACT(element))
3077 else if (impact && element == EL_PEARL)
3079 Feld[x][y] = EL_PEARL_BREAKING;
3080 PlayLevelSound(x, y, SND_PEARL_BREAKING);
3083 else if (impact && CheckElementChange(x, y, element, CE_IMPACT))
3085 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
3090 if (impact && element == EL_AMOEBA_DROP)
3092 if (object_hit && IS_PLAYER(x, y + 1))
3093 KillHeroUnlessProtected(x, y + 1);
3094 else if (object_hit && smashed == EL_PENGUIN)
3098 Feld[x][y] = EL_AMOEBA_GROWING;
3099 Store[x][y] = EL_AMOEBA_WET;
3101 ResetRandomAnimationValue(x, y);
3106 if (object_hit) /* check which object was hit */
3108 if (CAN_PASS_MAGIC_WALL(element) &&
3109 (smashed == EL_MAGIC_WALL ||
3110 smashed == EL_BD_MAGIC_WALL))
3113 int activated_magic_wall =
3114 (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
3115 EL_BD_MAGIC_WALL_ACTIVE);
3117 /* activate magic wall / mill */
3118 for (yy = 0; yy < lev_fieldy; yy++)
3119 for (xx = 0; xx < lev_fieldx; xx++)
3120 if (Feld[xx][yy] == smashed)
3121 Feld[xx][yy] = activated_magic_wall;
3123 game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
3124 game.magic_wall_active = TRUE;
3126 PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
3127 SND_MAGIC_WALL_ACTIVATING :
3128 SND_BD_MAGIC_WALL_ACTIVATING));
3131 if (IS_PLAYER(x, y + 1))
3133 if (CAN_SMASH_PLAYER(element))
3135 KillHeroUnlessProtected(x, y + 1);
3139 else if (smashed == EL_PENGUIN)
3141 if (CAN_SMASH_PLAYER(element))
3147 else if (element == EL_BD_DIAMOND)
3149 if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
3155 else if ((element == EL_SP_INFOTRON ||
3156 element == EL_SP_ZONK) &&
3157 (smashed == EL_SP_SNIKSNAK ||
3158 smashed == EL_SP_ELECTRON ||
3159 smashed == EL_SP_DISK_ORANGE))
3165 else if (CAN_SMASH_ENEMIES(element) && IS_CLASSIC_ENEMY(smashed))
3171 else if (CAN_SMASH_EVERYTHING(element))
3173 if (IS_CLASSIC_ENEMY(smashed) ||
3174 CAN_EXPLODE_SMASHED(smashed))
3179 else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
3181 if (smashed == EL_LAMP ||
3182 smashed == EL_LAMP_ACTIVE)
3187 else if (smashed == EL_NUT)
3189 Feld[x][y + 1] = EL_NUT_BREAKING;
3190 PlayLevelSound(x, y, SND_NUT_BREAKING);
3191 RaiseScoreElement(EL_NUT);
3194 else if (smashed == EL_PEARL)
3196 Feld[x][y + 1] = EL_PEARL_BREAKING;
3197 PlayLevelSound(x, y, SND_PEARL_BREAKING);
3200 else if (smashed == EL_DIAMOND)
3202 Feld[x][y + 1] = EL_DIAMOND_BREAKING;
3203 PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
3206 else if (IS_BELT_SWITCH(smashed))
3208 ToggleBeltSwitch(x, y + 1);
3210 else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
3211 smashed == EL_SWITCHGATE_SWITCH_DOWN)
3213 ToggleSwitchgateSwitch(x, y + 1);
3215 else if (smashed == EL_LIGHT_SWITCH ||
3216 smashed == EL_LIGHT_SWITCH_ACTIVE)
3218 ToggleLightSwitch(x, y + 1);
3222 CheckElementChange(x, y + 1, smashed, CE_SMASHED);
3224 CheckTriggeredElementSideChange(x, y + 1, smashed, CH_SIDE_TOP,
3225 CE_OTHER_IS_SWITCHING);
3226 CheckElementSideChange(x, y + 1, smashed, CH_SIDE_TOP,
3232 CheckElementChange(x, y + 1, smashed, CE_SMASHED);
3237 /* play sound of magic wall / mill */
3239 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
3240 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE))
3242 if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
3243 PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
3244 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
3245 PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
3250 /* play sound of object that hits the ground */
3251 if (lastline || object_hit)
3252 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
3255 inline static void TurnRoundExt(int x, int y)
3267 { 0, 0 }, { 0, 0 }, { 0, 0 },
3272 int left, right, back;
3276 { MV_DOWN, MV_UP, MV_RIGHT },
3277 { MV_UP, MV_DOWN, MV_LEFT },
3279 { MV_LEFT, MV_RIGHT, MV_DOWN },
3283 { MV_RIGHT, MV_LEFT, MV_UP }
3286 int element = Feld[x][y];
3287 int move_pattern = element_info[element].move_pattern;
3289 int old_move_dir = MovDir[x][y];
3290 int left_dir = turn[old_move_dir].left;
3291 int right_dir = turn[old_move_dir].right;
3292 int back_dir = turn[old_move_dir].back;
3294 int left_dx = move_xy[left_dir].x, left_dy = move_xy[left_dir].y;
3295 int right_dx = move_xy[right_dir].x, right_dy = move_xy[right_dir].y;
3296 int move_dx = move_xy[old_move_dir].x, move_dy = move_xy[old_move_dir].y;
3297 int back_dx = move_xy[back_dir].x, back_dy = move_xy[back_dir].y;
3299 int left_x = x + left_dx, left_y = y + left_dy;
3300 int right_x = x + right_dx, right_y = y + right_dy;
3301 int move_x = x + move_dx, move_y = y + move_dy;
3305 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
3307 TestIfBadThingTouchesOtherBadThing(x, y);
3309 if (ENEMY_CAN_ENTER_FIELD(right_x, right_y))
3310 MovDir[x][y] = right_dir;
3311 else if (!ENEMY_CAN_ENTER_FIELD(move_x, move_y))
3312 MovDir[x][y] = left_dir;
3314 if (element == EL_BUG && MovDir[x][y] != old_move_dir)
3316 else if (element == EL_BD_BUTTERFLY) /* && MovDir[x][y] == left_dir) */
3319 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
3320 element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
3322 TestIfBadThingTouchesOtherBadThing(x, y);
3324 if (ENEMY_CAN_ENTER_FIELD(left_x, left_y))
3325 MovDir[x][y] = left_dir;
3326 else if (!ENEMY_CAN_ENTER_FIELD(move_x, move_y))
3327 MovDir[x][y] = right_dir;
3329 if ((element == EL_SPACESHIP ||
3330 element == EL_SP_SNIKSNAK ||
3331 element == EL_SP_ELECTRON)
3332 && MovDir[x][y] != old_move_dir)
3334 else if (element == EL_BD_FIREFLY) /* && MovDir[x][y] == right_dir) */
3337 else if (element == EL_YAMYAM)
3339 boolean can_turn_left = YAMYAM_CAN_ENTER_FIELD(left_x, left_y);
3340 boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(right_x, right_y);
3342 if (can_turn_left && can_turn_right)
3343 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
3344 else if (can_turn_left)
3345 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
3346 else if (can_turn_right)
3347 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
3349 MovDir[x][y] = back_dir;
3351 MovDelay[x][y] = 16 + 16 * RND(3);
3353 else if (element == EL_DARK_YAMYAM)
3355 boolean can_turn_left = DARK_YAMYAM_CAN_ENTER_FIELD(left_x, left_y);
3356 boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(right_x, right_y);
3358 if (can_turn_left && can_turn_right)
3359 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
3360 else if (can_turn_left)
3361 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
3362 else if (can_turn_right)
3363 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
3365 MovDir[x][y] = back_dir;
3367 MovDelay[x][y] = 16 + 16 * RND(3);
3369 else if (element == EL_PACMAN)
3371 boolean can_turn_left = PACMAN_CAN_ENTER_FIELD(left_x, left_y);
3372 boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(right_x, right_y);
3374 if (can_turn_left && can_turn_right)
3375 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
3376 else if (can_turn_left)
3377 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
3378 else if (can_turn_right)
3379 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
3381 MovDir[x][y] = back_dir;
3383 MovDelay[x][y] = 6 + RND(40);
3385 else if (element == EL_PIG)
3387 boolean can_turn_left = PIG_CAN_ENTER_FIELD(left_x, left_y);
3388 boolean can_turn_right = PIG_CAN_ENTER_FIELD(right_x, right_y);
3389 boolean can_move_on = PIG_CAN_ENTER_FIELD(move_x, move_y);
3390 boolean should_turn_left, should_turn_right, should_move_on;
3392 int rnd = RND(rnd_value);
3394 should_turn_left = (can_turn_left &&
3396 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
3397 y + back_dy + left_dy)));
3398 should_turn_right = (can_turn_right &&
3400 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
3401 y + back_dy + right_dy)));
3402 should_move_on = (can_move_on &&
3405 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
3406 y + move_dy + left_dy) ||
3407 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
3408 y + move_dy + right_dy)));
3410 if (should_turn_left || should_turn_right || should_move_on)
3412 if (should_turn_left && should_turn_right && should_move_on)
3413 MovDir[x][y] = (rnd < rnd_value / 3 ? left_dir :
3414 rnd < 2 * rnd_value / 3 ? right_dir :
3416 else if (should_turn_left && should_turn_right)
3417 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
3418 else if (should_turn_left && should_move_on)
3419 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
3420 else if (should_turn_right && should_move_on)
3421 MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
3422 else if (should_turn_left)
3423 MovDir[x][y] = left_dir;
3424 else if (should_turn_right)
3425 MovDir[x][y] = right_dir;
3426 else if (should_move_on)
3427 MovDir[x][y] = old_move_dir;
3429 else if (can_move_on && rnd > rnd_value / 8)
3430 MovDir[x][y] = old_move_dir;
3431 else if (can_turn_left && can_turn_right)
3432 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
3433 else if (can_turn_left && rnd > rnd_value / 8)
3434 MovDir[x][y] = left_dir;
3435 else if (can_turn_right && rnd > rnd_value/8)
3436 MovDir[x][y] = right_dir;
3438 MovDir[x][y] = back_dir;
3440 xx = x + move_xy[MovDir[x][y]].x;
3441 yy = y + move_xy[MovDir[x][y]].y;
3443 if (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy]))
3444 MovDir[x][y] = old_move_dir;
3448 else if (element == EL_DRAGON)
3450 boolean can_turn_left = IN_LEV_FIELD_AND_IS_FREE(left_x, left_y);
3451 boolean can_turn_right = IN_LEV_FIELD_AND_IS_FREE(right_x, right_y);
3452 boolean can_move_on = IN_LEV_FIELD_AND_IS_FREE(move_x, move_y);
3454 int rnd = RND(rnd_value);
3457 if (FrameCounter < 1 && x == 0 && y == 29)
3458 printf(":2: %d/%d: %d [%d]\n", x, y, MovDir[x][y], FrameCounter);
3461 if (can_move_on && rnd > rnd_value / 8)
3462 MovDir[x][y] = old_move_dir;
3463 else if (can_turn_left && can_turn_right)
3464 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
3465 else if (can_turn_left && rnd > rnd_value / 8)
3466 MovDir[x][y] = left_dir;
3467 else if (can_turn_right && rnd > rnd_value / 8)
3468 MovDir[x][y] = right_dir;
3470 MovDir[x][y] = back_dir;
3472 xx = x + move_xy[MovDir[x][y]].x;
3473 yy = y + move_xy[MovDir[x][y]].y;
3476 if (FrameCounter < 1 && x == 0 && y == 29)
3477 printf(":3: %d/%d: %d (%d/%d: %d) [%d]\n", x, y, MovDir[x][y],
3478 xx, yy, Feld[xx][yy],
3483 if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
3484 MovDir[x][y] = old_move_dir;
3486 if (!IS_FREE(xx, yy))
3487 MovDir[x][y] = old_move_dir;
3491 if (FrameCounter < 1 && x == 0 && y == 29)
3492 printf(":4: %d/%d: %d [%d]\n", x, y, MovDir[x][y], FrameCounter);
3497 else if (element == EL_MOLE)
3499 boolean can_move_on =
3500 (MOLE_CAN_ENTER_FIELD(move_x, move_y,
3501 IS_AMOEBOID(Feld[move_x][move_y]) ||
3502 Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
3505 boolean can_turn_left =
3506 (MOLE_CAN_ENTER_FIELD(left_x, left_y,
3507 IS_AMOEBOID(Feld[left_x][left_y])));
3509 boolean can_turn_right =
3510 (MOLE_CAN_ENTER_FIELD(right_x, right_y,
3511 IS_AMOEBOID(Feld[right_x][right_y])));
3513 if (can_turn_left && can_turn_right)
3514 MovDir[x][y] = (RND(2) ? left_dir : right_dir);
3515 else if (can_turn_left)
3516 MovDir[x][y] = left_dir;
3518 MovDir[x][y] = right_dir;
3521 if (MovDir[x][y] != old_move_dir)
3524 else if (element == EL_BALLOON)
3526 MovDir[x][y] = game.balloon_dir;
3529 else if (element == EL_SPRING)
3531 if (MovDir[x][y] & MV_HORIZONTAL &&
3532 (!IN_LEV_FIELD_AND_IS_FREE(move_x, move_y) ||
3533 IN_LEV_FIELD_AND_IS_FREE(x, y + 1)))
3534 MovDir[x][y] = MV_NO_MOVING;
3538 else if (element == EL_ROBOT ||
3539 element == EL_SATELLITE ||
3540 element == EL_PENGUIN)
3542 int attr_x = -1, attr_y = -1;
3553 for (i = 0; i < MAX_PLAYERS; i++)
3555 struct PlayerInfo *player = &stored_player[i];
3556 int jx = player->jx, jy = player->jy;
3558 if (!player->active)
3562 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
3570 if (element == EL_ROBOT && ZX >= 0 && ZY >= 0)
3576 if (element == EL_PENGUIN)
3579 static int xy[4][2] =
3587 for (i = 0; i < 4; i++)
3589 int ex = x + xy[i % 4][0];
3590 int ey = y + xy[i % 4][1];
3592 if (IN_LEV_FIELD(ex, ey) && Feld[ex][ey] == EL_EXIT_OPEN)
3601 MovDir[x][y] = MV_NO_MOVING;
3603 MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
3604 else if (attr_x > x)
3605 MovDir[x][y] |= (AllPlayersGone ? MV_LEFT : MV_RIGHT);
3607 MovDir[x][y] |= (AllPlayersGone ? MV_DOWN : MV_UP);
3608 else if (attr_y > y)
3609 MovDir[x][y] |= (AllPlayersGone ? MV_UP : MV_DOWN);
3611 if (element == EL_ROBOT)
3615 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
3616 MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
3617 Moving2Blocked(x, y, &newx, &newy);
3619 if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
3620 MovDelay[x][y] = 8 + 8 * !RND(3);
3622 MovDelay[x][y] = 16;
3624 else if (element == EL_PENGUIN)
3630 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
3632 boolean first_horiz = RND(2);
3633 int new_move_dir = MovDir[x][y];
3636 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
3637 Moving2Blocked(x, y, &newx, &newy);
3639 if (PENGUIN_CAN_ENTER_FIELD(newx, newy))
3643 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
3644 Moving2Blocked(x, y, &newx, &newy);
3646 if (PENGUIN_CAN_ENTER_FIELD(newx, newy))
3649 MovDir[x][y] = old_move_dir;
3653 else /* (element == EL_SATELLITE) */
3659 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
3661 boolean first_horiz = RND(2);
3662 int new_move_dir = MovDir[x][y];
3665 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
3666 Moving2Blocked(x, y, &newx, &newy);
3668 if (ELEMENT_CAN_ENTER_FIELD_OR_ACID_2(newx, newy))
3672 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
3673 Moving2Blocked(x, y, &newx, &newy);
3675 if (ELEMENT_CAN_ENTER_FIELD_OR_ACID_2(newx, newy))
3678 MovDir[x][y] = old_move_dir;
3683 else if (move_pattern == MV_ALL_DIRECTIONS ||
3684 move_pattern == MV_TURNING_LEFT ||
3685 move_pattern == MV_TURNING_RIGHT)
3687 boolean can_turn_left = ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
3688 boolean can_turn_right = ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
3690 if (move_pattern == MV_TURNING_LEFT)
3691 MovDir[x][y] = left_dir;
3692 else if (move_pattern == MV_TURNING_RIGHT)
3693 MovDir[x][y] = right_dir;
3694 else if (can_turn_left && can_turn_right)
3695 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
3696 else if (can_turn_left)
3697 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
3698 else if (can_turn_right)
3699 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
3701 MovDir[x][y] = back_dir;
3703 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
3705 else if (move_pattern == MV_HORIZONTAL ||
3706 move_pattern == MV_VERTICAL)
3708 if (move_pattern & old_move_dir)
3709 MovDir[x][y] = back_dir;
3710 else if (move_pattern == MV_HORIZONTAL)
3711 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
3712 else if (move_pattern == MV_VERTICAL)
3713 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
3715 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
3717 else if (move_pattern & MV_ANY_DIRECTION)
3719 MovDir[x][y] = move_pattern;
3720 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
3722 else if (move_pattern == MV_ALONG_LEFT_SIDE)
3724 if (ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
3725 MovDir[x][y] = left_dir;
3726 else if (!ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
3727 MovDir[x][y] = right_dir;
3729 if (MovDir[x][y] != old_move_dir)
3730 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
3732 else if (move_pattern == MV_ALONG_RIGHT_SIDE)
3734 if (ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
3735 MovDir[x][y] = right_dir;
3736 else if (!ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
3737 MovDir[x][y] = left_dir;
3739 if (MovDir[x][y] != old_move_dir)
3740 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
3742 else if (move_pattern == MV_TOWARDS_PLAYER ||
3743 move_pattern == MV_AWAY_FROM_PLAYER)
3745 int attr_x = -1, attr_y = -1;
3747 boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
3758 for (i = 0; i < MAX_PLAYERS; i++)
3760 struct PlayerInfo *player = &stored_player[i];
3761 int jx = player->jx, jy = player->jy;
3763 if (!player->active)
3767 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
3775 MovDir[x][y] = MV_NO_MOVING;
3777 MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
3778 else if (attr_x > x)
3779 MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
3781 MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
3782 else if (attr_y > y)
3783 MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
3785 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
3787 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
3789 boolean first_horiz = RND(2);
3790 int new_move_dir = MovDir[x][y];
3793 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
3794 Moving2Blocked(x, y, &newx, &newy);
3796 if (ELEMENT_CAN_ENTER_FIELD_OR_ACID(element, newx, newy))
3800 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
3801 Moving2Blocked(x, y, &newx, &newy);
3803 if (ELEMENT_CAN_ENTER_FIELD_OR_ACID(element, newx, newy))
3806 MovDir[x][y] = old_move_dir;
3809 else if (move_pattern == MV_WHEN_PUSHED)
3811 if (!IN_LEV_FIELD_AND_IS_FREE(move_x, move_y))
3812 MovDir[x][y] = MV_NO_MOVING;
3816 else if (move_pattern & MV_MAZE_RUNNER_STYLE ||
3817 element == EL_MAZE_RUNNER)
3819 static int test_xy[7][2] =
3829 static int test_dir[7] =
3839 boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
3840 int move_preference = -1000000; /* start with very low preference */
3841 int new_move_dir = MV_NO_MOVING;
3842 int start_test = RND(4);
3845 for (i = 0; i < 4; i++)
3847 int move_dir = test_dir[start_test + i];
3848 int move_dir_preference;
3850 xx = x + test_xy[start_test + i][0];
3851 yy = y + test_xy[start_test + i][1];
3853 if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
3854 (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
3856 new_move_dir = move_dir;
3861 if (!MAZE_RUNNER_CAN_ENTER_FIELD(xx, yy))
3864 move_dir_preference = -1 * RunnerVisit[xx][yy];
3865 if (hunter_mode && PlayerVisit[xx][yy] > 0)
3866 move_dir_preference = PlayerVisit[xx][yy];
3868 if (move_dir_preference > move_preference)
3870 /* prefer field that has not been visited for the longest time */
3871 move_preference = move_dir_preference;
3872 new_move_dir = move_dir;
3874 else if (move_dir_preference == move_preference &&
3875 move_dir == old_move_dir)
3877 /* prefer last direction when all directions are preferred equally */
3878 move_preference = move_dir_preference;
3879 new_move_dir = move_dir;
3883 MovDir[x][y] = new_move_dir;
3884 if (old_move_dir != new_move_dir)
3889 static void TurnRound(int x, int y)
3891 int direction = MovDir[x][y];
3894 GfxDir[x][y] = MovDir[x][y];
3900 GfxDir[x][y] = MovDir[x][y];
3903 if (direction != MovDir[x][y])
3908 GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_BIT(direction);
3911 GfxAction[x][y] = ACTION_WAITING;
3915 static boolean JustBeingPushed(int x, int y)
3919 for (i = 0; i < MAX_PLAYERS; i++)
3921 struct PlayerInfo *player = &stored_player[i];
3923 if (player->active && player->is_pushing && player->MovPos)
3925 int next_jx = player->jx + (player->jx - player->last_jx);
3926 int next_jy = player->jy + (player->jy - player->last_jy);
3928 if (x == next_jx && y == next_jy)
3936 void StartMoving(int x, int y)
3938 boolean use_spring_bug = (game.engine_version < VERSION_IDENT(2,2,0,0));
3939 boolean started_moving = FALSE; /* some elements can fall _and_ move */
3940 int element = Feld[x][y];
3946 if (MovDelay[x][y] == 0)
3947 GfxAction[x][y] = ACTION_DEFAULT;
3949 /* !!! this should be handled more generic (not only for mole) !!! */
3950 if (element != EL_MOLE && GfxAction[x][y] != ACTION_DIGGING)
3951 GfxAction[x][y] = ACTION_DEFAULT;
3954 if (CAN_FALL(element) && y < lev_fieldy - 1)
3956 if ((x > 0 && IS_PLAYER(x - 1, y)) ||
3957 (x < lev_fieldx-1 && IS_PLAYER(x + 1, y)))
3958 if (JustBeingPushed(x, y))
3961 if (element == EL_QUICKSAND_FULL)
3963 if (IS_FREE(x, y + 1))
3965 InitMovingField(x, y, MV_DOWN);
3966 started_moving = TRUE;
3968 Feld[x][y] = EL_QUICKSAND_EMPTYING;
3969 Store[x][y] = EL_ROCK;
3971 PlayLevelSoundAction(x, y, ACTION_EMPTYING);
3973 PlayLevelSound(x, y, SND_QUICKSAND_EMPTYING);
3976 else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
3978 if (!MovDelay[x][y])
3979 MovDelay[x][y] = TILEY + 1;
3988 Feld[x][y] = EL_QUICKSAND_EMPTY;
3989 Feld[x][y + 1] = EL_QUICKSAND_FULL;
3990 Store[x][y + 1] = Store[x][y];
3993 PlayLevelSoundAction(x, y, ACTION_FILLING);
3995 PlayLevelSound(x, y, SND_QUICKSAND_FILLING);
3999 else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
4000 Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
4002 InitMovingField(x, y, MV_DOWN);
4003 started_moving = TRUE;
4005 Feld[x][y] = EL_QUICKSAND_FILLING;
4006 Store[x][y] = element;
4008 PlayLevelSoundAction(x, y, ACTION_FILLING);
4010 PlayLevelSound(x, y, SND_QUICKSAND_FILLING);
4013 else if (element == EL_MAGIC_WALL_FULL)
4015 if (IS_FREE(x, y + 1))
4017 InitMovingField(x, y, MV_DOWN);
4018 started_moving = TRUE;
4020 Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
4021 Store[x][y] = EL_CHANGED(Store[x][y]);
4023 else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
4025 if (!MovDelay[x][y])
4026 MovDelay[x][y] = TILEY/4 + 1;
4035 Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
4036 Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
4037 Store[x][y + 1] = EL_CHANGED(Store[x][y]);
4041 else if (element == EL_BD_MAGIC_WALL_FULL)
4043 if (IS_FREE(x, y + 1))
4045 InitMovingField(x, y, MV_DOWN);
4046 started_moving = TRUE;
4048 Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
4049 Store[x][y] = EL_CHANGED2(Store[x][y]);
4051 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
4053 if (!MovDelay[x][y])
4054 MovDelay[x][y] = TILEY/4 + 1;
4063 Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
4064 Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
4065 Store[x][y + 1] = EL_CHANGED2(Store[x][y]);
4069 else if (CAN_PASS_MAGIC_WALL(element) &&
4070 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
4071 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE))
4073 InitMovingField(x, y, MV_DOWN);
4074 started_moving = TRUE;
4077 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
4078 EL_BD_MAGIC_WALL_FILLING);
4079 Store[x][y] = element;
4082 else if (CAN_SMASH(element) && Feld[x][y + 1] == EL_ACID)
4084 else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
4089 InitMovingField(x, y, MV_DOWN);
4090 started_moving = TRUE;
4092 Store[x][y] = EL_ACID;
4094 /* !!! TEST !!! better use "_FALLING" etc. !!! */
4095 GfxAction[x][y + 1] = ACTION_ACTIVE;
4099 else if ((game.engine_version < VERSION_IDENT(2,2,0,7) &&
4100 CAN_SMASH(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
4101 (Feld[x][y + 1] == EL_BLOCKED)) ||
4102 (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
4103 CAN_SMASH(element) && WasJustFalling[x][y] &&
4104 (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))))
4108 else if (game.engine_version < VERSION_IDENT(2,2,0,7) &&
4109 CAN_SMASH(element) && Feld[x][y + 1] == EL_BLOCKED &&
4110 WasJustMoving[x][y] && !Pushed[x][y + 1])
4112 else if (CAN_SMASH(element) && Feld[x][y + 1] == EL_BLOCKED &&
4113 WasJustMoving[x][y])
4118 /* this is needed for a special case not covered by calling "Impact()"
4119 from "ContinueMoving()": if an element moves to a tile directly below
4120 another element which was just falling on that tile (which was empty
4121 in the previous frame), the falling element above would just stop
4122 instead of smashing the element below (in previous version, the above
4123 element was just checked for "moving" instead of "falling", resulting
4124 in incorrect smashes caused by horizontal movement of the above
4125 element; also, the case of the player being the element to smash was
4126 simply not covered here... :-/ ) */
4130 else if (IS_FREE(x, y + 1) && element == EL_SPRING && use_spring_bug)
4132 if (MovDir[x][y] == MV_NO_MOVING)
4134 InitMovingField(x, y, MV_DOWN);
4135 started_moving = TRUE;
4138 else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
4140 if (WasJustFalling[x][y]) /* prevent animation from being restarted */
4141 MovDir[x][y] = MV_DOWN;
4143 InitMovingField(x, y, MV_DOWN);
4144 started_moving = TRUE;
4146 else if (element == EL_AMOEBA_DROP)
4148 Feld[x][y] = EL_AMOEBA_GROWING;
4149 Store[x][y] = EL_AMOEBA_WET;
4151 /* Store[x][y + 1] must be zero, because:
4152 (EL_QUICKSAND_FULL -> EL_ROCK): Store[x][y + 1] == EL_QUICKSAND_EMPTY
4155 #if OLD_GAME_BEHAVIOUR
4156 else if (IS_SLIPPERY(Feld[x][y + 1]) && !Store[x][y + 1])
4158 else if (IS_SLIPPERY(Feld[x][y + 1]) && !Store[x][y + 1] &&
4159 !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
4160 element != EL_DX_SUPABOMB)
4163 else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
4164 (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
4165 !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
4166 element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
4169 boolean can_fall_left = (x > 0 && IS_FREE(x - 1, y) &&
4170 (IS_FREE(x - 1, y + 1) ||
4171 Feld[x - 1][y + 1] == EL_ACID));
4172 boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
4173 (IS_FREE(x + 1, y + 1) ||
4174 Feld[x + 1][y + 1] == EL_ACID));
4175 boolean can_fall_any = (can_fall_left || can_fall_right);
4176 boolean can_fall_both = (can_fall_left && can_fall_right);
4178 if (can_fall_any && IS_CUSTOM_ELEMENT(Feld[x][y + 1]))
4180 int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
4182 if (slippery_type == SLIPPERY_ONLY_LEFT)
4183 can_fall_right = FALSE;
4184 else if (slippery_type == SLIPPERY_ONLY_RIGHT)
4185 can_fall_left = FALSE;
4186 else if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
4187 can_fall_right = FALSE;
4188 else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
4189 can_fall_left = FALSE;
4191 can_fall_any = (can_fall_left || can_fall_right);
4192 can_fall_both = (can_fall_left && can_fall_right);
4197 if (can_fall_both &&
4198 (game.emulation != EMU_BOULDERDASH &&
4199 element != EL_BD_ROCK && element != EL_BD_DIAMOND))
4200 can_fall_left = !(can_fall_right = RND(2));
4202 InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
4203 started_moving = TRUE;
4206 else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
4208 boolean left_is_free = (x > 0 && IS_FREE(x - 1, y));
4209 boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
4210 int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
4211 int belt_dir = game.belt_dir[belt_nr];
4213 if ((belt_dir == MV_LEFT && left_is_free) ||
4214 (belt_dir == MV_RIGHT && right_is_free))
4216 InitMovingField(x, y, belt_dir);
4217 started_moving = TRUE;
4219 GfxAction[x][y] = ACTION_DEFAULT;
4224 /* not "else if" because of elements that can fall and move (EL_SPRING) */
4225 if (CAN_MOVE(element) && !started_moving)
4227 int move_pattern = element_info[element].move_pattern;
4231 if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
4234 if ((element == EL_SATELLITE ||
4235 element == EL_BALLOON ||
4236 element == EL_SPRING)
4237 && JustBeingPushed(x, y))
4243 if (element == EL_SPRING && MovDir[x][y] == MV_DOWN)
4244 Feld[x][y + 1] = EL_EMPTY; /* was set to EL_BLOCKED above */
4246 if (element == EL_SPRING && MovDir[x][y] != MV_NO_MOVING)
4248 Moving2Blocked(x, y, &newx, &newy);
4249 if (Feld[newx][newy] == EL_BLOCKED)
4250 Feld[newx][newy] = EL_EMPTY; /* was set to EL_BLOCKED above */
4256 if (FrameCounter < 1 && x == 0 && y == 29)
4257 printf(":1: %d/%d: %d [%d]\n", x, y, MovDir[x][y], FrameCounter);
4260 if (!MovDelay[x][y]) /* start new movement phase */
4262 /* all objects that can change their move direction after each step
4263 (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall */
4265 if (element != EL_YAMYAM &&
4266 element != EL_DARK_YAMYAM &&
4267 element != EL_PACMAN &&
4268 !(move_pattern & MV_ANY_DIRECTION) &&
4269 move_pattern != MV_TURNING_LEFT &&
4270 move_pattern != MV_TURNING_RIGHT)
4275 if (FrameCounter < 1 && x == 0 && y == 29)
4276 printf(":9: %d: %d [%d]\n", y, MovDir[x][y], FrameCounter);
4279 if (MovDelay[x][y] && (element == EL_BUG ||
4280 element == EL_SPACESHIP ||
4281 element == EL_SP_SNIKSNAK ||
4282 element == EL_SP_ELECTRON ||
4283 element == EL_MOLE))
4284 DrawLevelField(x, y);
4288 if (MovDelay[x][y]) /* wait some time before next movement */
4293 if (element == EL_YAMYAM)
4296 el_act_dir2img(EL_YAMYAM, ACTION_WAITING, MV_LEFT));
4297 DrawLevelElementAnimation(x, y, element);
4301 if (MovDelay[x][y]) /* element still has to wait some time */
4304 /* !!! PLACE THIS SOMEWHERE AFTER "TurnRound()" !!! */
4305 ResetGfxAnimation(x, y);
4309 if (GfxAction[x][y] != ACTION_WAITING)
4310 printf("::: %d: %d != ACTION_WAITING\n", element, GfxAction[x][y]);
4312 GfxAction[x][y] = ACTION_WAITING;
4316 if (element == EL_ROBOT ||
4318 element == EL_PACMAN ||
4320 element == EL_YAMYAM ||
4321 element == EL_DARK_YAMYAM)
4324 DrawLevelElementAnimation(x, y, element);
4326 DrawLevelElementAnimationIfNeeded(x, y, element);
4328 PlayLevelSoundAction(x, y, ACTION_WAITING);
4330 else if (element == EL_SP_ELECTRON)
4331 DrawLevelElementAnimationIfNeeded(x, y, element);
4332 else if (element == EL_DRAGON)
4335 int dir = MovDir[x][y];
4336 int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
4337 int dy = (dir == MV_UP ? -1 : dir == MV_DOWN ? +1 : 0);
4338 int graphic = (dir == MV_LEFT ? IMG_FLAMES_1_LEFT :
4339 dir == MV_RIGHT ? IMG_FLAMES_1_RIGHT :
4340 dir == MV_UP ? IMG_FLAMES_1_UP :
4341 dir == MV_DOWN ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
4342 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
4345 printf("::: %d, %d\n", GfxAction[x][y], GfxFrame[x][y]);
4348 GfxAction[x][y] = ACTION_ATTACKING;
4350 if (IS_PLAYER(x, y))
4351 DrawPlayerField(x, y);
4353 DrawLevelField(x, y);
4355 PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
4357 for (i = 1; i <= 3; i++)
4359 int xx = x + i * dx;
4360 int yy = y + i * dy;
4361 int sx = SCREENX(xx);
4362 int sy = SCREENY(yy);
4363 int flame_graphic = graphic + (i - 1);
4365 if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
4370 int flamed = MovingOrBlocked2Element(xx, yy);
4372 if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_FIRE(flamed))
4375 RemoveMovingField(xx, yy);
4377 Feld[xx][yy] = EL_FLAMES;
4378 if (IN_SCR_FIELD(sx, sy))
4380 DrawLevelFieldCrumbledSand(xx, yy);
4381 DrawGraphic(sx, sy, flame_graphic, frame);
4386 if (Feld[xx][yy] == EL_FLAMES)
4387 Feld[xx][yy] = EL_EMPTY;
4388 DrawLevelField(xx, yy);
4393 if (MovDelay[x][y]) /* element still has to wait some time */
4395 PlayLevelSoundAction(x, y, ACTION_WAITING);
4401 /* special case of "moving" animation of waiting elements (FIX THIS !!!);
4402 for all other elements GfxAction will be set by InitMovingField() */
4403 if (element == EL_BD_BUTTERFLY || element == EL_BD_FIREFLY)
4404 GfxAction[x][y] = ACTION_MOVING;
4408 /* now make next step */
4410 Moving2Blocked(x, y, &newx, &newy); /* get next screen position */
4412 if (DONT_COLLIDE_WITH(element) &&
4413 IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
4414 !PLAYER_PROTECTED(newx, newy))
4417 TestIfBadThingRunsIntoHero(x, y, MovDir[x][y]);
4420 /* player killed by element which is deadly when colliding with */
4422 KillHero(PLAYERINFO(newx, newy));
4427 else if ((element == EL_PENGUIN ||
4428 element == EL_ROBOT ||
4429 element == EL_SATELLITE ||
4430 element == EL_BALLOON ||
4431 IS_CUSTOM_ELEMENT(element)) &&
4432 IN_LEV_FIELD(newx, newy) &&
4433 MovDir[x][y] == MV_DOWN && Feld[newx][newy] == EL_ACID)
4436 Store[x][y] = EL_ACID;
4438 else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
4440 if (Feld[newx][newy] == EL_EXIT_OPEN)
4444 DrawLevelField(x, y);
4446 Feld[x][y] = EL_EMPTY;
4447 DrawLevelField(x, y);
4450 PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
4451 if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
4452 DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
4454 local_player->friends_still_needed--;
4455 if (!local_player->friends_still_needed &&
4456 !local_player->GameOver && AllPlayersGone)
4457 local_player->LevelSolved = local_player->GameOver = TRUE;
4461 else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
4463 if (DigField(local_player, newx, newy, 0, 0, DF_DIG) == MF_MOVING)
4464 DrawLevelField(newx, newy);
4466 GfxDir[x][y] = MovDir[x][y] = MV_NO_MOVING;
4468 else if (!IS_FREE(newx, newy))
4470 GfxAction[x][y] = ACTION_WAITING;
4472 if (IS_PLAYER(x, y))
4473 DrawPlayerField(x, y);
4475 DrawLevelField(x, y);
4479 else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
4481 if (IS_FOOD_PIG(Feld[newx][newy]))
4483 if (IS_MOVING(newx, newy))
4484 RemoveMovingField(newx, newy);
4487 Feld[newx][newy] = EL_EMPTY;
4488 DrawLevelField(newx, newy);
4491 PlayLevelSound(x, y, SND_PIG_DIGGING);
4493 else if (!IS_FREE(newx, newy))
4495 if (IS_PLAYER(x, y))
4496 DrawPlayerField(x, y);
4498 DrawLevelField(x, y);
4502 else if ((move_pattern & MV_MAZE_RUNNER_STYLE ||
4503 element == EL_MAZE_RUNNER) && IN_LEV_FIELD(newx, newy))
4505 if (IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
4507 if (IS_MOVING(newx, newy))
4508 RemoveMovingField(newx, newy);
4511 Feld[newx][newy] = EL_EMPTY;
4512 DrawLevelField(newx, newy);
4515 PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
4517 else if (!IS_FREE(newx, newy))
4520 if (IS_PLAYER(x, y))
4521 DrawPlayerField(x, y);
4523 DrawLevelField(x, y);
4528 RunnerVisit[x][y] = FrameCounter;
4529 PlayerVisit[x][y] /= 8; /* expire player visit path */
4531 else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
4533 if (!IS_FREE(newx, newy))
4535 if (IS_PLAYER(x, y))
4536 DrawPlayerField(x, y);
4538 DrawLevelField(x, y);
4544 boolean wanna_flame = !RND(10);
4545 int dx = newx - x, dy = newy - y;
4546 int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
4547 int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
4548 int element1 = (IN_LEV_FIELD(newx1, newy1) ?
4549 MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
4550 int element2 = (IN_LEV_FIELD(newx2, newy2) ?
4551 MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
4554 IS_CLASSIC_ENEMY(element1) ||
4555 IS_CLASSIC_ENEMY(element2)) &&
4556 element1 != EL_DRAGON && element2 != EL_DRAGON &&
4557 element1 != EL_FLAMES && element2 != EL_FLAMES)
4560 ResetGfxAnimation(x, y);
4561 GfxAction[x][y] = ACTION_ATTACKING;
4564 if (IS_PLAYER(x, y))
4565 DrawPlayerField(x, y);
4567 DrawLevelField(x, y);
4569 PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
4571 MovDelay[x][y] = 50;
4573 Feld[newx][newy] = EL_FLAMES;
4574 if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
4575 Feld[newx1][newy1] = EL_FLAMES;
4576 if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
4577 Feld[newx2][newy2] = EL_FLAMES;
4583 else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
4584 Feld[newx][newy] == EL_DIAMOND)
4586 if (IS_MOVING(newx, newy))
4587 RemoveMovingField(newx, newy);
4590 Feld[newx][newy] = EL_EMPTY;
4591 DrawLevelField(newx, newy);
4594 PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
4596 else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
4597 IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
4599 if (AmoebaNr[newx][newy])
4601 AmoebaCnt2[AmoebaNr[newx][newy]]--;
4602 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
4603 Feld[newx][newy] == EL_BD_AMOEBA)
4604 AmoebaCnt[AmoebaNr[newx][newy]]--;
4607 if (IS_MOVING(newx, newy))
4608 RemoveMovingField(newx, newy);
4611 Feld[newx][newy] = EL_EMPTY;
4612 DrawLevelField(newx, newy);
4615 PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
4617 else if ((element == EL_PACMAN || element == EL_MOLE)
4618 && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
4620 if (AmoebaNr[newx][newy])
4622 AmoebaCnt2[AmoebaNr[newx][newy]]--;
4623 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
4624 Feld[newx][newy] == EL_BD_AMOEBA)
4625 AmoebaCnt[AmoebaNr[newx][newy]]--;
4628 if (element == EL_MOLE)
4630 Feld[newx][newy] = EL_AMOEBA_SHRINKING;
4631 PlayLevelSound(x, y, SND_MOLE_DIGGING);
4633 ResetGfxAnimation(x, y);
4634 GfxAction[x][y] = ACTION_DIGGING;
4635 DrawLevelField(x, y);
4637 MovDelay[newx][newy] = 0; /* start amoeba shrinking delay */
4638 return; /* wait for shrinking amoeba */
4640 else /* element == EL_PACMAN */
4642 Feld[newx][newy] = EL_EMPTY;
4643 DrawLevelField(newx, newy);
4644 PlayLevelSound(x, y, SND_PACMAN_DIGGING);
4647 else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
4648 (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
4649 (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
4651 /* wait for shrinking amoeba to completely disappear */
4654 else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
4656 /* object was running against a wall */
4661 if (GFX_ELEMENT(element) != EL_SAND) /* !!! FIX THIS (crumble) !!! */
4662 DrawLevelElementAnimation(x, y, element);
4664 if (element == EL_BUG ||
4665 element == EL_SPACESHIP ||
4666 element == EL_SP_SNIKSNAK)
4667 DrawLevelField(x, y);
4668 else if (element == EL_MOLE)
4669 DrawLevelField(x, y);
4670 else if (element == EL_BD_BUTTERFLY ||
4671 element == EL_BD_FIREFLY)
4672 DrawLevelElementAnimationIfNeeded(x, y, element);
4673 else if (element == EL_SATELLITE)
4674 DrawLevelElementAnimationIfNeeded(x, y, element);
4675 else if (element == EL_SP_ELECTRON)
4676 DrawLevelElementAnimationIfNeeded(x, y, element);
4679 if (DONT_TOUCH(element))
4680 TestIfBadThingTouchesHero(x, y);
4683 PlayLevelSoundAction(x, y, ACTION_WAITING);
4689 InitMovingField(x, y, MovDir[x][y]);
4691 PlayLevelSoundAction(x, y, ACTION_MOVING);
4695 ContinueMoving(x, y);
4698 void ContinueMoving(int x, int y)
4700 int element = Feld[x][y];
4701 int direction = MovDir[x][y];
4702 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
4703 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
4704 int newx = x + dx, newy = y + dy;
4705 int nextx = newx + dx, nexty = newy + dy;
4706 boolean pushed = Pushed[x][y];
4708 MovPos[x][y] += getElementMoveStepsize(x, y);
4710 if (pushed) /* special case: moving object pushed by player */
4711 MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
4713 if (ABS(MovPos[x][y]) < TILEX)
4715 DrawLevelField(x, y);
4717 return; /* element is still moving */
4720 /* element reached destination field */
4722 Feld[x][y] = EL_EMPTY;
4723 Feld[newx][newy] = element;
4724 MovPos[x][y] = 0; /* force "not moving" for "crumbled sand" */
4726 if (element == EL_MOLE)
4728 Feld[x][y] = EL_SAND;
4730 DrawLevelFieldCrumbledSandNeighbours(x, y);
4732 else if (element == EL_QUICKSAND_FILLING)
4734 element = Feld[newx][newy] = get_next_element(element);
4735 Store[newx][newy] = Store[x][y];
4737 else if (element == EL_QUICKSAND_EMPTYING)
4739 Feld[x][y] = get_next_element(element);
4740 element = Feld[newx][newy] = Store[x][y];
4742 else if (element == EL_MAGIC_WALL_FILLING)
4744 element = Feld[newx][newy] = get_next_element(element);
4745 if (!game.magic_wall_active)
4746 element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
4747 Store[newx][newy] = Store[x][y];
4749 else if (element == EL_MAGIC_WALL_EMPTYING)
4751 Feld[x][y] = get_next_element(element);
4752 if (!game.magic_wall_active)
4753 Feld[x][y] = EL_MAGIC_WALL_DEAD;
4754 element = Feld[newx][newy] = Store[x][y];
4756 else if (element == EL_BD_MAGIC_WALL_FILLING)
4758 element = Feld[newx][newy] = get_next_element(element);
4759 if (!game.magic_wall_active)
4760 element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
4761 Store[newx][newy] = Store[x][y];
4763 else if (element == EL_BD_MAGIC_WALL_EMPTYING)
4765 Feld[x][y] = get_next_element(element);
4766 if (!game.magic_wall_active)
4767 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
4768 element = Feld[newx][newy] = Store[x][y];
4770 else if (element == EL_AMOEBA_DROPPING)
4772 Feld[x][y] = get_next_element(element);
4773 element = Feld[newx][newy] = Store[x][y];
4775 else if (element == EL_SOKOBAN_OBJECT)
4778 Feld[x][y] = Back[x][y];
4780 if (Back[newx][newy])
4781 Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
4783 Back[x][y] = Back[newx][newy] = 0;
4785 else if (Store[x][y] == EL_ACID)
4787 element = Feld[newx][newy] = EL_ACID;
4791 MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
4792 MovDelay[newx][newy] = 0;
4794 /* copy element change control values to new field */
4795 ChangeDelay[newx][newy] = ChangeDelay[x][y];
4796 ChangePage[newx][newy] = ChangePage[x][y];
4797 Changed[newx][newy] = Changed[x][y];
4798 ChangeEvent[newx][newy] = ChangeEvent[x][y];
4800 ChangeDelay[x][y] = 0;
4801 ChangePage[x][y] = -1;
4802 Changed[x][y] = CE_BITMASK_DEFAULT;
4803 ChangeEvent[x][y] = CE_BITMASK_DEFAULT;
4805 /* copy animation control values to new field */
4806 GfxFrame[newx][newy] = GfxFrame[x][y];
4807 GfxRandom[newx][newy] = GfxRandom[x][y]; /* keep same random value */
4808 GfxAction[newx][newy] = GfxAction[x][y]; /* keep action one frame */
4809 GfxDir[newx][newy] = GfxDir[x][y]; /* keep element direction */
4811 Pushed[x][y] = Pushed[newx][newy] = FALSE;
4813 ResetGfxAnimation(x, y); /* reset animation values for old field */
4816 /* 2.1.1 (does not work correctly for spring) */
4817 if (!CAN_MOVE(element))
4818 MovDir[newx][newy] = 0;
4822 /* (does not work for falling objects that slide horizontally) */
4823 if (CAN_FALL(element) && MovDir[newx][newy] == MV_DOWN)
4824 MovDir[newx][newy] = 0;
4827 if (!CAN_MOVE(element) ||
4828 (element == EL_SPRING && MovDir[newx][newy] == MV_DOWN))
4829 MovDir[newx][newy] = 0;
4832 if (!CAN_MOVE(element) ||
4833 (CAN_FALL(element) && direction == MV_DOWN))
4834 GfxDir[x][y] = MovDir[newx][newy] = 0;
4839 DrawLevelField(x, y);
4840 DrawLevelField(newx, newy);
4842 Stop[newx][newy] = TRUE; /* ignore this element until the next frame */
4844 /* prevent pushed element from moving on in pushed direction */
4845 if (pushed && CAN_MOVE(element) &&
4846 element_info[element].move_pattern & MV_ANY_DIRECTION &&
4847 !(element_info[element].move_pattern & direction))
4848 TurnRound(newx, newy);
4850 if (!pushed) /* special case: moving object pushed by player */
4852 WasJustMoving[newx][newy] = 3;
4854 if (CAN_FALL(element) && direction == MV_DOWN)
4855 WasJustFalling[newx][newy] = 3;
4858 if (DONT_TOUCH(element)) /* object may be nasty to player or others */
4860 TestIfBadThingTouchesHero(newx, newy);
4861 TestIfBadThingTouchesFriend(newx, newy);
4863 if (!IS_CUSTOM_ELEMENT(element))
4864 TestIfBadThingTouchesOtherBadThing(newx, newy);
4866 else if (element == EL_PENGUIN)
4867 TestIfFriendTouchesBadThing(newx, newy);
4869 if (CAN_FALL(element) && direction == MV_DOWN &&
4870 (newy == lev_fieldy - 1 || !IS_FREE(x, newy + 1)))
4874 TestIfElementTouchesCustomElement(x, y); /* for empty space */
4878 if (ChangePage[newx][newy] != -1) /* delayed change */
4879 ChangeElement(newx, newy, ChangePage[newx][newy]);
4882 if (!IN_LEV_FIELD(nextx, nexty) || !IS_FREE(nextx, nexty))
4884 /* !!! fix side (direction) orientation here and elsewhere !!! */
4885 CheckElementSideChange(newx, newy, Feld[newx][newy],
4886 direction, CE_COLLISION_ACTIVE, -1);
4889 if (IN_LEV_FIELD(nextx, nexty))
4891 static int opposite_directions[] =
4898 int move_dir_bit = MV_DIR_BIT(direction);
4899 int opposite_direction = opposite_directions[move_dir_bit];
4900 int hitting_side = direction;
4901 int touched_side = opposite_direction;
4902 int hitting_element = Feld[newx][newy];
4903 int touched_element = MovingOrBlocked2Element(nextx, nexty);
4904 boolean object_hit = (!IS_MOVING(nextx, nexty) ||
4905 MovDir[nextx][nexty] != direction ||
4906 ABS(MovPos[nextx][nexty]) <= TILEY / 2);
4912 CheckElementSideChange(nextx, nexty, Feld[nextx][nexty],
4913 opposite_direction, CE_COLLISION_PASSIVE, -1);
4915 if (IS_CUSTOM_ELEMENT(hitting_element) &&
4916 HAS_ANY_CHANGE_EVENT(hitting_element, CE_OTHER_IS_COLL_ACTIVE))
4918 for (i = 0; i < element_info[hitting_element].num_change_pages; i++)
4920 struct ElementChangeInfo *change =
4921 &element_info[hitting_element].change_page[i];
4923 if (change->can_change &&
4924 change->events & CH_EVENT_BIT(CE_OTHER_IS_COLL_ACTIVE) &&
4925 change->sides & touched_side &&
4926 change->trigger_element == touched_element)
4928 CheckElementSideChange(newx, newy, hitting_element,
4929 CH_SIDE_ANY, CE_OTHER_IS_COLL_ACTIVE, i);
4935 if (IS_CUSTOM_ELEMENT(touched_element) &&
4936 HAS_ANY_CHANGE_EVENT(touched_element, CE_OTHER_IS_COLL_PASSIVE))
4938 for (i = 0; i < element_info[touched_element].num_change_pages; i++)
4940 struct ElementChangeInfo *change =
4941 &element_info[touched_element].change_page[i];
4943 if (change->can_change &&
4944 change->events & CH_EVENT_BIT(CE_OTHER_IS_COLL_PASSIVE) &&
4945 change->sides & hitting_side &&
4946 change->trigger_element == hitting_element)
4948 CheckElementSideChange(nextx, nexty, touched_element,
4949 CH_SIDE_ANY, CE_OTHER_IS_COLL_PASSIVE, i);
4959 TestIfPlayerTouchesCustomElement(newx, newy);
4960 TestIfElementTouchesCustomElement(newx, newy);
4963 int AmoebeNachbarNr(int ax, int ay)
4966 int element = Feld[ax][ay];
4968 static int xy[4][2] =
4976 for (i = 0; i < 4; i++)
4978 int x = ax + xy[i][0];
4979 int y = ay + xy[i][1];
4981 if (!IN_LEV_FIELD(x, y))
4984 if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
4985 group_nr = AmoebaNr[x][y];
4991 void AmoebenVereinigen(int ax, int ay)
4993 int i, x, y, xx, yy;
4994 int new_group_nr = AmoebaNr[ax][ay];
4995 static int xy[4][2] =
5003 if (new_group_nr == 0)
5006 for (i = 0; i < 4; i++)
5011 if (!IN_LEV_FIELD(x, y))
5014 if ((Feld[x][y] == EL_AMOEBA_FULL ||
5015 Feld[x][y] == EL_BD_AMOEBA ||
5016 Feld[x][y] == EL_AMOEBA_DEAD) &&
5017 AmoebaNr[x][y] != new_group_nr)
5019 int old_group_nr = AmoebaNr[x][y];
5021 if (old_group_nr == 0)
5024 AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
5025 AmoebaCnt[old_group_nr] = 0;
5026 AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
5027 AmoebaCnt2[old_group_nr] = 0;
5029 for (yy = 0; yy < lev_fieldy; yy++)
5031 for (xx = 0; xx < lev_fieldx; xx++)
5033 if (AmoebaNr[xx][yy] == old_group_nr)
5034 AmoebaNr[xx][yy] = new_group_nr;
5041 void AmoebeUmwandeln(int ax, int ay)
5045 if (Feld[ax][ay] == EL_AMOEBA_DEAD)
5047 int group_nr = AmoebaNr[ax][ay];
5052 printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
5053 printf("AmoebeUmwandeln(): This should never happen!\n");
5058 for (y = 0; y < lev_fieldy; y++)
5060 for (x = 0; x < lev_fieldx; x++)
5062 if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
5065 Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
5069 PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
5070 SND_AMOEBA_TURNING_TO_GEM :
5071 SND_AMOEBA_TURNING_TO_ROCK));
5076 static int xy[4][2] =
5084 for (i = 0; i < 4; i++)
5089 if (!IN_LEV_FIELD(x, y))
5092 if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
5094 PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
5095 SND_AMOEBA_TURNING_TO_GEM :
5096 SND_AMOEBA_TURNING_TO_ROCK));
5103 void AmoebeUmwandelnBD(int ax, int ay, int new_element)
5106 int group_nr = AmoebaNr[ax][ay];
5107 boolean done = FALSE;
5112 printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
5113 printf("AmoebeUmwandelnBD(): This should never happen!\n");
5118 for (y = 0; y < lev_fieldy; y++)
5120 for (x = 0; x < lev_fieldx; x++)
5122 if (AmoebaNr[x][y] == group_nr &&
5123 (Feld[x][y] == EL_AMOEBA_DEAD ||
5124 Feld[x][y] == EL_BD_AMOEBA ||
5125 Feld[x][y] == EL_AMOEBA_GROWING))
5128 Feld[x][y] = new_element;
5129 InitField(x, y, FALSE);
5130 DrawLevelField(x, y);
5137 PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
5138 SND_BD_AMOEBA_TURNING_TO_ROCK :
5139 SND_BD_AMOEBA_TURNING_TO_GEM));
5142 void AmoebeWaechst(int x, int y)
5144 static unsigned long sound_delay = 0;
5145 static unsigned long sound_delay_value = 0;
5147 if (!MovDelay[x][y]) /* start new growing cycle */
5151 if (DelayReached(&sound_delay, sound_delay_value))
5154 PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
5156 if (Store[x][y] == EL_BD_AMOEBA)
5157 PlayLevelSound(x, y, SND_BD_AMOEBA_GROWING);
5159 PlayLevelSound(x, y, SND_AMOEBA_GROWING);
5161 sound_delay_value = 30;
5165 if (MovDelay[x][y]) /* wait some time before growing bigger */
5168 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
5170 int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
5171 6 - MovDelay[x][y]);
5173 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
5176 if (!MovDelay[x][y])
5178 Feld[x][y] = Store[x][y];
5180 DrawLevelField(x, y);
5185 void AmoebaDisappearing(int x, int y)
5187 static unsigned long sound_delay = 0;
5188 static unsigned long sound_delay_value = 0;
5190 if (!MovDelay[x][y]) /* start new shrinking cycle */
5194 if (DelayReached(&sound_delay, sound_delay_value))
5195 sound_delay_value = 30;
5198 if (MovDelay[x][y]) /* wait some time before shrinking */
5201 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
5203 int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
5204 6 - MovDelay[x][y]);
5206 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
5209 if (!MovDelay[x][y])
5211 Feld[x][y] = EL_EMPTY;
5212 DrawLevelField(x, y);
5214 /* don't let mole enter this field in this cycle;
5215 (give priority to objects falling to this field from above) */
5221 void AmoebeAbleger(int ax, int ay)
5224 int element = Feld[ax][ay];
5225 int graphic = el2img(element);
5226 int newax = ax, neway = ay;
5227 static int xy[4][2] =
5235 if (!level.amoeba_speed)
5237 Feld[ax][ay] = EL_AMOEBA_DEAD;
5238 DrawLevelField(ax, ay);
5242 if (IS_ANIMATED(graphic))
5243 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
5245 if (!MovDelay[ax][ay]) /* start making new amoeba field */
5246 MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
5248 if (MovDelay[ax][ay]) /* wait some time before making new amoeba */
5251 if (MovDelay[ax][ay])
5255 if (element == EL_AMOEBA_WET) /* object is an acid / amoeba drop */
5258 int x = ax + xy[start][0];
5259 int y = ay + xy[start][1];
5261 if (!IN_LEV_FIELD(x, y))
5264 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
5265 if (IS_FREE(x, y) ||
5266 Feld[x][y] == EL_SAND || Feld[x][y] == EL_QUICKSAND_EMPTY)
5272 if (newax == ax && neway == ay)
5275 else /* normal or "filled" (BD style) amoeba */
5278 boolean waiting_for_player = FALSE;
5280 for (i = 0; i < 4; i++)
5282 int j = (start + i) % 4;
5283 int x = ax + xy[j][0];
5284 int y = ay + xy[j][1];
5286 if (!IN_LEV_FIELD(x, y))
5289 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
5290 if (IS_FREE(x, y) ||
5291 Feld[x][y] == EL_SAND || Feld[x][y] == EL_QUICKSAND_EMPTY)
5297 else if (IS_PLAYER(x, y))
5298 waiting_for_player = TRUE;
5301 if (newax == ax && neway == ay) /* amoeba cannot grow */
5303 if (i == 4 && (!waiting_for_player || game.emulation == EMU_BOULDERDASH))
5305 Feld[ax][ay] = EL_AMOEBA_DEAD;
5306 DrawLevelField(ax, ay);
5307 AmoebaCnt[AmoebaNr[ax][ay]]--;
5309 if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0) /* amoeba is completely dead */
5311 if (element == EL_AMOEBA_FULL)
5312 AmoebeUmwandeln(ax, ay);
5313 else if (element == EL_BD_AMOEBA)
5314 AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
5319 else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
5321 /* amoeba gets larger by growing in some direction */
5323 int new_group_nr = AmoebaNr[ax][ay];
5326 if (new_group_nr == 0)
5328 printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
5329 printf("AmoebeAbleger(): This should never happen!\n");
5334 AmoebaNr[newax][neway] = new_group_nr;
5335 AmoebaCnt[new_group_nr]++;
5336 AmoebaCnt2[new_group_nr]++;
5338 /* if amoeba touches other amoeba(s) after growing, unify them */
5339 AmoebenVereinigen(newax, neway);
5341 if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
5343 AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
5349 if (element != EL_AMOEBA_WET || neway < ay || !IS_FREE(newax, neway) ||
5350 (neway == lev_fieldy - 1 && newax != ax))
5352 Feld[newax][neway] = EL_AMOEBA_GROWING; /* creation of new amoeba */
5353 Store[newax][neway] = element;
5355 else if (neway == ay)
5357 Feld[newax][neway] = EL_AMOEBA_DROP; /* drop left/right of amoeba */
5359 PlayLevelSoundAction(newax, neway, ACTION_GROWING);
5361 PlayLevelSound(newax, neway, SND_AMOEBA_GROWING);
5366 InitMovingField(ax, ay, MV_DOWN); /* drop dripping from amoeba */
5367 Feld[ax][ay] = EL_AMOEBA_DROPPING;
5368 Store[ax][ay] = EL_AMOEBA_DROP;
5369 ContinueMoving(ax, ay);
5373 DrawLevelField(newax, neway);
5376 void Life(int ax, int ay)
5379 static int life[4] = { 2, 3, 3, 3 }; /* parameters for "game of life" */
5381 int element = Feld[ax][ay];
5382 int graphic = el2img(element);
5383 boolean changed = FALSE;
5385 if (IS_ANIMATED(graphic))
5386 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
5391 if (!MovDelay[ax][ay]) /* start new "game of life" cycle */
5392 MovDelay[ax][ay] = life_time;
5394 if (MovDelay[ax][ay]) /* wait some time before next cycle */
5397 if (MovDelay[ax][ay])
5401 for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
5403 int xx = ax+x1, yy = ay+y1;
5406 if (!IN_LEV_FIELD(xx, yy))
5409 for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
5411 int x = xx+x2, y = yy+y2;
5413 if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
5416 if (((Feld[x][y] == element ||
5417 (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y))) &&
5419 (IS_FREE(x, y) && Stop[x][y]))
5423 if (xx == ax && yy == ay) /* field in the middle */
5425 if (nachbarn < life[0] || nachbarn > life[1])
5427 Feld[xx][yy] = EL_EMPTY;
5429 DrawLevelField(xx, yy);
5430 Stop[xx][yy] = TRUE;
5434 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
5435 else if (IS_FREE(xx, yy) || Feld[xx][yy] == EL_SAND)
5436 { /* free border field */
5437 if (nachbarn >= life[2] && nachbarn <= life[3])
5439 Feld[xx][yy] = element;
5440 MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
5442 DrawLevelField(xx, yy);
5443 Stop[xx][yy] = TRUE;
5450 PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
5451 SND_GAME_OF_LIFE_GROWING);
5454 static void InitRobotWheel(int x, int y)
5456 ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
5459 static void RunRobotWheel(int x, int y)
5461 PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
5464 static void StopRobotWheel(int x, int y)
5466 if (ZX == x && ZY == y)
5470 static void InitTimegateWheel(int x, int y)
5472 ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
5475 static void RunTimegateWheel(int x, int y)
5477 PlayLevelSound(x, y, SND_TIMEGATE_SWITCH_ACTIVE);
5480 void CheckExit(int x, int y)
5482 if (local_player->gems_still_needed > 0 ||
5483 local_player->sokobanfields_still_needed > 0 ||
5484 local_player->lights_still_needed > 0)
5486 int element = Feld[x][y];
5487 int graphic = el2img(element);
5489 if (IS_ANIMATED(graphic))
5490 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
5495 if (AllPlayersGone) /* do not re-open exit door closed after last player */
5498 Feld[x][y] = EL_EXIT_OPENING;
5500 PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
5503 void CheckExitSP(int x, int y)
5505 if (local_player->gems_still_needed > 0)
5507 int element = Feld[x][y];
5508 int graphic = el2img(element);
5510 if (IS_ANIMATED(graphic))
5511 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
5516 if (AllPlayersGone) /* do not re-open exit door closed after last player */
5519 Feld[x][y] = EL_SP_EXIT_OPENING;
5521 PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
5524 static void CloseAllOpenTimegates()
5528 for (y = 0; y < lev_fieldy; y++)
5530 for (x = 0; x < lev_fieldx; x++)
5532 int element = Feld[x][y];
5534 if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
5536 Feld[x][y] = EL_TIMEGATE_CLOSING;
5538 PlayLevelSoundAction(x, y, ACTION_CLOSING);
5540 PlayLevelSound(x, y, SND_TIMEGATE_CLOSING);
5547 void EdelsteinFunkeln(int x, int y)
5549 if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
5552 if (Feld[x][y] == EL_BD_DIAMOND)
5555 if (MovDelay[x][y] == 0) /* next animation frame */
5556 MovDelay[x][y] = 11 * !SimpleRND(500);
5558 if (MovDelay[x][y] != 0) /* wait some time before next frame */
5562 if (setup.direct_draw && MovDelay[x][y])
5563 SetDrawtoField(DRAW_BUFFERED);
5565 DrawLevelElementAnimation(x, y, Feld[x][y]);
5567 if (MovDelay[x][y] != 0)
5569 int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
5570 10 - MovDelay[x][y]);
5572 DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
5574 if (setup.direct_draw)
5578 dest_x = FX + SCREENX(x) * TILEX;
5579 dest_y = FY + SCREENY(y) * TILEY;
5581 BlitBitmap(drawto_field, window,
5582 dest_x, dest_y, TILEX, TILEY, dest_x, dest_y);
5583 SetDrawtoField(DRAW_DIRECT);
5589 void MauerWaechst(int x, int y)
5593 if (!MovDelay[x][y]) /* next animation frame */
5594 MovDelay[x][y] = 3 * delay;
5596 if (MovDelay[x][y]) /* wait some time before next frame */
5600 if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
5602 int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
5603 int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
5605 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5608 if (!MovDelay[x][y])
5610 if (MovDir[x][y] == MV_LEFT)
5612 if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
5613 DrawLevelField(x - 1, y);
5615 else if (MovDir[x][y] == MV_RIGHT)
5617 if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
5618 DrawLevelField(x + 1, y);
5620 else if (MovDir[x][y] == MV_UP)
5622 if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
5623 DrawLevelField(x, y - 1);
5627 if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
5628 DrawLevelField(x, y + 1);
5631 Feld[x][y] = Store[x][y];
5633 GfxDir[x][y] = MovDir[x][y] = MV_NO_MOVING;
5634 DrawLevelField(x, y);
5639 void MauerAbleger(int ax, int ay)
5641 int element = Feld[ax][ay];
5642 int graphic = el2img(element);
5643 boolean oben_frei = FALSE, unten_frei = FALSE;
5644 boolean links_frei = FALSE, rechts_frei = FALSE;
5645 boolean oben_massiv = FALSE, unten_massiv = FALSE;
5646 boolean links_massiv = FALSE, rechts_massiv = FALSE;
5647 boolean new_wall = FALSE;
5649 if (IS_ANIMATED(graphic))
5650 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
5652 if (!MovDelay[ax][ay]) /* start building new wall */
5653 MovDelay[ax][ay] = 6;
5655 if (MovDelay[ax][ay]) /* wait some time before building new wall */
5658 if (MovDelay[ax][ay])
5662 if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
5664 if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
5666 if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
5668 if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
5671 if (element == EL_EXPANDABLE_WALL_VERTICAL ||
5672 element == EL_EXPANDABLE_WALL_ANY)
5676 Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
5677 Store[ax][ay-1] = element;
5678 GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
5679 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
5680 DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
5681 IMG_EXPANDABLE_WALL_GROWING_UP, 0);
5686 Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
5687 Store[ax][ay+1] = element;
5688 GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
5689 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
5690 DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
5691 IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
5696 if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
5697 element == EL_EXPANDABLE_WALL_ANY ||
5698 element == EL_EXPANDABLE_WALL)
5702 Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
5703 Store[ax-1][ay] = element;
5704 GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
5705 if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
5706 DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
5707 IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
5713 Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
5714 Store[ax+1][ay] = element;
5715 GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
5716 if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
5717 DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
5718 IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
5723 if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
5724 DrawLevelField(ax, ay);
5726 if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
5728 if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
5729 unten_massiv = TRUE;
5730 if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
5731 links_massiv = TRUE;
5732 if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
5733 rechts_massiv = TRUE;
5735 if (((oben_massiv && unten_massiv) ||
5736 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
5737 element == EL_EXPANDABLE_WALL) &&
5738 ((links_massiv && rechts_massiv) ||
5739 element == EL_EXPANDABLE_WALL_VERTICAL))
5740 Feld[ax][ay] = EL_WALL;
5744 PlayLevelSoundAction(ax, ay, ACTION_GROWING);
5746 PlayLevelSound(ax, ay, SND_EXPANDABLE_WALL_GROWING);
5750 void CheckForDragon(int x, int y)
5753 boolean dragon_found = FALSE;
5754 static int xy[4][2] =
5762 for (i = 0; i < 4; i++)
5764 for (j = 0; j < 4; j++)
5766 int xx = x + j*xy[i][0], yy = y + j*xy[i][1];
5768 if (IN_LEV_FIELD(xx, yy) &&
5769 (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
5771 if (Feld[xx][yy] == EL_DRAGON)
5772 dragon_found = TRUE;
5781 for (i = 0; i < 4; i++)
5783 for (j = 0; j < 3; j++)
5785 int xx = x + j*xy[i][0], yy = y + j*xy[i][1];
5787 if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
5789 Feld[xx][yy] = EL_EMPTY;
5790 DrawLevelField(xx, yy);
5799 static void InitBuggyBase(int x, int y)
5801 int element = Feld[x][y];
5802 int activating_delay = FRAMES_PER_SECOND / 4;
5805 (element == EL_SP_BUGGY_BASE ?
5806 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
5807 element == EL_SP_BUGGY_BASE_ACTIVATING ?
5809 element == EL_SP_BUGGY_BASE_ACTIVE ?
5810 1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
5813 static void WarnBuggyBase(int x, int y)
5816 static int xy[4][2] =
5824 for (i = 0; i < 4; i++)
5826 int xx = x + xy[i][0], yy = y + xy[i][1];
5828 if (IS_PLAYER(xx, yy))
5830 PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
5837 static void InitTrap(int x, int y)
5839 ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
5842 static void ActivateTrap(int x, int y)
5844 PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
5847 static void ChangeActiveTrap(int x, int y)
5849 int graphic = IMG_TRAP_ACTIVE;
5851 /* if new animation frame was drawn, correct crumbled sand border */
5852 if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
5853 DrawLevelFieldCrumbledSand(x, y);
5856 static void ChangeElementNowExt(int x, int y, int target_element)
5858 /* check if element under player changes from accessible to unaccessible
5859 (needed for special case of dropping element which then changes) */
5860 if (IS_PLAYER(x, y) && !PLAYER_PROTECTED(x, y) &&
5861 IS_ACCESSIBLE(Feld[x][y]) && !IS_ACCESSIBLE(target_element))
5868 Feld[x][y] = target_element;
5870 Changed[x][y] |= ChangeEvent[x][y]; /* ignore same changes in this frame */
5872 ResetGfxAnimation(x, y);
5873 ResetRandomAnimationValue(x, y);
5875 InitField(x, y, FALSE);
5876 if (CAN_MOVE(Feld[x][y]))
5879 DrawLevelField(x, y);
5881 if (GFX_CRUMBLED(Feld[x][y]))
5882 DrawLevelFieldCrumbledSandNeighbours(x, y);
5884 TestIfBadThingTouchesHero(x, y);
5885 TestIfPlayerTouchesCustomElement(x, y);
5886 TestIfElementTouchesCustomElement(x, y);
5888 if (ELEM_IS_PLAYER(target_element))
5889 RelocatePlayer(x, y, target_element);
5892 static boolean ChangeElementNow(int x, int y, int element, int page)
5894 struct ElementChangeInfo *change = &element_info[element].change_page[page];
5896 /* always use default change event to prevent running into a loop */
5897 if (ChangeEvent[x][y] == CE_BITMASK_DEFAULT)
5898 ChangeEvent[x][y] = CH_EVENT_BIT(CE_DELAY);
5900 /* do not change already changed elements with same change event */
5902 if (Changed[x][y] & ChangeEvent[x][y])
5909 Changed[x][y] |= ChangeEvent[x][y]; /* ignore same changes in this frame */
5911 CheckTriggeredElementChange(x, y, Feld[x][y], CE_OTHER_IS_CHANGING);
5913 if (change->explode)
5920 if (change->use_content)
5922 boolean complete_change = TRUE;
5923 boolean can_change[3][3];
5926 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
5928 boolean half_destructible;
5929 int ex = x + xx - 1;
5930 int ey = y + yy - 1;
5933 can_change[xx][yy] = TRUE;
5935 if (ex == x && ey == y) /* do not check changing element itself */
5938 if (change->content[xx][yy] == EL_EMPTY_SPACE)
5940 can_change[xx][yy] = FALSE; /* do not change empty borders */
5945 if (!IN_LEV_FIELD(ex, ey))
5947 can_change[xx][yy] = FALSE;
5948 complete_change = FALSE;
5955 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
5956 e = MovingOrBlocked2Element(ex, ey);
5958 half_destructible = (IS_FREE(ex, ey) || IS_DIGGABLE(e));
5960 if ((change->power <= CP_NON_DESTRUCTIVE && !IS_FREE(ex, ey)) ||
5961 (change->power <= CP_HALF_DESTRUCTIVE && !half_destructible) ||
5962 (change->power <= CP_FULL_DESTRUCTIVE && IS_INDESTRUCTIBLE(e)))
5964 can_change[xx][yy] = FALSE;
5965 complete_change = FALSE;
5969 if (!change->only_complete || complete_change)
5971 boolean something_has_changed = FALSE;
5973 if (change->only_complete && change->use_random_change &&
5974 RND(100) < change->random)
5977 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
5979 int ex = x + xx - 1;
5980 int ey = y + yy - 1;
5982 if (can_change[xx][yy] && (!change->use_random_change ||
5983 RND(100) < change->random))
5985 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
5986 RemoveMovingField(ex, ey);
5988 ChangeEvent[ex][ey] = ChangeEvent[x][y];
5990 ChangeElementNowExt(ex, ey, change->content[xx][yy]);
5992 something_has_changed = TRUE;
5994 /* for symmetry reasons, freeze newly created border elements */
5995 if (ex != x || ey != y)
5996 Stop[ex][ey] = TRUE; /* no more moving in this frame */
6000 if (something_has_changed)
6001 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
6006 ChangeElementNowExt(x, y, change->target_element);
6008 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
6014 static void ChangeElement(int x, int y, int page)
6016 int element = MovingOrBlocked2Element(x, y);
6017 struct ElementInfo *ei = &element_info[element];
6018 struct ElementChangeInfo *change = &ei->change_page[page];
6022 if (!CAN_CHANGE(element))
6025 printf("ChangeElement(): %d,%d: element = %d ('%s')\n",
6026 x, y, element, element_info[element].token_name);
6027 printf("ChangeElement(): This should never happen!\n");
6033 if (ChangeDelay[x][y] == 0) /* initialize element change */
6035 ChangeDelay[x][y] = ( change->delay_fixed * change->delay_frames +
6036 RND(change->delay_random * change->delay_frames)) + 1;
6038 ResetGfxAnimation(x, y);
6039 ResetRandomAnimationValue(x, y);
6041 if (change->pre_change_function)
6042 change->pre_change_function(x, y);
6045 ChangeDelay[x][y]--;
6047 if (ChangeDelay[x][y] != 0) /* continue element change */
6049 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
6051 if (IS_ANIMATED(graphic))
6052 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
6054 if (change->change_function)
6055 change->change_function(x, y);
6057 else /* finish element change */
6059 if (ChangePage[x][y] != -1) /* remember page from delayed change */
6061 page = ChangePage[x][y];
6062 ChangePage[x][y] = -1;
6065 if (IS_MOVING(x, y)) /* never change a running system ;-) */
6067 ChangeDelay[x][y] = 1; /* try change after next move step */
6068 ChangePage[x][y] = page; /* remember page to use for change */
6073 if (ChangeElementNow(x, y, element, page))
6075 if (change->post_change_function)
6076 change->post_change_function(x, y);
6081 static boolean CheckTriggeredElementSideChange(int lx, int ly,
6082 int trigger_element,
6088 if (!(trigger_events[trigger_element] & CH_EVENT_BIT(trigger_event)))
6091 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6093 int element = EL_CUSTOM_START + i;
6095 boolean change_element = FALSE;
6098 if (!CAN_CHANGE(element) || !HAS_ANY_CHANGE_EVENT(element, trigger_event))
6101 for (j = 0; j < element_info[element].num_change_pages; j++)
6103 struct ElementChangeInfo *change = &element_info[element].change_page[j];
6105 if (change->can_change &&
6107 change->events & CH_EVENT_BIT(trigger_event) &&
6109 change->sides & trigger_side &&
6110 change->trigger_element == trigger_element)
6113 if (!(change->events & CH_EVENT_BIT(trigger_event)))
6114 printf("::: !!! %d triggers %d: using wrong page %d [event %d]\n",
6115 trigger_element-EL_CUSTOM_START+1, i+1, j, trigger_event);
6118 change_element = TRUE;
6125 if (!change_element)
6128 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
6131 if (x == lx && y == ly) /* do not change trigger element itself */
6135 if (Feld[x][y] == element)
6137 ChangeDelay[x][y] = 1;
6138 ChangeEvent[x][y] = CH_EVENT_BIT(trigger_event);
6139 ChangeElement(x, y, page);
6147 static boolean CheckTriggeredElementChange(int lx, int ly, int trigger_element,
6150 return CheckTriggeredElementSideChange(lx, ly, trigger_element, CH_SIDE_ANY,
6154 static boolean CheckElementSideChange(int x, int y, int element, int side,
6155 int trigger_event, int page)
6157 if (!CAN_CHANGE(element) || !HAS_ANY_CHANGE_EVENT(element, trigger_event))
6160 if (Feld[x][y] == EL_BLOCKED)
6162 Blocked2Moving(x, y, &x, &y);
6163 element = Feld[x][y];
6167 page = element_info[element].event_page_nr[trigger_event];
6169 if (!(element_info[element].change_page[page].sides & side))
6172 ChangeDelay[x][y] = 1;
6173 ChangeEvent[x][y] = CH_EVENT_BIT(trigger_event);
6174 ChangeElement(x, y, page);
6179 static boolean CheckElementChange(int x, int y, int element, int trigger_event)
6181 return CheckElementSideChange(x, y, element, CH_SIDE_ANY, trigger_event, -1);
6184 static void PlayPlayerSound(struct PlayerInfo *player)
6186 int jx = player->jx, jy = player->jy;
6187 int element = player->element_nr;
6188 int last_action = player->last_action_waiting;
6189 int action = player->action_waiting;
6191 if (player->is_waiting)
6193 if (action != last_action)
6194 PlayLevelSoundElementAction(jx, jy, element, action);
6196 PlayLevelSoundElementActionIfLoop(jx, jy, element, action);
6200 if (action != last_action)
6201 StopSound(element_info[element].sound[last_action]);
6203 if (last_action == ACTION_SLEEPING)
6204 PlayLevelSoundElementAction(jx, jy, element, ACTION_AWAKENING);
6208 static void PlayAllPlayersSound()
6212 for (i = 0; i < MAX_PLAYERS; i++)
6213 if (stored_player[i].active)
6214 PlayPlayerSound(&stored_player[i]);
6217 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
6219 boolean last_waiting = player->is_waiting;
6220 int move_dir = player->MovDir;
6222 player->last_action_waiting = player->action_waiting;
6226 if (!last_waiting) /* not waiting -> waiting */
6228 player->is_waiting = TRUE;
6230 player->frame_counter_bored =
6232 game.player_boring_delay_fixed +
6233 SimpleRND(game.player_boring_delay_random);
6234 player->frame_counter_sleeping =
6236 game.player_sleeping_delay_fixed +
6237 SimpleRND(game.player_sleeping_delay_random);
6239 InitPlayerGfxAnimation(player, ACTION_WAITING, player->MovDir);
6242 if (game.player_sleeping_delay_fixed +
6243 game.player_sleeping_delay_random > 0 &&
6244 player->anim_delay_counter == 0 &&
6245 player->post_delay_counter == 0 &&
6246 FrameCounter >= player->frame_counter_sleeping)
6247 player->is_sleeping = TRUE;
6248 else if (game.player_boring_delay_fixed +
6249 game.player_boring_delay_random > 0 &&
6250 FrameCounter >= player->frame_counter_bored)
6251 player->is_bored = TRUE;
6253 player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
6254 player->is_bored ? ACTION_BORING :
6257 if (player->is_sleeping)
6259 if (player->num_special_action_sleeping > 0)
6261 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
6263 int last_special_action = player->special_action_sleeping;
6264 int num_special_action = player->num_special_action_sleeping;
6265 int special_action =
6266 (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
6267 last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
6268 last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
6269 last_special_action + 1 : ACTION_SLEEPING);
6270 int special_graphic =
6271 el_act_dir2img(player->element_nr, special_action, move_dir);
6273 player->anim_delay_counter =
6274 graphic_info[special_graphic].anim_delay_fixed +
6275 SimpleRND(graphic_info[special_graphic].anim_delay_random);
6276 player->post_delay_counter =
6277 graphic_info[special_graphic].post_delay_fixed +
6278 SimpleRND(graphic_info[special_graphic].post_delay_random);
6280 player->special_action_sleeping = special_action;
6283 if (player->anim_delay_counter > 0)
6285 player->action_waiting = player->special_action_sleeping;
6286 player->anim_delay_counter--;
6288 else if (player->post_delay_counter > 0)
6290 player->post_delay_counter--;
6294 else if (player->is_bored)
6296 if (player->num_special_action_bored > 0)
6298 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
6300 int special_action =
6301 ACTION_BORING_1 + SimpleRND(player->num_special_action_bored);
6302 int special_graphic =
6303 el_act_dir2img(player->element_nr, special_action, move_dir);
6305 player->anim_delay_counter =
6306 graphic_info[special_graphic].anim_delay_fixed +
6307 SimpleRND(graphic_info[special_graphic].anim_delay_random);
6308 player->post_delay_counter =
6309 graphic_info[special_graphic].post_delay_fixed +
6310 SimpleRND(graphic_info[special_graphic].post_delay_random);
6312 player->special_action_bored = special_action;
6315 if (player->anim_delay_counter > 0)
6317 player->action_waiting = player->special_action_bored;
6318 player->anim_delay_counter--;
6320 else if (player->post_delay_counter > 0)
6322 player->post_delay_counter--;
6327 else if (last_waiting) /* waiting -> not waiting */
6329 player->is_waiting = FALSE;
6330 player->is_bored = FALSE;
6331 player->is_sleeping = FALSE;
6333 player->frame_counter_bored = -1;
6334 player->frame_counter_sleeping = -1;
6336 player->anim_delay_counter = 0;
6337 player->post_delay_counter = 0;
6339 player->action_waiting = ACTION_DEFAULT;
6341 player->special_action_bored = ACTION_DEFAULT;
6342 player->special_action_sleeping = ACTION_DEFAULT;
6347 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
6350 static byte stored_player_action[MAX_PLAYERS];
6351 static int num_stored_actions = 0;
6353 boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
6354 int left = player_action & JOY_LEFT;
6355 int right = player_action & JOY_RIGHT;
6356 int up = player_action & JOY_UP;
6357 int down = player_action & JOY_DOWN;
6358 int button1 = player_action & JOY_BUTTON_1;
6359 int button2 = player_action & JOY_BUTTON_2;
6360 int dx = (left ? -1 : right ? 1 : 0);
6361 int dy = (up ? -1 : down ? 1 : 0);
6364 stored_player_action[player->index_nr] = 0;
6365 num_stored_actions++;
6369 printf("::: player %d [%d]\n", player->index_nr, FrameCounter);
6372 if (!player->active || tape.pausing)
6378 printf("::: player %d acts [%d]\n", player->index_nr, FrameCounter);
6382 snapped = SnapField(player, dx, dy);
6386 dropped = DropElement(player);
6388 moved = MovePlayer(player, dx, dy);
6391 if (tape.single_step && tape.recording && !tape.pausing)
6393 if (button1 || (dropped && !moved))
6395 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
6396 SnapField(player, 0, 0); /* stop snapping */
6400 SetPlayerWaiting(player, FALSE);
6403 return player_action;
6405 stored_player_action[player->index_nr] = player_action;
6411 printf("::: player %d waits [%d]\n", player->index_nr, FrameCounter);
6414 /* no actions for this player (no input at player's configured device) */
6416 DigField(player, 0, 0, 0, 0, DF_NO_PUSH);
6417 SnapField(player, 0, 0);
6418 CheckGravityMovement(player);
6420 if (player->MovPos == 0)
6421 SetPlayerWaiting(player, TRUE);
6423 if (player->MovPos == 0) /* needed for tape.playing */
6424 player->is_moving = FALSE;
6430 if (tape.recording && num_stored_actions >= MAX_PLAYERS)
6432 printf("::: player %d recorded [%d]\n", player->index_nr, FrameCounter);
6434 TapeRecordAction(stored_player_action);
6435 num_stored_actions = 0;
6442 static void PlayerActions(struct PlayerInfo *player, byte player_action)
6444 static byte stored_player_action[MAX_PLAYERS];
6445 static int num_stored_actions = 0;
6446 boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
6447 int left = player_action & JOY_LEFT;
6448 int right = player_action & JOY_RIGHT;
6449 int up = player_action & JOY_UP;
6450 int down = player_action & JOY_DOWN;
6451 int button1 = player_action & JOY_BUTTON_1;
6452 int button2 = player_action & JOY_BUTTON_2;
6453 int dx = (left ? -1 : right ? 1 : 0);
6454 int dy = (up ? -1 : down ? 1 : 0);
6456 stored_player_action[player->index_nr] = 0;
6457 num_stored_actions++;
6459 printf("::: player %d [%d]\n", player->index_nr, FrameCounter);
6461 if (!player->active || tape.pausing)
6466 printf("::: player %d acts [%d]\n", player->index_nr, FrameCounter);
6469 snapped = SnapField(player, dx, dy);
6473 dropped = DropElement(player);
6475 moved = MovePlayer(player, dx, dy);
6478 if (tape.single_step && tape.recording && !tape.pausing)
6480 if (button1 || (dropped && !moved))
6482 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
6483 SnapField(player, 0, 0); /* stop snapping */
6487 stored_player_action[player->index_nr] = player_action;
6491 printf("::: player %d waits [%d]\n", player->index_nr, FrameCounter);
6493 /* no actions for this player (no input at player's configured device) */
6495 DigField(player, 0, 0, 0, 0, DF_NO_PUSH);
6496 SnapField(player, 0, 0);
6497 CheckGravityMovement(player);
6499 if (player->MovPos == 0)
6500 InitPlayerGfxAnimation(player, ACTION_DEFAULT, player->MovDir);
6502 if (player->MovPos == 0) /* needed for tape.playing */
6503 player->is_moving = FALSE;
6506 if (tape.recording && num_stored_actions >= MAX_PLAYERS)
6508 printf("::: player %d recorded [%d]\n", player->index_nr, FrameCounter);
6510 TapeRecordAction(stored_player_action);
6511 num_stored_actions = 0;
6518 static unsigned long action_delay = 0;
6519 unsigned long action_delay_value;
6520 int magic_wall_x = 0, magic_wall_y = 0;
6521 int i, x, y, element, graphic;
6522 byte *recorded_player_action;
6523 byte summarized_player_action = 0;
6525 byte tape_action[MAX_PLAYERS];
6528 if (game_status != GAME_MODE_PLAYING)
6531 action_delay_value =
6532 (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
6534 if (tape.playing && tape.index_search && !tape.pausing)
6535 action_delay_value = 0;
6537 /* ---------- main game synchronization point ---------- */
6539 WaitUntilDelayReached(&action_delay, action_delay_value);
6541 if (network_playing && !network_player_action_received)
6545 printf("DEBUG: try to get network player actions in time\n");
6549 #if defined(PLATFORM_UNIX)
6550 /* last chance to get network player actions without main loop delay */
6554 if (game_status != GAME_MODE_PLAYING)
6557 if (!network_player_action_received)
6561 printf("DEBUG: failed to get network player actions in time\n");
6572 printf("::: getting new tape action [%d]\n", FrameCounter);
6575 recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
6577 for (i = 0; i < MAX_PLAYERS; i++)
6579 summarized_player_action |= stored_player[i].action;
6581 if (!network_playing)
6582 stored_player[i].effective_action = stored_player[i].action;
6585 #if defined(PLATFORM_UNIX)
6586 if (network_playing)
6587 SendToServer_MovePlayer(summarized_player_action);
6590 if (!options.network && !setup.team_mode)
6591 local_player->effective_action = summarized_player_action;
6593 for (i = 0; i < MAX_PLAYERS; i++)
6595 int actual_player_action = stored_player[i].effective_action;
6597 if (stored_player[i].programmed_action)
6598 actual_player_action = stored_player[i].programmed_action;
6600 if (recorded_player_action)
6601 actual_player_action = recorded_player_action[i];
6603 tape_action[i] = PlayerActions(&stored_player[i], actual_player_action);
6605 if (tape.recording && tape_action[i] && !tape.player_participates[i])
6606 tape.player_participates[i] = TRUE; /* player just appeared from CE */
6608 ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
6613 TapeRecordAction(tape_action);
6616 network_player_action_received = FALSE;
6618 ScrollScreen(NULL, SCROLL_GO_ON);
6624 for (i = 0; i < MAX_PLAYERS; i++)
6625 stored_player[i].Frame++;
6629 if (game.engine_version < VERSION_IDENT(2,2,0,7))
6631 for (i = 0; i < MAX_PLAYERS; i++)
6633 struct PlayerInfo *player = &stored_player[i];
6637 if (player->active && player->is_pushing && player->is_moving &&
6640 ContinueMoving(x, y);
6642 /* continue moving after pushing (this is actually a bug) */
6643 if (!IS_MOVING(x, y))
6652 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
6654 Changed[x][y] = CE_BITMASK_DEFAULT;
6655 ChangeEvent[x][y] = CE_BITMASK_DEFAULT;
6658 if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
6660 printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
6661 printf("GameActions(): This should never happen!\n");
6663 ChangePage[x][y] = -1;
6668 if (WasJustMoving[x][y] > 0)
6669 WasJustMoving[x][y]--;
6670 if (WasJustFalling[x][y] > 0)
6671 WasJustFalling[x][y]--;
6676 /* reset finished pushing action (not done in ContinueMoving() to allow
6677 continous pushing animation for elements with zero push delay) */
6678 if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
6680 ResetGfxAnimation(x, y);
6681 DrawLevelField(x, y);
6686 if (IS_BLOCKED(x, y))
6690 Blocked2Moving(x, y, &oldx, &oldy);
6691 if (!IS_MOVING(oldx, oldy))
6693 printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
6694 printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
6695 printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
6696 printf("GameActions(): This should never happen!\n");
6702 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
6704 element = Feld[x][y];
6706 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
6708 graphic = el2img(element);
6714 printf("::: %d,%d: %d [%d]\n", x, y, element, FrameCounter);
6716 element = graphic = 0;
6720 if (graphic_info[graphic].anim_global_sync)
6721 GfxFrame[x][y] = FrameCounter;
6723 if (ANIM_MODE(graphic) == ANIM_RANDOM &&
6724 IS_NEXT_FRAME(GfxFrame[x][y], graphic))
6725 ResetRandomAnimationValue(x, y);
6727 SetRandomAnimationValue(x, y);
6730 PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
6733 if (IS_INACTIVE(element))
6735 if (IS_ANIMATED(graphic))
6736 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
6742 /* this may take place after moving, so 'element' may have changed */
6744 if (IS_CHANGING(x, y))
6746 if (IS_CHANGING(x, y) &&
6747 (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
6751 ChangeElement(x, y, ChangePage[x][y] != -1 ? ChangePage[x][y] :
6752 element_info[element].event_page_nr[CE_DELAY]);
6754 ChangeElement(x, y, element_info[element].event_page_nr[CE_DELAY]);
6757 element = Feld[x][y];
6758 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
6762 if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
6767 element = Feld[x][y];
6768 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
6770 if (element == EL_MOLE)
6771 printf("::: %d, %d, %d [%d]\n",
6772 IS_ANIMATED(graphic), IS_MOVING(x, y), Stop[x][y],
6776 if (element == EL_YAMYAM)
6777 printf("::: %d, %d, %d\n",
6778 IS_ANIMATED(graphic), IS_MOVING(x, y), Stop[x][y]);
6782 if (IS_ANIMATED(graphic) &&
6786 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
6789 if (element == EL_BUG)
6790 printf("::: %d, %d\n", graphic, GfxFrame[x][y]);
6794 if (element == EL_MOLE)
6795 printf("::: %d, %d\n", graphic, GfxFrame[x][y]);
6799 if (IS_GEM(element) || element == EL_SP_INFOTRON)
6800 EdelsteinFunkeln(x, y);
6802 else if ((element == EL_ACID ||
6803 element == EL_EXIT_OPEN ||
6804 element == EL_SP_EXIT_OPEN ||
6805 element == EL_SP_TERMINAL ||
6806 element == EL_SP_TERMINAL_ACTIVE ||
6807 element == EL_EXTRA_TIME ||
6808 element == EL_SHIELD_NORMAL ||
6809 element == EL_SHIELD_DEADLY) &&
6810 IS_ANIMATED(graphic))
6811 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
6812 else if (IS_MOVING(x, y))
6813 ContinueMoving(x, y);
6814 else if (IS_ACTIVE_BOMB(element))
6815 CheckDynamite(x, y);
6817 else if (element == EL_EXPLOSION && !game.explosions_delayed)
6818 Explode(x, y, ExplodePhase[x][y], EX_NORMAL);
6820 else if (element == EL_AMOEBA_GROWING)
6821 AmoebeWaechst(x, y);
6822 else if (element == EL_AMOEBA_SHRINKING)
6823 AmoebaDisappearing(x, y);
6825 #if !USE_NEW_AMOEBA_CODE
6826 else if (IS_AMOEBALIVE(element))
6827 AmoebeAbleger(x, y);
6830 else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
6832 else if (element == EL_EXIT_CLOSED)
6834 else if (element == EL_SP_EXIT_CLOSED)
6836 else if (element == EL_EXPANDABLE_WALL_GROWING)
6838 else if (element == EL_EXPANDABLE_WALL ||
6839 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
6840 element == EL_EXPANDABLE_WALL_VERTICAL ||
6841 element == EL_EXPANDABLE_WALL_ANY)
6843 else if (element == EL_FLAMES)
6844 CheckForDragon(x, y);
6846 else if (IS_AUTO_CHANGING(element))
6847 ChangeElement(x, y);
6849 else if (element == EL_EXPLOSION)
6850 ; /* drawing of correct explosion animation is handled separately */
6851 else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
6852 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
6855 /* this may take place after moving, so 'element' may have changed */
6856 if (IS_AUTO_CHANGING(Feld[x][y]))
6857 ChangeElement(x, y);
6860 if (IS_BELT_ACTIVE(element))
6861 PlayLevelSoundAction(x, y, ACTION_ACTIVE);
6863 if (game.magic_wall_active)
6865 int jx = local_player->jx, jy = local_player->jy;
6867 /* play the element sound at the position nearest to the player */
6868 if ((element == EL_MAGIC_WALL_FULL ||
6869 element == EL_MAGIC_WALL_ACTIVE ||
6870 element == EL_MAGIC_WALL_EMPTYING ||
6871 element == EL_BD_MAGIC_WALL_FULL ||
6872 element == EL_BD_MAGIC_WALL_ACTIVE ||
6873 element == EL_BD_MAGIC_WALL_EMPTYING) &&
6874 ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
6882 #if USE_NEW_AMOEBA_CODE
6883 /* new experimental amoeba growth stuff */
6885 if (!(FrameCounter % 8))
6888 static unsigned long random = 1684108901;
6890 for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
6893 x = (random >> 10) % lev_fieldx;
6894 y = (random >> 20) % lev_fieldy;
6896 x = RND(lev_fieldx);
6897 y = RND(lev_fieldy);
6899 element = Feld[x][y];
6901 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
6902 if (!IS_PLAYER(x,y) &&
6903 (element == EL_EMPTY ||
6904 element == EL_SAND ||
6905 element == EL_QUICKSAND_EMPTY ||
6906 element == EL_ACID_SPLASH_LEFT ||
6907 element == EL_ACID_SPLASH_RIGHT))
6909 if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
6910 (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
6911 (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
6912 (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
6913 Feld[x][y] = EL_AMOEBA_DROP;
6916 random = random * 129 + 1;
6922 if (game.explosions_delayed)
6925 game.explosions_delayed = FALSE;
6927 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
6929 element = Feld[x][y];
6931 if (ExplodeField[x][y])
6932 Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
6933 else if (element == EL_EXPLOSION)
6934 Explode(x, y, ExplodePhase[x][y], EX_NORMAL);
6936 ExplodeField[x][y] = EX_NO_EXPLOSION;
6939 game.explosions_delayed = TRUE;
6942 if (game.magic_wall_active)
6944 if (!(game.magic_wall_time_left % 4))
6946 int element = Feld[magic_wall_x][magic_wall_y];
6948 if (element == EL_BD_MAGIC_WALL_FULL ||
6949 element == EL_BD_MAGIC_WALL_ACTIVE ||
6950 element == EL_BD_MAGIC_WALL_EMPTYING)
6951 PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
6953 PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
6956 if (game.magic_wall_time_left > 0)
6958 game.magic_wall_time_left--;
6959 if (!game.magic_wall_time_left)
6961 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
6963 element = Feld[x][y];
6965 if (element == EL_MAGIC_WALL_ACTIVE ||
6966 element == EL_MAGIC_WALL_FULL)
6968 Feld[x][y] = EL_MAGIC_WALL_DEAD;
6969 DrawLevelField(x, y);
6971 else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
6972 element == EL_BD_MAGIC_WALL_FULL)
6974 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
6975 DrawLevelField(x, y);
6979 game.magic_wall_active = FALSE;
6984 if (game.light_time_left > 0)
6986 game.light_time_left--;
6988 if (game.light_time_left == 0)
6989 RedrawAllLightSwitchesAndInvisibleElements();
6992 if (game.timegate_time_left > 0)
6994 game.timegate_time_left--;
6996 if (game.timegate_time_left == 0)
6997 CloseAllOpenTimegates();
7000 for (i = 0; i < MAX_PLAYERS; i++)
7002 struct PlayerInfo *player = &stored_player[i];
7004 if (SHIELD_ON(player))
7006 if (player->shield_deadly_time_left)
7007 PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
7008 else if (player->shield_normal_time_left)
7009 PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
7013 if (TimeFrames >= FRAMES_PER_SECOND)
7018 for (i = 0; i < MAX_PLAYERS; i++)
7020 struct PlayerInfo *player = &stored_player[i];
7022 if (SHIELD_ON(player))
7024 player->shield_normal_time_left--;
7026 if (player->shield_deadly_time_left > 0)
7027 player->shield_deadly_time_left--;
7031 if (tape.recording || tape.playing)
7032 DrawVideoDisplay(VIDEO_STATE_TIME_ON, TimePlayed);
7038 if (TimeLeft <= 10 && setup.time_limit)
7039 PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, SOUND_MIDDLE);
7041 DrawText(DX_TIME, DY_TIME, int2str(TimeLeft, 3), FONT_TEXT_2);
7043 if (!TimeLeft && setup.time_limit)
7044 for (i = 0; i < MAX_PLAYERS; i++)
7045 KillHero(&stored_player[i]);
7047 else if (level.time == 0 && !AllPlayersGone) /* level without time limit */
7048 DrawText(DX_TIME, DY_TIME, int2str(TimePlayed, 3), FONT_TEXT_2);
7052 PlayAllPlayersSound();
7054 if (options.debug) /* calculate frames per second */
7056 static unsigned long fps_counter = 0;
7057 static int fps_frames = 0;
7058 unsigned long fps_delay_ms = Counter() - fps_counter;
7062 if (fps_delay_ms >= 500) /* calculate fps every 0.5 seconds */
7064 global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
7067 fps_counter = Counter();
7070 redraw_mask |= REDRAW_FPS;
7074 if (stored_player[0].jx != stored_player[0].last_jx ||
7075 stored_player[0].jy != stored_player[0].last_jy)
7076 printf("::: %d, %d, %d, %d, %d\n",
7077 stored_player[0].MovDir,
7078 stored_player[0].MovPos,
7079 stored_player[0].GfxPos,
7080 stored_player[0].Frame,
7081 stored_player[0].StepFrame);
7088 for (i = 0; i < MAX_PLAYERS; i++)
7091 MOVE_DELAY_NORMAL_SPEED / stored_player[i].move_delay_value;
7093 stored_player[i].Frame += move_frames;
7095 if (stored_player[i].MovPos != 0)
7096 stored_player[i].StepFrame += move_frames;
7101 if (local_player->show_envelope != 0 && local_player->MovPos == 0)
7103 ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
7105 local_player->show_envelope = 0;
7110 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
7112 int min_x = x, min_y = y, max_x = x, max_y = y;
7115 for (i = 0; i < MAX_PLAYERS; i++)
7117 int jx = stored_player[i].jx, jy = stored_player[i].jy;
7119 if (!stored_player[i].active || &stored_player[i] == player)
7122 min_x = MIN(min_x, jx);
7123 min_y = MIN(min_y, jy);
7124 max_x = MAX(max_x, jx);
7125 max_y = MAX(max_y, jy);
7128 return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
7131 static boolean AllPlayersInVisibleScreen()
7135 for (i = 0; i < MAX_PLAYERS; i++)
7137 int jx = stored_player[i].jx, jy = stored_player[i].jy;
7139 if (!stored_player[i].active)
7142 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
7149 void ScrollLevel(int dx, int dy)
7151 int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
7154 BlitBitmap(drawto_field, drawto_field,
7155 FX + TILEX * (dx == -1) - softscroll_offset,
7156 FY + TILEY * (dy == -1) - softscroll_offset,
7157 SXSIZE - TILEX * (dx!=0) + 2 * softscroll_offset,
7158 SYSIZE - TILEY * (dy!=0) + 2 * softscroll_offset,
7159 FX + TILEX * (dx == 1) - softscroll_offset,
7160 FY + TILEY * (dy == 1) - softscroll_offset);
7164 x = (dx == 1 ? BX1 : BX2);
7165 for (y = BY1; y <= BY2; y++)
7166 DrawScreenField(x, y);
7171 y = (dy == 1 ? BY1 : BY2);
7172 for (x = BX1; x <= BX2; x++)
7173 DrawScreenField(x, y);
7176 redraw_mask |= REDRAW_FIELD;
7179 static void CheckGravityMovement(struct PlayerInfo *player)
7181 if (game.gravity && !player->programmed_action)
7183 int move_dir_vertical = player->action & (MV_UP | MV_DOWN);
7184 int move_dir_horizontal = player->action & (MV_LEFT | MV_RIGHT);
7186 (player->last_move_dir & (MV_LEFT | MV_RIGHT) ?
7187 (move_dir_vertical ? move_dir_vertical : move_dir_horizontal) :
7188 (move_dir_horizontal ? move_dir_horizontal : move_dir_vertical));
7189 int jx = player->jx, jy = player->jy;
7190 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
7191 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
7192 int new_jx = jx + dx, new_jy = jy + dy;
7193 boolean field_under_player_is_free =
7194 (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
7195 boolean player_is_moving_to_valid_field =
7196 (IN_LEV_FIELD(new_jx, new_jy) &&
7197 (Feld[new_jx][new_jy] == EL_SP_BASE ||
7198 Feld[new_jx][new_jy] == EL_SAND));
7199 /* !!! extend EL_SAND to anything diggable !!! */
7201 if (field_under_player_is_free &&
7202 !player_is_moving_to_valid_field &&
7203 !IS_WALKABLE_INSIDE(Feld[jx][jy]))
7204 player->programmed_action = MV_DOWN;
7210 -----------------------------------------------------------------------------
7211 dx, dy: direction (non-diagonal) to try to move the player to
7212 real_dx, real_dy: direction as read from input device (can be diagonal)
7215 boolean MovePlayerOneStep(struct PlayerInfo *player,
7216 int dx, int dy, int real_dx, int real_dy)
7219 static int change_sides[4][2] =
7221 /* enter side leave side */
7222 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* moving left */
7223 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* moving right */
7224 { CH_SIDE_BOTTOM, CH_SIDE_TOP }, /* moving up */
7225 { CH_SIDE_TOP, CH_SIDE_BOTTOM } /* moving down */
7227 int move_direction = (dx == -1 ? MV_LEFT :
7228 dx == +1 ? MV_RIGHT :
7230 dy == +1 ? MV_DOWN : MV_NO_MOVING);
7231 int enter_side = change_sides[MV_DIR_BIT(move_direction)][0];
7232 int leave_side = change_sides[MV_DIR_BIT(move_direction)][1];
7234 int jx = player->jx, jy = player->jy;
7235 int new_jx = jx + dx, new_jy = jy + dy;
7239 if (!player->active || (!dx && !dy))
7240 return MF_NO_ACTION;
7242 player->MovDir = (dx < 0 ? MV_LEFT :
7245 dy > 0 ? MV_DOWN : MV_NO_MOVING);
7247 if (!IN_LEV_FIELD(new_jx, new_jy))
7248 return MF_NO_ACTION;
7250 if (!options.network && !AllPlayersInSight(player, new_jx, new_jy))
7251 return MF_NO_ACTION;
7254 element = MovingOrBlocked2Element(new_jx, new_jy);
7256 element = MovingOrBlocked2ElementIfNotLeaving(new_jx, new_jy);
7259 if (DONT_RUN_INTO(element))
7261 if (element == EL_ACID && dx == 0 && dy == 1)
7264 Feld[jx][jy] = EL_PLAYER_1;
7265 InitMovingField(jx, jy, MV_DOWN);
7266 Store[jx][jy] = EL_ACID;
7267 ContinueMoving(jx, jy);
7271 TestIfHeroRunsIntoBadThing(jx, jy, player->MovDir);
7276 can_move = DigField(player, new_jx, new_jy, real_dx, real_dy, DF_DIG);
7277 if (can_move != MF_MOVING)
7280 /* check if DigField() has caused relocation of the player */
7281 if (player->jx != jx || player->jy != jy)
7282 return MF_NO_ACTION;
7284 StorePlayer[jx][jy] = 0;
7285 player->last_jx = jx;
7286 player->last_jy = jy;
7287 player->jx = new_jx;
7288 player->jy = new_jy;
7289 StorePlayer[new_jx][new_jy] = player->element_nr;
7292 (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
7294 player->step_counter++;
7296 PlayerVisit[jx][jy] = FrameCounter;
7298 ScrollPlayer(player, SCROLL_INIT);
7301 if (IS_CUSTOM_ELEMENT(Feld[jx][jy]))
7303 CheckTriggeredElementSideChange(jx, jy, Feld[jx][jy], leave_side,
7304 CE_OTHER_GETS_LEFT);
7305 CheckElementSideChange(jx, jy, Feld[jx][jy], leave_side,
7306 CE_LEFT_BY_PLAYER, -1);
7309 if (IS_CUSTOM_ELEMENT(Feld[new_jx][new_jy]))
7311 CheckTriggeredElementSideChange(new_jx, new_jy, Feld[new_jx][new_jy],
7312 enter_side, CE_OTHER_GETS_ENTERED);
7313 CheckElementSideChange(new_jx, new_jy, Feld[new_jx][new_jy], enter_side,
7314 CE_ENTERED_BY_PLAYER, -1);
7321 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
7323 int jx = player->jx, jy = player->jy;
7324 int old_jx = jx, old_jy = jy;
7325 int moved = MF_NO_ACTION;
7327 if (!player->active || (!dx && !dy))
7331 if (!FrameReached(&player->move_delay, player->move_delay_value) &&
7335 if (!FrameReached(&player->move_delay, player->move_delay_value) &&
7336 !(tape.playing && tape.file_version < FILE_VERSION_2_0))
7340 /* remove the last programmed player action */
7341 player->programmed_action = 0;
7345 /* should only happen if pre-1.2 tape recordings are played */
7346 /* this is only for backward compatibility */
7348 int original_move_delay_value = player->move_delay_value;
7351 printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%ld]\n",
7355 /* scroll remaining steps with finest movement resolution */
7356 player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
7358 while (player->MovPos)
7360 ScrollPlayer(player, SCROLL_GO_ON);
7361 ScrollScreen(NULL, SCROLL_GO_ON);
7367 player->move_delay_value = original_move_delay_value;
7370 if (player->last_move_dir & (MV_LEFT | MV_RIGHT))
7372 if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
7373 moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
7377 if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
7378 moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
7384 if (moved & MF_MOVING && !ScreenMovPos &&
7385 (player == local_player || !options.network))
7387 int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
7388 int offset = (setup.scroll_delay ? 3 : 0);
7390 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
7392 /* actual player has left the screen -- scroll in that direction */
7393 if (jx != old_jx) /* player has moved horizontally */
7394 scroll_x += (jx - old_jx);
7395 else /* player has moved vertically */
7396 scroll_y += (jy - old_jy);
7400 if (jx != old_jx) /* player has moved horizontally */
7402 if ((player->MovDir == MV_LEFT && scroll_x > jx - MIDPOSX + offset) ||
7403 (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
7404 scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
7406 /* don't scroll over playfield boundaries */
7407 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
7408 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
7410 /* don't scroll more than one field at a time */
7411 scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
7413 /* don't scroll against the player's moving direction */
7414 if ((player->MovDir == MV_LEFT && scroll_x > old_scroll_x) ||
7415 (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
7416 scroll_x = old_scroll_x;
7418 else /* player has moved vertically */
7420 if ((player->MovDir == MV_UP && scroll_y > jy - MIDPOSY + offset) ||
7421 (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
7422 scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
7424 /* don't scroll over playfield boundaries */
7425 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
7426 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
7428 /* don't scroll more than one field at a time */
7429 scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
7431 /* don't scroll against the player's moving direction */
7432 if ((player->MovDir == MV_UP && scroll_y > old_scroll_y) ||
7433 (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
7434 scroll_y = old_scroll_y;
7438 if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
7440 if (!options.network && !AllPlayersInVisibleScreen())
7442 scroll_x = old_scroll_x;
7443 scroll_y = old_scroll_y;
7447 ScrollScreen(player, SCROLL_INIT);
7448 ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
7455 InitPlayerGfxAnimation(player, ACTION_DEFAULT);
7457 if (!(moved & MF_MOVING) && !player->is_pushing)
7462 player->StepFrame = 0;
7464 if (moved & MF_MOVING)
7466 if (old_jx != jx && old_jy == jy)
7467 player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
7468 else if (old_jx == jx && old_jy != jy)
7469 player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
7471 DrawLevelField(jx, jy); /* for "crumbled sand" */
7473 player->last_move_dir = player->MovDir;
7474 player->is_moving = TRUE;
7476 player->is_snapping = FALSE;
7480 player->is_switching = FALSE;
7486 static int change_sides[4][2] =
7488 /* enter side leave side */
7489 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* moving left */
7490 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* moving right */
7491 { CH_SIDE_BOTTOM, CH_SIDE_TOP }, /* moving up */
7492 { CH_SIDE_TOP, CH_SIDE_BOTTOM } /* moving down */
7494 int move_direction = player->MovDir;
7495 int enter_side = change_sides[MV_DIR_BIT(move_direction)][0];
7496 int leave_side = change_sides[MV_DIR_BIT(move_direction)][1];
7499 if (IS_CUSTOM_ELEMENT(Feld[old_jx][old_jy]))
7501 CheckTriggeredElementSideChange(old_jx, old_jy, Feld[old_jx][old_jy],
7502 leave_side, CE_OTHER_GETS_LEFT);
7503 CheckElementSideChange(old_jx, old_jy, Feld[old_jx][old_jy],
7504 leave_side, CE_LEFT_BY_PLAYER, -1);
7507 if (IS_CUSTOM_ELEMENT(Feld[jx][jy]))
7509 CheckTriggeredElementSideChange(jx, jy, Feld[jx][jy],
7510 enter_side, CE_OTHER_GETS_ENTERED);
7511 CheckElementSideChange(jx, jy, Feld[jx][jy],
7512 enter_side, CE_ENTERED_BY_PLAYER, -1);
7523 CheckGravityMovement(player);
7526 player->last_move_dir = MV_NO_MOVING;
7528 player->is_moving = FALSE;
7531 if (game.engine_version < VERSION_IDENT(3,0,7,0))
7533 TestIfHeroTouchesBadThing(jx, jy);
7534 TestIfPlayerTouchesCustomElement(jx, jy);
7537 if (!player->active)
7543 void ScrollPlayer(struct PlayerInfo *player, int mode)
7545 int jx = player->jx, jy = player->jy;
7546 int last_jx = player->last_jx, last_jy = player->last_jy;
7547 int move_stepsize = TILEX / player->move_delay_value;
7549 if (!player->active || !player->MovPos)
7552 if (mode == SCROLL_INIT)
7554 player->actual_frame_counter = FrameCounter;
7555 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
7557 if (Feld[last_jx][last_jy] == EL_EMPTY)
7558 Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
7565 else if (!FrameReached(&player->actual_frame_counter, 1))
7568 player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
7569 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
7571 if (Feld[last_jx][last_jy] == EL_PLAYER_IS_LEAVING)
7572 Feld[last_jx][last_jy] = EL_EMPTY;
7574 /* before DrawPlayer() to draw correct player graphic for this case */
7575 if (player->MovPos == 0)
7576 CheckGravityMovement(player);
7579 DrawPlayer(player); /* needed here only to cleanup last field */
7582 if (player->MovPos == 0) /* player reached destination field */
7584 if (IS_PASSABLE(Feld[last_jx][last_jy]))
7586 /* continue with normal speed after quickly moving through gate */
7587 HALVE_PLAYER_SPEED(player);
7589 /* be able to make the next move without delay */
7590 player->move_delay = 0;
7593 player->last_jx = jx;
7594 player->last_jy = jy;
7596 if (Feld[jx][jy] == EL_EXIT_OPEN ||
7597 Feld[jx][jy] == EL_SP_EXIT_OPEN ||
7598 Feld[jx][jy] == EL_SP_EXIT_OPENING) /* <-- special case */
7600 DrawPlayer(player); /* needed here only to cleanup last field */
7603 if (local_player->friends_still_needed == 0 ||
7604 IS_SP_ELEMENT(Feld[jx][jy]))
7605 player->LevelSolved = player->GameOver = TRUE;
7608 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
7610 TestIfHeroTouchesBadThing(jx, jy);
7611 TestIfPlayerTouchesCustomElement(jx, jy);
7613 TestIfElementTouchesCustomElement(jx, jy); /* for empty space */
7616 if (!player->active)
7620 if (tape.single_step && tape.recording && !tape.pausing &&
7621 !player->programmed_action)
7622 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
7626 void ScrollScreen(struct PlayerInfo *player, int mode)
7628 static unsigned long screen_frame_counter = 0;
7630 if (mode == SCROLL_INIT)
7632 /* set scrolling step size according to actual player's moving speed */
7633 ScrollStepSize = TILEX / player->move_delay_value;
7635 screen_frame_counter = FrameCounter;
7636 ScreenMovDir = player->MovDir;
7637 ScreenMovPos = player->MovPos;
7638 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
7641 else if (!FrameReached(&screen_frame_counter, 1))
7646 ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
7647 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
7648 redraw_mask |= REDRAW_FIELD;
7651 ScreenMovDir = MV_NO_MOVING;
7654 void TestIfPlayerTouchesCustomElement(int x, int y)
7656 static int xy[4][2] =
7663 static int change_sides[4][2] =
7665 /* center side border side */
7666 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
7667 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
7668 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
7669 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
7671 static int touch_dir[4] =
7678 int center_element = Feld[x][y]; /* should always be non-moving! */
7681 for (i = 0; i < 4; i++)
7683 int xx = x + xy[i][0];
7684 int yy = y + xy[i][1];
7685 int center_side = change_sides[i][0];
7686 int border_side = change_sides[i][1];
7689 if (!IN_LEV_FIELD(xx, yy))
7692 if (IS_PLAYER(x, y))
7694 if (game.engine_version < VERSION_IDENT(3,0,7,0))
7695 border_element = Feld[xx][yy]; /* may be moving! */
7696 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
7697 border_element = Feld[xx][yy];
7698 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
7699 border_element = MovingOrBlocked2Element(xx, yy);
7701 continue; /* center and border element do not touch */
7703 CheckTriggeredElementSideChange(xx, yy, border_element, border_side,
7704 CE_OTHER_GETS_TOUCHED);
7705 CheckElementSideChange(xx, yy, border_element, border_side,
7706 CE_TOUCHED_BY_PLAYER, -1);
7708 else if (IS_PLAYER(xx, yy))
7710 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
7712 struct PlayerInfo *player = PLAYERINFO(xx, yy);
7714 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
7715 continue; /* center and border element do not touch */
7718 CheckTriggeredElementSideChange(x, y, center_element, center_side,
7719 CE_OTHER_GETS_TOUCHED);
7720 CheckElementSideChange(x, y, center_element, center_side,
7721 CE_TOUCHED_BY_PLAYER, -1);
7728 void TestIfElementTouchesCustomElement(int x, int y)
7730 static int xy[4][2] =
7737 static int change_sides[4][2] =
7739 /* center side border side */
7740 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
7741 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
7742 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
7743 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
7745 static int touch_dir[4] =
7752 boolean change_center_element = FALSE;
7753 int center_element_change_page = 0;
7754 int center_element = Feld[x][y]; /* should always be non-moving! */
7757 for (i = 0; i < 4; i++)
7759 int xx = x + xy[i][0];
7760 int yy = y + xy[i][1];
7761 int center_side = change_sides[i][0];
7762 int border_side = change_sides[i][1];
7765 if (!IN_LEV_FIELD(xx, yy))
7768 if (game.engine_version < VERSION_IDENT(3,0,7,0))
7769 border_element = Feld[xx][yy]; /* may be moving! */
7770 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
7771 border_element = Feld[xx][yy];
7772 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
7773 border_element = MovingOrBlocked2Element(xx, yy);
7775 continue; /* center and border element do not touch */
7777 /* check for change of center element (but change it only once) */
7778 if (IS_CUSTOM_ELEMENT(center_element) &&
7779 HAS_ANY_CHANGE_EVENT(center_element, CE_OTHER_IS_TOUCHING) &&
7780 !change_center_element)
7782 for (j = 0; j < element_info[center_element].num_change_pages; j++)
7784 struct ElementChangeInfo *change =
7785 &element_info[center_element].change_page[j];
7787 if (change->can_change &&
7788 change->events & CH_EVENT_BIT(CE_OTHER_IS_TOUCHING) &&
7789 change->sides & border_side &&
7790 change->trigger_element == border_element)
7792 change_center_element = TRUE;
7793 center_element_change_page = j;
7800 /* check for change of border element */
7801 if (IS_CUSTOM_ELEMENT(border_element) &&
7802 HAS_ANY_CHANGE_EVENT(border_element, CE_OTHER_IS_TOUCHING))
7804 for (j = 0; j < element_info[border_element].num_change_pages; j++)
7806 struct ElementChangeInfo *change =
7807 &element_info[border_element].change_page[j];
7809 if (change->can_change &&
7810 change->events & CH_EVENT_BIT(CE_OTHER_IS_TOUCHING) &&
7811 change->sides & center_side &&
7812 change->trigger_element == center_element)
7814 CheckElementSideChange(xx, yy, border_element, CH_SIDE_ANY,
7815 CE_OTHER_IS_TOUCHING, j);
7822 if (change_center_element)
7823 CheckElementSideChange(x, y, center_element, CH_SIDE_ANY,
7824 CE_OTHER_IS_TOUCHING, center_element_change_page);
7827 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
7829 int i, kill_x = -1, kill_y = -1;
7830 static int test_xy[4][2] =
7837 static int test_dir[4] =
7845 for (i = 0; i < 4; i++)
7847 int test_x, test_y, test_move_dir, test_element;
7849 test_x = good_x + test_xy[i][0];
7850 test_y = good_y + test_xy[i][1];
7851 if (!IN_LEV_FIELD(test_x, test_y))
7855 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NO_MOVING);
7858 test_element = Feld[test_x][test_y];
7860 test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
7863 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
7864 2nd case: DONT_TOUCH style bad thing does not move away from good thing
7866 if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
7867 (DONT_TOUCH(test_element) && test_move_dir != test_dir[i]))
7875 if (kill_x != -1 || kill_y != -1)
7877 if (IS_PLAYER(good_x, good_y))
7879 struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
7881 if (player->shield_deadly_time_left > 0)
7882 Bang(kill_x, kill_y);
7883 else if (!PLAYER_PROTECTED(good_x, good_y))
7887 Bang(good_x, good_y);
7891 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
7893 int i, kill_x = -1, kill_y = -1;
7894 int bad_element = Feld[bad_x][bad_y];
7895 static int test_xy[4][2] =
7902 static int touch_dir[4] =
7909 static int test_dir[4] =
7917 if (bad_element == EL_EXPLOSION) /* skip just exploding bad things */
7920 for (i = 0; i < 4; i++)
7922 int test_x, test_y, test_move_dir, test_element;
7924 test_x = bad_x + test_xy[i][0];
7925 test_y = bad_y + test_xy[i][1];
7926 if (!IN_LEV_FIELD(test_x, test_y))
7930 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NO_MOVING);
7932 test_element = Feld[test_x][test_y];
7934 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
7935 2nd case: DONT_TOUCH style bad thing does not move away from good thing
7937 if ((DONT_RUN_INTO(bad_element) && bad_move_dir == test_dir[i]) ||
7938 (DONT_TOUCH(bad_element) && test_move_dir != test_dir[i]))
7940 /* good thing is player or penguin that does not move away */
7941 if (IS_PLAYER(test_x, test_y))
7943 struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
7945 if (bad_element == EL_ROBOT && player->is_moving)
7946 continue; /* robot does not kill player if he is moving */
7948 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
7950 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
7951 continue; /* center and border element do not touch */
7958 else if (test_element == EL_PENGUIN)
7967 if (kill_x != -1 || kill_y != -1)
7969 if (IS_PLAYER(kill_x, kill_y))
7971 struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
7973 if (player->shield_deadly_time_left > 0)
7975 else if (!PLAYER_PROTECTED(kill_x, kill_y))
7979 Bang(kill_x, kill_y);
7983 void TestIfHeroTouchesBadThing(int x, int y)
7985 TestIfGoodThingHitsBadThing(x, y, MV_NO_MOVING);
7988 void TestIfHeroRunsIntoBadThing(int x, int y, int move_dir)
7990 TestIfGoodThingHitsBadThing(x, y, move_dir);
7993 void TestIfBadThingTouchesHero(int x, int y)
7995 TestIfBadThingHitsGoodThing(x, y, MV_NO_MOVING);
7998 void TestIfBadThingRunsIntoHero(int x, int y, int move_dir)
8000 TestIfBadThingHitsGoodThing(x, y, move_dir);
8003 void TestIfFriendTouchesBadThing(int x, int y)
8005 TestIfGoodThingHitsBadThing(x, y, MV_NO_MOVING);
8008 void TestIfBadThingTouchesFriend(int x, int y)
8010 TestIfBadThingHitsGoodThing(x, y, MV_NO_MOVING);
8013 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
8015 int i, kill_x = bad_x, kill_y = bad_y;
8016 static int xy[4][2] =
8024 for (i = 0; i < 4; i++)
8028 x = bad_x + xy[i][0];
8029 y = bad_y + xy[i][1];
8030 if (!IN_LEV_FIELD(x, y))
8033 element = Feld[x][y];
8034 if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
8035 element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
8043 if (kill_x != bad_x || kill_y != bad_y)
8047 void KillHero(struct PlayerInfo *player)
8049 int jx = player->jx, jy = player->jy;
8051 if (!player->active)
8054 /* remove accessible field at the player's position */
8055 Feld[jx][jy] = EL_EMPTY;
8057 /* deactivate shield (else Bang()/Explode() would not work right) */
8058 player->shield_normal_time_left = 0;
8059 player->shield_deadly_time_left = 0;
8065 static void KillHeroUnlessProtected(int x, int y)
8067 if (!PLAYER_PROTECTED(x, y))
8068 KillHero(PLAYERINFO(x, y));
8071 void BuryHero(struct PlayerInfo *player)
8073 int jx = player->jx, jy = player->jy;
8075 if (!player->active)
8079 PlayLevelSoundElementAction(jx, jy, player->element_nr, ACTION_DYING);
8081 PlayLevelSound(jx, jy, SND_CLASS_PLAYER_DYING);
8083 PlayLevelSound(jx, jy, SND_GAME_LOSING);
8085 player->GameOver = TRUE;
8089 void RemoveHero(struct PlayerInfo *player)
8091 int jx = player->jx, jy = player->jy;
8092 int i, found = FALSE;
8094 player->present = FALSE;
8095 player->active = FALSE;
8097 if (!ExplodeField[jx][jy])
8098 StorePlayer[jx][jy] = 0;
8100 for (i = 0; i < MAX_PLAYERS; i++)
8101 if (stored_player[i].active)
8105 AllPlayersGone = TRUE;
8112 =============================================================================
8113 checkDiagonalPushing()
8114 -----------------------------------------------------------------------------
8115 check if diagonal input device direction results in pushing of object
8116 (by checking if the alternative direction is walkable, diggable, ...)
8117 =============================================================================
8120 static boolean checkDiagonalPushing(struct PlayerInfo *player,
8121 int x, int y, int real_dx, int real_dy)
8123 int jx, jy, dx, dy, xx, yy;
8125 if (real_dx == 0 || real_dy == 0) /* no diagonal direction => push */
8128 /* diagonal direction: check alternative direction */
8133 xx = jx + (dx == 0 ? real_dx : 0);
8134 yy = jy + (dy == 0 ? real_dy : 0);
8136 return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
8140 =============================================================================
8142 -----------------------------------------------------------------------------
8143 x, y: field next to player (non-diagonal) to try to dig to
8144 real_dx, real_dy: direction as read from input device (can be diagonal)
8145 =============================================================================
8148 int DigField(struct PlayerInfo *player,
8149 int x, int y, int real_dx, int real_dy, int mode)
8151 static int change_sides[4] =
8153 CH_SIDE_RIGHT, /* moving left */
8154 CH_SIDE_LEFT, /* moving right */
8155 CH_SIDE_BOTTOM, /* moving up */
8156 CH_SIDE_TOP, /* moving down */
8158 boolean use_spring_bug = (game.engine_version < VERSION_IDENT(2,2,0,0));
8159 int jx = player->jx, jy = player->jy;
8160 int dx = x - jx, dy = y - jy;
8161 int nextx = x + dx, nexty = y + dy;
8162 int move_direction = (dx == -1 ? MV_LEFT :
8163 dx == +1 ? MV_RIGHT :
8165 dy == +1 ? MV_DOWN : MV_NO_MOVING);
8166 int dig_side = change_sides[MV_DIR_BIT(move_direction)];
8169 if (player->MovPos == 0)
8171 player->is_digging = FALSE;
8172 player->is_collecting = FALSE;
8175 if (player->MovPos == 0) /* last pushing move finished */
8176 player->is_pushing = FALSE;
8178 if (mode == DF_NO_PUSH) /* player just stopped pushing */
8180 player->is_switching = FALSE;
8181 player->push_delay = 0;
8183 return MF_NO_ACTION;
8186 if (IS_MOVING(x, y) || IS_PLAYER(x, y))
8187 return MF_NO_ACTION;
8190 if (IS_TUBE(Feld[jx][jy]) || IS_TUBE(Back[jx][jy]))
8192 if (IS_TUBE(Feld[jx][jy]) ||
8193 (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0)))
8197 int tube_element = (IS_TUBE(Feld[jx][jy]) ? Feld[jx][jy] : Back[jx][jy]);
8198 int tube_leave_directions[][2] =
8200 { EL_TUBE_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
8201 { EL_TUBE_VERTICAL, MV_UP | MV_DOWN },
8202 { EL_TUBE_HORIZONTAL, MV_LEFT | MV_RIGHT },
8203 { EL_TUBE_VERTICAL_LEFT, MV_LEFT | MV_UP | MV_DOWN },
8204 { EL_TUBE_VERTICAL_RIGHT, MV_RIGHT | MV_UP | MV_DOWN },
8205 { EL_TUBE_HORIZONTAL_UP, MV_LEFT | MV_RIGHT | MV_UP },
8206 { EL_TUBE_HORIZONTAL_DOWN, MV_LEFT | MV_RIGHT | MV_DOWN },
8207 { EL_TUBE_LEFT_UP, MV_LEFT | MV_UP },
8208 { EL_TUBE_LEFT_DOWN, MV_LEFT | MV_DOWN },
8209 { EL_TUBE_RIGHT_UP, MV_RIGHT | MV_UP },
8210 { EL_TUBE_RIGHT_DOWN, MV_RIGHT | MV_DOWN },
8211 { -1, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN }
8214 while (tube_leave_directions[i][0] != tube_element)
8217 if (tube_leave_directions[i][0] == -1) /* should not happen */
8221 if (!(tube_leave_directions[i][1] & move_direction))
8222 return MF_NO_ACTION; /* tube has no opening in this direction */
8225 element = Feld[x][y];
8227 if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
8228 game.engine_version >= VERSION_IDENT(2,2,0,0))
8229 return MF_NO_ACTION;
8233 case EL_SP_PORT_LEFT:
8234 case EL_SP_PORT_RIGHT:
8236 case EL_SP_PORT_DOWN:
8237 case EL_SP_PORT_HORIZONTAL:
8238 case EL_SP_PORT_VERTICAL:
8239 case EL_SP_PORT_ANY:
8240 case EL_SP_GRAVITY_PORT_LEFT:
8241 case EL_SP_GRAVITY_PORT_RIGHT:
8242 case EL_SP_GRAVITY_PORT_UP:
8243 case EL_SP_GRAVITY_PORT_DOWN:
8245 element != EL_SP_PORT_LEFT &&
8246 element != EL_SP_GRAVITY_PORT_LEFT &&
8247 element != EL_SP_PORT_HORIZONTAL &&
8248 element != EL_SP_PORT_ANY) ||
8250 element != EL_SP_PORT_RIGHT &&
8251 element != EL_SP_GRAVITY_PORT_RIGHT &&
8252 element != EL_SP_PORT_HORIZONTAL &&
8253 element != EL_SP_PORT_ANY) ||
8255 element != EL_SP_PORT_UP &&
8256 element != EL_SP_GRAVITY_PORT_UP &&
8257 element != EL_SP_PORT_VERTICAL &&
8258 element != EL_SP_PORT_ANY) ||
8260 element != EL_SP_PORT_DOWN &&
8261 element != EL_SP_GRAVITY_PORT_DOWN &&
8262 element != EL_SP_PORT_VERTICAL &&
8263 element != EL_SP_PORT_ANY) ||
8264 !IN_LEV_FIELD(nextx, nexty) ||
8265 !IS_FREE(nextx, nexty))
8266 return MF_NO_ACTION;
8268 if (element == EL_SP_GRAVITY_PORT_LEFT ||
8269 element == EL_SP_GRAVITY_PORT_RIGHT ||
8270 element == EL_SP_GRAVITY_PORT_UP ||
8271 element == EL_SP_GRAVITY_PORT_DOWN)
8272 game.gravity = !game.gravity;
8274 /* automatically move to the next field with double speed */
8275 player->programmed_action = move_direction;
8276 DOUBLE_PLAYER_SPEED(player);
8278 PlayLevelSound(x, y, SND_CLASS_SP_PORT_PASSING);
8282 case EL_TUBE_VERTICAL:
8283 case EL_TUBE_HORIZONTAL:
8284 case EL_TUBE_VERTICAL_LEFT:
8285 case EL_TUBE_VERTICAL_RIGHT:
8286 case EL_TUBE_HORIZONTAL_UP:
8287 case EL_TUBE_HORIZONTAL_DOWN:
8288 case EL_TUBE_LEFT_UP:
8289 case EL_TUBE_LEFT_DOWN:
8290 case EL_TUBE_RIGHT_UP:
8291 case EL_TUBE_RIGHT_DOWN:
8294 int tube_enter_directions[][2] =
8296 { EL_TUBE_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
8297 { EL_TUBE_VERTICAL, MV_UP | MV_DOWN },
8298 { EL_TUBE_HORIZONTAL, MV_LEFT | MV_RIGHT },
8299 { EL_TUBE_VERTICAL_LEFT, MV_RIGHT | MV_UP | MV_DOWN },
8300 { EL_TUBE_VERTICAL_RIGHT, MV_LEFT | MV_UP | MV_DOWN },
8301 { EL_TUBE_HORIZONTAL_UP, MV_LEFT | MV_RIGHT | MV_DOWN },
8302 { EL_TUBE_HORIZONTAL_DOWN, MV_LEFT | MV_RIGHT | MV_UP },
8303 { EL_TUBE_LEFT_UP, MV_RIGHT | MV_DOWN },
8304 { EL_TUBE_LEFT_DOWN, MV_RIGHT | MV_UP },
8305 { EL_TUBE_RIGHT_UP, MV_LEFT | MV_DOWN },
8306 { EL_TUBE_RIGHT_DOWN, MV_LEFT | MV_UP },
8307 { -1, MV_NO_MOVING }
8310 while (tube_enter_directions[i][0] != element)
8313 if (tube_enter_directions[i][0] == -1) /* should not happen */
8317 if (!(tube_enter_directions[i][1] & move_direction))
8318 return MF_NO_ACTION; /* tube has no opening in this direction */
8320 PlayLevelSound(x, y, SND_CLASS_TUBE_WALKING);
8326 if (IS_WALKABLE(element))
8328 int sound_action = ACTION_WALKING;
8330 if (element >= EL_GATE_1 && element <= EL_GATE_4)
8332 if (!player->key[element - EL_GATE_1])
8333 return MF_NO_ACTION;
8335 else if (element >= EL_GATE_1_GRAY && element <= EL_GATE_4_GRAY)
8337 if (!player->key[element - EL_GATE_1_GRAY])
8338 return MF_NO_ACTION;
8340 else if (element == EL_EXIT_OPEN ||
8341 element == EL_SP_EXIT_OPEN ||
8342 element == EL_SP_EXIT_OPENING)
8344 sound_action = ACTION_PASSING; /* player is passing exit */
8346 else if (element == EL_EMPTY)
8348 sound_action = ACTION_MOVING; /* nothing to walk on */
8351 /* play sound from background or player, whatever is available */
8352 if (element_info[element].sound[sound_action] != SND_UNDEFINED)
8353 PlayLevelSoundElementAction(x, y, element, sound_action);
8355 PlayLevelSoundElementAction(x, y, player->element_nr, sound_action);
8359 else if (IS_PASSABLE(element))
8361 if (!IN_LEV_FIELD(nextx, nexty) || !IS_FREE(nextx, nexty))
8362 return MF_NO_ACTION;
8365 if (CAN_MOVE(element)) /* only fixed elements can be passed! */
8366 return MF_NO_ACTION;
8369 if (element >= EL_EM_GATE_1 && element <= EL_EM_GATE_4)
8371 if (!player->key[element - EL_EM_GATE_1])
8372 return MF_NO_ACTION;
8374 else if (element >= EL_EM_GATE_1_GRAY && element <= EL_EM_GATE_4_GRAY)
8376 if (!player->key[element - EL_EM_GATE_1_GRAY])
8377 return MF_NO_ACTION;
8380 /* automatically move to the next field with double speed */
8381 player->programmed_action = move_direction;
8382 DOUBLE_PLAYER_SPEED(player);
8384 PlayLevelSoundAction(x, y, ACTION_PASSING);
8388 else if (IS_DIGGABLE(element))
8392 if (mode != DF_SNAP)
8395 GfxElement[x][y] = GFX_ELEMENT(element);
8398 (GFX_CRUMBLED(element) ? EL_SAND : GFX_ELEMENT(element));
8400 player->is_digging = TRUE;
8403 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
8405 CheckTriggeredElementChange(x, y, element, CE_OTHER_GETS_DIGGED);
8408 if (mode == DF_SNAP)
8409 TestIfElementTouchesCustomElement(x, y); /* for empty space */
8414 else if (IS_COLLECTIBLE(element))
8418 if (mode != DF_SNAP)
8420 GfxElement[x][y] = element;
8421 player->is_collecting = TRUE;
8424 if (element == EL_SPEED_PILL)
8425 player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
8426 else if (element == EL_EXTRA_TIME && level.time > 0)
8429 DrawText(DX_TIME, DY_TIME, int2str(TimeLeft, 3), FONT_TEXT_2);
8431 else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
8433 player->shield_normal_time_left += 10;
8434 if (element == EL_SHIELD_DEADLY)
8435 player->shield_deadly_time_left += 10;
8437 else if (element == EL_DYNAMITE || element == EL_SP_DISK_RED)
8439 if (player->inventory_size < MAX_INVENTORY_SIZE)
8440 player->inventory_element[player->inventory_size++] = element;
8442 DrawText(DX_DYNAMITE, DY_DYNAMITE,
8443 int2str(local_player->inventory_size, 3), FONT_TEXT_2);
8445 else if (element == EL_DYNABOMB_INCREASE_NUMBER)
8447 player->dynabomb_count++;
8448 player->dynabombs_left++;
8450 else if (element == EL_DYNABOMB_INCREASE_SIZE)
8452 player->dynabomb_size++;
8454 else if (element == EL_DYNABOMB_INCREASE_POWER)
8456 player->dynabomb_xl = TRUE;
8458 else if ((element >= EL_KEY_1 && element <= EL_KEY_4) ||
8459 (element >= EL_EM_KEY_1 && element <= EL_EM_KEY_4))
8461 int key_nr = (element >= EL_KEY_1 && element <= EL_KEY_4 ?
8462 element - EL_KEY_1 : element - EL_EM_KEY_1);
8464 player->key[key_nr] = TRUE;
8466 DrawMiniGraphicExt(drawto, DX_KEYS + key_nr * MINI_TILEX, DY_KEYS,
8467 el2edimg(EL_KEY_1 + key_nr));
8468 redraw_mask |= REDRAW_DOOR_1;
8470 else if (IS_ENVELOPE(element))
8473 player->show_envelope = element;
8475 ShowEnvelope(element - EL_ENVELOPE_1);
8478 else if (IS_DROPPABLE(element)) /* can be collected and dropped */
8482 for (i = 0; i < element_info[element].collect_count; i++)
8483 if (player->inventory_size < MAX_INVENTORY_SIZE)
8484 player->inventory_element[player->inventory_size++] = element;
8486 DrawText(DX_DYNAMITE, DY_DYNAMITE,
8487 int2str(local_player->inventory_size, 3), FONT_TEXT_2);
8489 else if (element_info[element].collect_count > 0)
8491 local_player->gems_still_needed -=
8492 element_info[element].collect_count;
8493 if (local_player->gems_still_needed < 0)
8494 local_player->gems_still_needed = 0;
8496 DrawText(DX_EMERALDS, DY_EMERALDS,
8497 int2str(local_player->gems_still_needed, 3), FONT_TEXT_2);
8500 RaiseScoreElement(element);
8501 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
8503 CheckTriggeredElementChange(x, y, element, CE_OTHER_GETS_COLLECTED);
8506 if (mode == DF_SNAP)
8507 TestIfElementTouchesCustomElement(x, y); /* for empty space */
8512 else if (IS_PUSHABLE(element))
8514 if (mode == DF_SNAP && element != EL_BD_ROCK)
8515 return MF_NO_ACTION;
8517 if (CAN_FALL(element) && dy)
8518 return MF_NO_ACTION;
8520 if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
8521 !(element == EL_SPRING && use_spring_bug))
8522 return MF_NO_ACTION;
8525 if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
8526 ((move_direction & MV_VERTICAL &&
8527 ((element_info[element].move_pattern & MV_LEFT &&
8528 IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
8529 (element_info[element].move_pattern & MV_RIGHT &&
8530 IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
8531 (move_direction & MV_HORIZONTAL &&
8532 ((element_info[element].move_pattern & MV_UP &&
8533 IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
8534 (element_info[element].move_pattern & MV_DOWN &&
8535 IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
8536 return MF_NO_ACTION;
8540 /* do not push elements already moving away faster than player */
8541 if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
8542 ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
8543 return MF_NO_ACTION;
8545 if (element == EL_SPRING && MovDir[x][y] != MV_NO_MOVING)
8546 return MF_NO_ACTION;
8550 if (game.engine_version >= VERSION_IDENT(3,0,7,1))
8552 if (player->push_delay_value == -1)
8553 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
8555 else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
8557 if (!player->is_pushing)
8558 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
8562 if (game.engine_version >= VERSION_IDENT(2,2,0,7) &&
8563 (game.engine_version < VERSION_IDENT(3,0,7,1) ||
8564 !player_is_pushing))
8565 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
8568 if (!player->is_pushing &&
8569 game.engine_version >= VERSION_IDENT(2,2,0,7))
8570 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
8574 printf("::: push delay: %ld [%d, %d] [%d]\n",
8575 player->push_delay_value, FrameCounter, game.engine_version,
8576 player->is_pushing);
8579 player->is_pushing = TRUE;
8581 if (!(IN_LEV_FIELD(nextx, nexty) &&
8582 (IS_FREE(nextx, nexty) ||
8583 (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY &&
8584 IS_SB_ELEMENT(element)))))
8585 return MF_NO_ACTION;
8587 if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
8588 return MF_NO_ACTION;
8590 if (player->push_delay == 0) /* new pushing; restart delay */
8591 player->push_delay = FrameCounter;
8593 if (!FrameReached(&player->push_delay, player->push_delay_value) &&
8594 !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
8595 element != EL_SPRING && element != EL_BALLOON)
8597 /* make sure that there is no move delay before next try to push */
8598 if (game.engine_version >= VERSION_IDENT(3,0,7,1))
8599 player->move_delay = INITIAL_MOVE_DELAY_OFF;
8601 return MF_NO_ACTION;
8605 printf("::: NOW PUSHING... [%d]\n", FrameCounter);
8608 if (IS_SB_ELEMENT(element))
8610 if (element == EL_SOKOBAN_FIELD_FULL)
8612 Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
8613 local_player->sokobanfields_still_needed++;
8616 if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
8618 Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
8619 local_player->sokobanfields_still_needed--;
8622 Feld[x][y] = EL_SOKOBAN_OBJECT;
8624 if (Back[x][y] == Back[nextx][nexty])
8625 PlayLevelSoundAction(x, y, ACTION_PUSHING);
8626 else if (Back[x][y] != 0)
8627 PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
8630 PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
8633 if (local_player->sokobanfields_still_needed == 0 &&
8634 game.emulation == EMU_SOKOBAN)
8636 player->LevelSolved = player->GameOver = TRUE;
8637 PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
8641 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
8643 InitMovingField(x, y, move_direction);
8644 GfxAction[x][y] = ACTION_PUSHING;
8646 if (mode == DF_SNAP)
8647 ContinueMoving(x, y);
8649 MovPos[x][y] = (dx != 0 ? dx : dy);
8651 Pushed[x][y] = TRUE;
8652 Pushed[nextx][nexty] = TRUE;
8654 if (game.engine_version < VERSION_IDENT(2,2,0,7))
8655 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
8657 player->push_delay_value = -1; /* get new value later */
8659 CheckTriggeredElementSideChange(x, y, element, dig_side,
8660 CE_OTHER_GETS_PUSHED);
8661 CheckElementSideChange(x, y, element, dig_side,
8662 CE_PUSHED_BY_PLAYER, -1);
8666 else if (IS_SWITCHABLE(element))
8668 if (PLAYER_SWITCHING(player, x, y))
8671 player->is_switching = TRUE;
8672 player->switch_x = x;
8673 player->switch_y = y;
8675 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
8677 if (element == EL_ROBOT_WHEEL)
8679 Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
8683 DrawLevelField(x, y);
8685 else if (element == EL_SP_TERMINAL)
8689 for (yy = 0; yy < lev_fieldy; yy++) for (xx=0; xx < lev_fieldx; xx++)
8691 if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
8693 else if (Feld[xx][yy] == EL_SP_TERMINAL)
8694 Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
8697 else if (IS_BELT_SWITCH(element))
8699 ToggleBeltSwitch(x, y);
8701 else if (element == EL_SWITCHGATE_SWITCH_UP ||
8702 element == EL_SWITCHGATE_SWITCH_DOWN)
8704 ToggleSwitchgateSwitch(x, y);
8706 else if (element == EL_LIGHT_SWITCH ||
8707 element == EL_LIGHT_SWITCH_ACTIVE)
8709 ToggleLightSwitch(x, y);
8712 PlayLevelSound(x, y, element == EL_LIGHT_SWITCH ?
8713 SND_LIGHT_SWITCH_ACTIVATING :
8714 SND_LIGHT_SWITCH_DEACTIVATING);
8717 else if (element == EL_TIMEGATE_SWITCH)
8719 ActivateTimegateSwitch(x, y);
8721 else if (element == EL_BALLOON_SWITCH_LEFT ||
8722 element == EL_BALLOON_SWITCH_RIGHT ||
8723 element == EL_BALLOON_SWITCH_UP ||
8724 element == EL_BALLOON_SWITCH_DOWN ||
8725 element == EL_BALLOON_SWITCH_ANY)
8727 if (element == EL_BALLOON_SWITCH_ANY)
8728 game.balloon_dir = move_direction;
8730 game.balloon_dir = (element == EL_BALLOON_SWITCH_LEFT ? MV_LEFT :
8731 element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
8732 element == EL_BALLOON_SWITCH_UP ? MV_UP :
8733 element == EL_BALLOON_SWITCH_DOWN ? MV_DOWN :
8736 else if (element == EL_LAMP)
8738 Feld[x][y] = EL_LAMP_ACTIVE;
8739 local_player->lights_still_needed--;
8741 DrawLevelField(x, y);
8743 else if (element == EL_TIME_ORB_FULL)
8745 Feld[x][y] = EL_TIME_ORB_EMPTY;
8747 DrawText(DX_TIME, DY_TIME, int2str(TimeLeft, 3), FONT_TEXT_2);
8749 DrawLevelField(x, y);
8752 PlaySoundStereo(SND_TIME_ORB_FULL_COLLECTING, SOUND_MIDDLE);
8760 if (!PLAYER_SWITCHING(player, x, y))
8762 player->is_switching = TRUE;
8763 player->switch_x = x;
8764 player->switch_y = y;
8766 CheckTriggeredElementSideChange(x, y, element, dig_side,
8767 CE_OTHER_IS_SWITCHING);
8768 CheckElementSideChange(x, y, element, dig_side, CE_SWITCHED, -1);
8771 CheckTriggeredElementSideChange(x, y, element, dig_side,
8772 CE_OTHER_GETS_PRESSED);
8773 CheckElementSideChange(x, y, element, dig_side,
8774 CE_PRESSED_BY_PLAYER, -1);
8777 return MF_NO_ACTION;
8780 player->push_delay = 0;
8782 if (Feld[x][y] != element) /* really digged/collected something */
8783 player->is_collecting = !player->is_digging;
8788 boolean SnapField(struct PlayerInfo *player, int dx, int dy)
8790 int jx = player->jx, jy = player->jy;
8791 int x = jx + dx, y = jy + dy;
8792 int snap_direction = (dx == -1 ? MV_LEFT :
8793 dx == +1 ? MV_RIGHT :
8795 dy == +1 ? MV_DOWN : MV_NO_MOVING);
8797 if (player->MovPos && game.engine_version >= VERSION_IDENT(2,2,0,0))
8800 if (!player->active || !IN_LEV_FIELD(x, y))
8808 if (player->MovPos == 0)
8809 player->is_pushing = FALSE;
8811 player->is_snapping = FALSE;
8813 if (player->MovPos == 0)
8815 player->is_moving = FALSE;
8816 player->is_digging = FALSE;
8817 player->is_collecting = FALSE;
8823 if (player->is_snapping)
8826 player->MovDir = snap_direction;
8828 player->is_moving = FALSE;
8829 player->is_digging = FALSE;
8830 player->is_collecting = FALSE;
8832 if (DigField(player, x, y, 0, 0, DF_SNAP) == MF_NO_ACTION)
8835 player->is_snapping = TRUE;
8837 player->is_moving = FALSE;
8838 player->is_digging = FALSE;
8839 player->is_collecting = FALSE;
8841 DrawLevelField(x, y);
8847 boolean DropElement(struct PlayerInfo *player)
8849 int jx = player->jx, jy = player->jy;
8852 if (!player->active || player->MovPos)
8855 old_element = Feld[jx][jy];
8857 /* check if player has anything that can be dropped */
8858 if (player->inventory_size == 0 && player->dynabombs_left == 0)
8861 /* check if anything can be dropped at the current position */
8862 if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
8865 /* collected custom elements can only be dropped on empty fields */
8866 if (player->inventory_size > 0 &&
8867 IS_CUSTOM_ELEMENT(player->inventory_element[player->inventory_size - 1])
8868 && old_element != EL_EMPTY)
8871 if (old_element != EL_EMPTY)
8872 Back[jx][jy] = old_element; /* store old element on this field */
8874 MovDelay[jx][jy] = 96;
8876 ResetGfxAnimation(jx, jy);
8877 ResetRandomAnimationValue(jx, jy);
8879 if (player->inventory_size > 0)
8881 int new_element = player->inventory_element[--player->inventory_size];
8883 Feld[jx][jy] = (new_element == EL_DYNAMITE ? EL_DYNAMITE_ACTIVE :
8884 new_element == EL_SP_DISK_RED ? EL_SP_DISK_RED_ACTIVE :
8887 DrawText(DX_DYNAMITE, DY_DYNAMITE,
8888 int2str(local_player->inventory_size, 3), FONT_TEXT_2);
8890 if (IN_SCR_FIELD(SCREENX(jx), SCREENY(jy)))
8891 DrawGraphicThruMask(SCREENX(jx), SCREENY(jy), el2img(Feld[jx][jy]), 0);
8893 PlayLevelSoundAction(jx, jy, ACTION_DROPPING);
8895 CheckTriggeredElementChange(jx, jy, new_element, CE_OTHER_GETS_DROPPED);
8896 CheckElementChange(jx, jy, new_element, CE_DROPPED_BY_PLAYER);
8898 TestIfElementTouchesCustomElement(jx, jy);
8900 else /* player is dropping a dyna bomb */
8902 player->dynabombs_left--;
8905 EL_DYNABOMB_PLAYER_1_ACTIVE + (player->element_nr - EL_PLAYER_1);
8907 if (IN_SCR_FIELD(SCREENX(jx), SCREENY(jy)))
8908 DrawGraphicThruMask(SCREENX(jx), SCREENY(jy), el2img(Feld[jx][jy]), 0);
8910 PlayLevelSoundAction(jx, jy, ACTION_DROPPING);
8916 /* ------------------------------------------------------------------------- */
8917 /* game sound playing functions */
8918 /* ------------------------------------------------------------------------- */
8920 static int *loop_sound_frame = NULL;
8921 static int *loop_sound_volume = NULL;
8923 void InitPlayLevelSound()
8925 int num_sounds = getSoundListSize();
8927 checked_free(loop_sound_frame);
8928 checked_free(loop_sound_volume);
8930 loop_sound_frame = checked_calloc(num_sounds * sizeof(int));
8931 loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
8934 static void PlayLevelSound(int x, int y, int nr)
8936 int sx = SCREENX(x), sy = SCREENY(y);
8937 int volume, stereo_position;
8938 int max_distance = 8;
8939 int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
8941 if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
8942 (!setup.sound_loops && IS_LOOP_SOUND(nr)))
8945 if (!IN_LEV_FIELD(x, y) ||
8946 sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
8947 sy < -max_distance || sy >= SCR_FIELDY + max_distance)
8950 volume = SOUND_MAX_VOLUME;
8952 if (!IN_SCR_FIELD(sx, sy))
8954 int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
8955 int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
8957 volume -= volume * (dx > dy ? dx : dy) / max_distance;
8960 stereo_position = (SOUND_MAX_LEFT +
8961 (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
8962 (SCR_FIELDX + 2 * max_distance));
8964 if (IS_LOOP_SOUND(nr))
8966 /* This assures that quieter loop sounds do not overwrite louder ones,
8967 while restarting sound volume comparison with each new game frame. */
8969 if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
8972 loop_sound_volume[nr] = volume;
8973 loop_sound_frame[nr] = FrameCounter;
8976 PlaySoundExt(nr, volume, stereo_position, type);
8979 static void PlayLevelSoundNearest(int x, int y, int sound_action)
8981 PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
8982 x > LEVELX(BX2) ? LEVELX(BX2) : x,
8983 y < LEVELY(BY1) ? LEVELY(BY1) :
8984 y > LEVELY(BY2) ? LEVELY(BY2) : y,
8988 static void PlayLevelSoundAction(int x, int y, int action)
8990 PlayLevelSoundElementAction(x, y, Feld[x][y], action);
8993 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
8995 int sound_effect = element_info[element].sound[action];
8997 if (sound_effect != SND_UNDEFINED)
8998 PlayLevelSound(x, y, sound_effect);
9001 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
9004 int sound_effect = element_info[element].sound[action];
9006 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
9007 PlayLevelSound(x, y, sound_effect);
9010 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
9012 int sound_effect = element_info[Feld[x][y]].sound[action];
9014 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
9015 PlayLevelSound(x, y, sound_effect);
9018 static void StopLevelSoundActionIfLoop(int x, int y, int action)
9020 int sound_effect = element_info[Feld[x][y]].sound[action];
9022 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
9023 StopSound(sound_effect);
9026 static void PlayLevelMusic()
9028 if (levelset.music[level_nr] != MUS_UNDEFINED)
9029 PlayMusic(levelset.music[level_nr]); /* from config file */
9031 PlayMusic(MAP_NOCONF_MUSIC(level_nr)); /* from music dir */
9034 void RaiseScore(int value)
9036 local_player->score += value;
9037 DrawText(DX_SCORE, DY_SCORE, int2str(local_player->score, 5), FONT_TEXT_2);
9040 void RaiseScoreElement(int element)
9046 case EL_EMERALD_YELLOW:
9047 case EL_EMERALD_RED:
9048 case EL_EMERALD_PURPLE:
9049 case EL_SP_INFOTRON:
9050 RaiseScore(level.score[SC_EMERALD]);
9053 RaiseScore(level.score[SC_DIAMOND]);
9056 RaiseScore(level.score[SC_CRYSTAL]);
9059 RaiseScore(level.score[SC_PEARL]);
9062 case EL_BD_BUTTERFLY:
9063 case EL_SP_ELECTRON:
9064 RaiseScore(level.score[SC_BUG]);
9068 case EL_SP_SNIKSNAK:
9069 RaiseScore(level.score[SC_SPACESHIP]);
9072 case EL_DARK_YAMYAM:
9073 RaiseScore(level.score[SC_YAMYAM]);
9076 RaiseScore(level.score[SC_ROBOT]);
9079 RaiseScore(level.score[SC_PACMAN]);
9082 RaiseScore(level.score[SC_NUT]);
9085 case EL_SP_DISK_RED:
9086 case EL_DYNABOMB_INCREASE_NUMBER:
9087 case EL_DYNABOMB_INCREASE_SIZE:
9088 case EL_DYNABOMB_INCREASE_POWER:
9089 RaiseScore(level.score[SC_DYNAMITE]);
9091 case EL_SHIELD_NORMAL:
9092 case EL_SHIELD_DEADLY:
9093 RaiseScore(level.score[SC_SHIELD]);
9096 RaiseScore(level.score[SC_TIME_BONUS]);
9102 RaiseScore(level.score[SC_KEY]);
9105 RaiseScore(element_info[element].collect_score);
9110 void RequestQuitGame(boolean ask_if_really_quit)
9112 if (AllPlayersGone ||
9113 !ask_if_really_quit ||
9114 level_editor_test_game ||
9115 Request("Do you really want to quit the game ?",
9116 REQ_ASK | REQ_STAY_CLOSED))
9118 #if defined(PLATFORM_UNIX)
9119 if (options.network)
9120 SendToServer_StopPlaying();
9124 game_status = GAME_MODE_MAIN;
9130 OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
9135 /* ---------- new game button stuff ---------------------------------------- */
9137 /* graphic position values for game buttons */
9138 #define GAME_BUTTON_XSIZE 30
9139 #define GAME_BUTTON_YSIZE 30
9140 #define GAME_BUTTON_XPOS 5
9141 #define GAME_BUTTON_YPOS 215
9142 #define SOUND_BUTTON_XPOS 5
9143 #define SOUND_BUTTON_YPOS (GAME_BUTTON_YPOS + GAME_BUTTON_YSIZE)
9145 #define GAME_BUTTON_STOP_XPOS (GAME_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
9146 #define GAME_BUTTON_PAUSE_XPOS (GAME_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
9147 #define GAME_BUTTON_PLAY_XPOS (GAME_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
9148 #define SOUND_BUTTON_MUSIC_XPOS (SOUND_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
9149 #define SOUND_BUTTON_LOOPS_XPOS (SOUND_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
9150 #define SOUND_BUTTON_SIMPLE_XPOS (SOUND_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
9157 } gamebutton_info[NUM_GAME_BUTTONS] =
9160 GAME_BUTTON_STOP_XPOS, GAME_BUTTON_YPOS,
9165 GAME_BUTTON_PAUSE_XPOS, GAME_BUTTON_YPOS,
9170 GAME_BUTTON_PLAY_XPOS, GAME_BUTTON_YPOS,
9175 SOUND_BUTTON_MUSIC_XPOS, SOUND_BUTTON_YPOS,
9176 SOUND_CTRL_ID_MUSIC,
9177 "background music on/off"
9180 SOUND_BUTTON_LOOPS_XPOS, SOUND_BUTTON_YPOS,
9181 SOUND_CTRL_ID_LOOPS,
9182 "sound loops on/off"
9185 SOUND_BUTTON_SIMPLE_XPOS, SOUND_BUTTON_YPOS,
9186 SOUND_CTRL_ID_SIMPLE,
9187 "normal sounds on/off"
9191 void CreateGameButtons()
9195 for (i = 0; i < NUM_GAME_BUTTONS; i++)
9197 Bitmap *gd_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
9198 struct GadgetInfo *gi;
9201 unsigned long event_mask;
9202 int gd_xoffset, gd_yoffset;
9203 int gd_x1, gd_x2, gd_y1, gd_y2;
9206 gd_xoffset = gamebutton_info[i].x;
9207 gd_yoffset = gamebutton_info[i].y;
9208 gd_x1 = DOOR_GFX_PAGEX4 + gd_xoffset;
9209 gd_x2 = DOOR_GFX_PAGEX3 + gd_xoffset;
9211 if (id == GAME_CTRL_ID_STOP ||
9212 id == GAME_CTRL_ID_PAUSE ||
9213 id == GAME_CTRL_ID_PLAY)
9215 button_type = GD_TYPE_NORMAL_BUTTON;
9217 event_mask = GD_EVENT_RELEASED;
9218 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
9219 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
9223 button_type = GD_TYPE_CHECK_BUTTON;
9225 ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
9226 (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
9227 (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
9228 event_mask = GD_EVENT_PRESSED;
9229 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset;
9230 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
9233 gi = CreateGadget(GDI_CUSTOM_ID, id,
9234 GDI_INFO_TEXT, gamebutton_info[i].infotext,
9235 GDI_X, DX + gd_xoffset,
9236 GDI_Y, DY + gd_yoffset,
9237 GDI_WIDTH, GAME_BUTTON_XSIZE,
9238 GDI_HEIGHT, GAME_BUTTON_YSIZE,
9239 GDI_TYPE, button_type,
9240 GDI_STATE, GD_BUTTON_UNPRESSED,
9241 GDI_CHECKED, checked,
9242 GDI_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y1,
9243 GDI_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y1,
9244 GDI_ALT_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y2,
9245 GDI_ALT_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y2,
9246 GDI_EVENT_MASK, event_mask,
9247 GDI_CALLBACK_ACTION, HandleGameButtons,
9251 Error(ERR_EXIT, "cannot create gadget");
9253 game_gadget[id] = gi;
9257 void FreeGameButtons()
9261 for (i = 0; i < NUM_GAME_BUTTONS; i++)
9262 FreeGadget(game_gadget[i]);
9265 static void MapGameButtons()
9269 for (i = 0; i < NUM_GAME_BUTTONS; i++)
9270 MapGadget(game_gadget[i]);
9273 void UnmapGameButtons()
9277 for (i = 0; i < NUM_GAME_BUTTONS; i++)
9278 UnmapGadget(game_gadget[i]);
9281 static void HandleGameButtons(struct GadgetInfo *gi)
9283 int id = gi->custom_id;
9285 if (game_status != GAME_MODE_PLAYING)
9290 case GAME_CTRL_ID_STOP:
9291 RequestQuitGame(TRUE);
9294 case GAME_CTRL_ID_PAUSE:
9295 if (options.network)
9297 #if defined(PLATFORM_UNIX)
9299 SendToServer_ContinuePlaying();
9301 SendToServer_PausePlaying();
9305 TapeTogglePause(TAPE_TOGGLE_MANUAL);
9308 case GAME_CTRL_ID_PLAY:
9311 #if defined(PLATFORM_UNIX)
9312 if (options.network)
9313 SendToServer_ContinuePlaying();
9317 tape.pausing = FALSE;
9318 DrawVideoDisplay(VIDEO_STATE_PAUSE_OFF,0);
9323 case SOUND_CTRL_ID_MUSIC:
9324 if (setup.sound_music)
9326 setup.sound_music = FALSE;
9329 else if (audio.music_available)
9331 setup.sound = setup.sound_music = TRUE;
9333 SetAudioMode(setup.sound);
9339 case SOUND_CTRL_ID_LOOPS:
9340 if (setup.sound_loops)
9341 setup.sound_loops = FALSE;
9342 else if (audio.loops_available)
9344 setup.sound = setup.sound_loops = TRUE;
9345 SetAudioMode(setup.sound);
9349 case SOUND_CTRL_ID_SIMPLE:
9350 if (setup.sound_simple)
9351 setup.sound_simple = FALSE;
9352 else if (audio.sound_available)
9354 setup.sound = setup.sound_simple = TRUE;
9355 SetAudioMode(setup.sound);