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))
102 #define ELEMENT_CAN_ENTER_FIELD_GENERIC(e, x, y, condition) \
103 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
105 (DONT_COLLIDE_WITH(e) && \
106 IS_FREE_OR_PLAYER(x, y))))
108 #define ELEMENT_CAN_ENTER_FIELD_GENERIC_2(x, y, condition) \
109 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
112 #define ELEMENT_CAN_ENTER_FIELD(e, x, y) \
113 ELEMENT_CAN_ENTER_FIELD_GENERIC(e, x, y, 0)
115 #define ELEMENT_CAN_ENTER_FIELD_OR_ACID(e, x, y) \
116 ELEMENT_CAN_ENTER_FIELD_GENERIC(e, x, y, (Feld[x][y] == EL_ACID))
118 #define ELEMENT_CAN_ENTER_FIELD_OR_ACID_2(x, y) \
119 ELEMENT_CAN_ENTER_FIELD_GENERIC_2(x, y, (Feld[x][y] == EL_ACID))
121 #define ENEMY_CAN_ENTER_FIELD(x, y) (IN_LEV_FIELD(x, y) && IS_FREE(x, y))
123 #define YAMYAM_CAN_ENTER_FIELD(x, y) \
124 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) || \
125 Feld[x][y] == EL_DIAMOND))
127 #define DARK_YAMYAM_CAN_ENTER_FIELD(x, y) \
128 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) || \
129 IS_FOOD_DARK_YAMYAM(Feld[x][y])))
131 #define PACMAN_CAN_ENTER_FIELD(x, y) \
132 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) || \
133 IS_AMOEBOID(Feld[x][y])))
135 #define PIG_CAN_ENTER_FIELD(x, y) \
136 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
137 IS_FOOD_PIG(Feld[x][y])))
139 #define PENGUIN_CAN_ENTER_FIELD(x, y) \
140 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
141 IS_FOOD_PENGUIN(Feld[x][y]) || \
142 Feld[x][y] == EL_EXIT_OPEN || \
143 Feld[x][y] == EL_ACID))
145 #define MOLE_CAN_ENTER_FIELD(x, y, condition) \
146 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || (condition)))
148 #define IN_LEV_FIELD_AND_IS_FREE(x, y) (IN_LEV_FIELD(x, y) && IS_FREE(x, y))
149 #define IN_LEV_FIELD_AND_NOT_FREE(x, y) (IN_LEV_FIELD(x, y) && !IS_FREE(x, y))
151 /* game button identifiers */
152 #define GAME_CTRL_ID_STOP 0
153 #define GAME_CTRL_ID_PAUSE 1
154 #define GAME_CTRL_ID_PLAY 2
155 #define SOUND_CTRL_ID_MUSIC 3
156 #define SOUND_CTRL_ID_LOOPS 4
157 #define SOUND_CTRL_ID_SIMPLE 5
159 #define NUM_GAME_BUTTONS 6
162 /* forward declaration for internal use */
164 static boolean MovePlayerOneStep(struct PlayerInfo *, int, int, int, int);
165 static boolean MovePlayer(struct PlayerInfo *, int, int);
166 static void ScrollPlayer(struct PlayerInfo *, int);
167 static void ScrollScreen(struct PlayerInfo *, int);
169 static void InitBeltMovement(void);
170 static void CloseAllOpenTimegates(void);
171 static void CheckGravityMovement(struct PlayerInfo *);
172 static void KillHeroUnlessProtected(int, int);
174 static void TestIfPlayerTouchesCustomElement(int, int);
175 static void TestIfElementTouchesCustomElement(int, int);
177 static void ChangeElement(int, int, int);
178 static boolean CheckTriggeredElementSideChange(int, int, int, int, int);
179 static boolean CheckTriggeredElementChange(int, int, int, int);
180 static boolean CheckElementSideChange(int, int, int, int, int, int);
181 static boolean CheckElementChange(int, int, int, int);
183 static void PlaySoundLevel(int, int, int);
184 static void PlaySoundLevelNearest(int, int, int);
185 static void PlaySoundLevelAction(int, int, int);
186 static void PlaySoundLevelElementAction(int, int, int, int);
187 static void PlaySoundLevelActionIfLoop(int, int, int);
188 static void StopSoundLevelActionIfLoop(int, int, int);
190 static void MapGameButtons();
191 static void HandleGameButtons(struct GadgetInfo *);
193 static struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS];
196 /* ------------------------------------------------------------------------- */
197 /* definition of elements that automatically change to other elements after */
198 /* a specified time, eventually calling a function when changing */
199 /* ------------------------------------------------------------------------- */
201 /* forward declaration for changer functions */
202 static void InitBuggyBase(int x, int y);
203 static void WarnBuggyBase(int x, int y);
205 static void InitTrap(int x, int y);
206 static void ActivateTrap(int x, int y);
207 static void ChangeActiveTrap(int x, int y);
209 static void InitRobotWheel(int x, int y);
210 static void RunRobotWheel(int x, int y);
211 static void StopRobotWheel(int x, int y);
213 static void InitTimegateWheel(int x, int y);
214 static void RunTimegateWheel(int x, int y);
216 struct ChangingElementInfo
221 void (*pre_change_function)(int x, int y);
222 void (*change_function)(int x, int y);
223 void (*post_change_function)(int x, int y);
226 static struct ChangingElementInfo change_delay_list[] =
277 EL_SWITCHGATE_OPENING,
285 EL_SWITCHGATE_CLOSING,
286 EL_SWITCHGATE_CLOSED,
318 EL_ACID_SPLASH_RIGHT,
327 EL_SP_BUGGY_BASE_ACTIVATING,
334 EL_SP_BUGGY_BASE_ACTIVATING,
335 EL_SP_BUGGY_BASE_ACTIVE,
342 EL_SP_BUGGY_BASE_ACTIVE,
366 EL_ROBOT_WHEEL_ACTIVE,
374 EL_TIMEGATE_SWITCH_ACTIVE,
395 int push_delay_fixed, push_delay_random;
400 { EL_BALLOON, 0, 0 },
402 { EL_SOKOBAN_OBJECT, 2, 0 },
403 { EL_SOKOBAN_FIELD_FULL, 2, 0 },
404 { EL_SATELLITE, 2, 0 },
405 { EL_SP_DISK_YELLOW, 2, 0 },
407 { EL_UNDEFINED, 0, 0 },
415 move_stepsize_list[] =
417 { EL_AMOEBA_DROP, 2 },
418 { EL_AMOEBA_DROPPING, 2 },
419 { EL_QUICKSAND_FILLING, 1 },
420 { EL_QUICKSAND_EMPTYING, 1 },
421 { EL_MAGIC_WALL_FILLING, 2 },
422 { EL_BD_MAGIC_WALL_FILLING, 2 },
423 { EL_MAGIC_WALL_EMPTYING, 2 },
424 { EL_BD_MAGIC_WALL_EMPTYING, 2 },
434 collect_count_list[] =
437 { EL_BD_DIAMOND, 1 },
438 { EL_EMERALD_YELLOW, 1 },
439 { EL_EMERALD_RED, 1 },
440 { EL_EMERALD_PURPLE, 1 },
442 { EL_SP_INFOTRON, 1 },
449 static unsigned long trigger_events[MAX_NUM_ELEMENTS];
451 #define IS_AUTO_CHANGING(e) (element_info[e].change_events & \
452 CH_EVENT_BIT(CE_DELAY))
453 #define IS_JUST_CHANGING(x, y) (ChangeDelay[x][y] != 0)
454 #define IS_CHANGING(x, y) (IS_AUTO_CHANGING(Feld[x][y]) || \
455 IS_JUST_CHANGING(x, y))
457 #define CE_PAGE(e, ce) (element_info[e].event_page[ce])
460 void GetPlayerConfig()
462 if (!audio.sound_available)
463 setup.sound_simple = FALSE;
465 if (!audio.loops_available)
466 setup.sound_loops = FALSE;
468 if (!audio.music_available)
469 setup.sound_music = FALSE;
471 if (!video.fullscreen_available)
472 setup.fullscreen = FALSE;
474 setup.sound = (setup.sound_simple || setup.sound_loops || setup.sound_music);
476 SetAudioMode(setup.sound);
480 static int getBeltNrFromBeltElement(int element)
482 return (element < EL_CONVEYOR_BELT_2_LEFT ? 0 :
483 element < EL_CONVEYOR_BELT_3_LEFT ? 1 :
484 element < EL_CONVEYOR_BELT_4_LEFT ? 2 : 3);
487 static int getBeltNrFromBeltActiveElement(int element)
489 return (element < EL_CONVEYOR_BELT_2_LEFT_ACTIVE ? 0 :
490 element < EL_CONVEYOR_BELT_3_LEFT_ACTIVE ? 1 :
491 element < EL_CONVEYOR_BELT_4_LEFT_ACTIVE ? 2 : 3);
494 static int getBeltNrFromBeltSwitchElement(int element)
496 return (element < EL_CONVEYOR_BELT_2_SWITCH_LEFT ? 0 :
497 element < EL_CONVEYOR_BELT_3_SWITCH_LEFT ? 1 :
498 element < EL_CONVEYOR_BELT_4_SWITCH_LEFT ? 2 : 3);
501 static int getBeltDirNrFromBeltSwitchElement(int element)
503 static int belt_base_element[4] =
505 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
506 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
507 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
508 EL_CONVEYOR_BELT_4_SWITCH_LEFT
511 int belt_nr = getBeltNrFromBeltSwitchElement(element);
512 int belt_dir_nr = element - belt_base_element[belt_nr];
514 return (belt_dir_nr % 3);
517 static int getBeltDirFromBeltSwitchElement(int element)
519 static int belt_move_dir[3] =
526 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(element);
528 return belt_move_dir[belt_dir_nr];
531 static void InitPlayerField(int x, int y, int element, boolean init_game)
533 if (element == EL_SP_MURPHY)
537 if (stored_player[0].present)
539 Feld[x][y] = EL_SP_MURPHY_CLONE;
545 stored_player[0].use_murphy_graphic = TRUE;
548 Feld[x][y] = EL_PLAYER_1;
554 struct PlayerInfo *player = &stored_player[Feld[x][y] - EL_PLAYER_1];
555 int jx = player->jx, jy = player->jy;
557 player->present = TRUE;
559 if (!options.network || player->connected)
561 player->active = TRUE;
563 /* remove potentially duplicate players */
564 if (StorePlayer[jx][jy] == Feld[x][y])
565 StorePlayer[jx][jy] = 0;
567 StorePlayer[x][y] = Feld[x][y];
571 printf("Player %d activated.\n", player->element_nr);
572 printf("[Local player is %d and currently %s.]\n",
573 local_player->element_nr,
574 local_player->active ? "active" : "not active");
578 Feld[x][y] = EL_EMPTY;
579 player->jx = player->last_jx = x;
580 player->jy = player->last_jy = y;
584 static void InitField(int x, int y, boolean init_game)
586 int element = Feld[x][y];
595 InitPlayerField(x, y, element, init_game);
599 if (x < lev_fieldx-1 && Feld[x+1][y] == EL_ACID)
600 Feld[x][y] = EL_ACID_POOL_TOPLEFT;
601 else if (x > 0 && Feld[x-1][y] == EL_ACID)
602 Feld[x][y] = EL_ACID_POOL_TOPRIGHT;
603 else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPLEFT)
604 Feld[x][y] = EL_ACID_POOL_BOTTOMLEFT;
605 else if (y > 0 && Feld[x][y-1] == EL_ACID)
606 Feld[x][y] = EL_ACID_POOL_BOTTOM;
607 else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPRIGHT)
608 Feld[x][y] = EL_ACID_POOL_BOTTOMRIGHT;
616 case EL_SPACESHIP_RIGHT:
617 case EL_SPACESHIP_UP:
618 case EL_SPACESHIP_LEFT:
619 case EL_SPACESHIP_DOWN:
621 case EL_BD_BUTTERFLY_RIGHT:
622 case EL_BD_BUTTERFLY_UP:
623 case EL_BD_BUTTERFLY_LEFT:
624 case EL_BD_BUTTERFLY_DOWN:
625 case EL_BD_BUTTERFLY:
626 case EL_BD_FIREFLY_RIGHT:
627 case EL_BD_FIREFLY_UP:
628 case EL_BD_FIREFLY_LEFT:
629 case EL_BD_FIREFLY_DOWN:
631 case EL_PACMAN_RIGHT:
655 if (y == lev_fieldy - 1)
657 Feld[x][y] = EL_AMOEBA_GROWING;
658 Store[x][y] = EL_AMOEBA_WET;
662 case EL_DYNAMITE_ACTIVE:
667 local_player->lights_still_needed++;
670 case EL_SOKOBAN_FIELD_EMPTY:
671 local_player->sokobanfields_still_needed++;
675 local_player->friends_still_needed++;
680 MovDir[x][y] = 1 << RND(4);
685 Feld[x][y] = EL_EMPTY;
690 case EL_EM_KEY_1_FILE:
691 Feld[x][y] = EL_EM_KEY_1;
693 case EL_EM_KEY_2_FILE:
694 Feld[x][y] = EL_EM_KEY_2;
696 case EL_EM_KEY_3_FILE:
697 Feld[x][y] = EL_EM_KEY_3;
699 case EL_EM_KEY_4_FILE:
700 Feld[x][y] = EL_EM_KEY_4;
704 case EL_CONVEYOR_BELT_1_SWITCH_LEFT:
705 case EL_CONVEYOR_BELT_1_SWITCH_MIDDLE:
706 case EL_CONVEYOR_BELT_1_SWITCH_RIGHT:
707 case EL_CONVEYOR_BELT_2_SWITCH_LEFT:
708 case EL_CONVEYOR_BELT_2_SWITCH_MIDDLE:
709 case EL_CONVEYOR_BELT_2_SWITCH_RIGHT:
710 case EL_CONVEYOR_BELT_3_SWITCH_LEFT:
711 case EL_CONVEYOR_BELT_3_SWITCH_MIDDLE:
712 case EL_CONVEYOR_BELT_3_SWITCH_RIGHT:
713 case EL_CONVEYOR_BELT_4_SWITCH_LEFT:
714 case EL_CONVEYOR_BELT_4_SWITCH_MIDDLE:
715 case EL_CONVEYOR_BELT_4_SWITCH_RIGHT:
718 int belt_nr = getBeltNrFromBeltSwitchElement(Feld[x][y]);
719 int belt_dir = getBeltDirFromBeltSwitchElement(Feld[x][y]);
720 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(Feld[x][y]);
722 if (game.belt_dir_nr[belt_nr] == 3) /* initial value */
724 game.belt_dir[belt_nr] = belt_dir;
725 game.belt_dir_nr[belt_nr] = belt_dir_nr;
727 else /* more than one switch -- set it like the first switch */
729 Feld[x][y] = Feld[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
734 case EL_SWITCHGATE_SWITCH_DOWN: /* always start with same switch pos */
736 Feld[x][y] = EL_SWITCHGATE_SWITCH_UP;
739 case EL_LIGHT_SWITCH_ACTIVE:
741 game.light_time_left = level.time_light * FRAMES_PER_SECOND;
745 if (IS_CUSTOM_ELEMENT(element) && CAN_MOVE(element))
751 void DrawGameDoorValues()
755 for (i=0; i<MAX_PLAYERS; i++)
757 if (stored_player[i].key[j])
758 DrawMiniGraphicExt(drawto, DX_KEYS + j * MINI_TILEX, DY_KEYS,
759 el2edimg(EL_KEY_1 + j));
761 DrawText(DX + XX_EMERALDS, DY + YY_EMERALDS,
762 int2str(local_player->gems_still_needed, 3), FONT_TEXT_2);
763 DrawText(DX + XX_DYNAMITE, DY + YY_DYNAMITE,
764 int2str(local_player->inventory_size, 3), FONT_TEXT_2);
765 DrawText(DX + XX_SCORE, DY + YY_SCORE,
766 int2str(local_player->score, 5), FONT_TEXT_2);
767 DrawText(DX + XX_TIME, DY + YY_TIME,
768 int2str(TimeLeft, 3), FONT_TEXT_2);
773 =============================================================================
775 -----------------------------------------------------------------------------
776 initialize game engine due to level / tape version number
777 =============================================================================
780 static void InitGameEngine()
784 /* set game engine from tape file when re-playing, else from level file */
785 game.engine_version = (tape.playing ? tape.engine_version :
788 /* dynamically adjust element properties according to game engine version */
789 InitElementPropertiesEngine(game.engine_version);
792 printf("level %d: level version == %06d\n", level_nr, level.game_version);
793 printf(" tape version == %06d [%s] [file: %06d]\n",
794 tape.engine_version, (tape.playing ? "PLAYING" : "RECORDING"),
796 printf(" => game.engine_version == %06d\n", game.engine_version);
799 /* ---------- initialize player's initial move delay --------------------- */
801 /* dynamically adjust player properties according to game engine version */
802 game.initial_move_delay =
803 (game.engine_version <= VERSION_IDENT(2,0,1,0) ? INITIAL_MOVE_DELAY_ON :
804 INITIAL_MOVE_DELAY_OFF);
806 /* dynamically adjust player properties according to level information */
807 game.initial_move_delay_value =
808 (level.double_speed ? MOVE_DELAY_HIGH_SPEED : MOVE_DELAY_NORMAL_SPEED);
810 /* ---------- initialize player's initial push delay --------------------- */
812 /* dynamically adjust player properties according to game engine version */
813 game.initial_push_delay_value =
814 (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
816 /* ---------- initialize changing elements ------------------------------- */
818 /* initialize changing elements information */
819 for (i=0; i < MAX_NUM_ELEMENTS; i++)
821 struct ElementInfo *ei = &element_info[i];
823 /* this pointer might have been changed in the level editor */
824 ei->change = &ei->change_page[0];
826 if (!IS_CUSTOM_ELEMENT(i))
828 ei->change->target_element = EL_EMPTY_SPACE;
829 ei->change->delay_fixed = 0;
830 ei->change->delay_random = 0;
831 ei->change->delay_frames = 1;
834 ei->change_events = CE_BITMASK_DEFAULT;
835 for (j=0; j < NUM_CHANGE_EVENTS; j++)
837 ei->event_page_nr[j] = 0;
838 ei->event_page[j] = &ei->change_page[0];
842 /* add changing elements from pre-defined list */
843 for (i=0; change_delay_list[i].element != EL_UNDEFINED; i++)
845 struct ChangingElementInfo *ch_delay = &change_delay_list[i];
846 struct ElementInfo *ei = &element_info[ch_delay->element];
848 ei->change->target_element = ch_delay->target_element;
849 ei->change->delay_fixed = ch_delay->change_delay;
851 ei->change->pre_change_function = ch_delay->pre_change_function;
852 ei->change->change_function = ch_delay->change_function;
853 ei->change->post_change_function = ch_delay->post_change_function;
855 ei->change_events |= CH_EVENT_BIT(CE_DELAY);
859 /* add change events from custom element configuration */
860 for (i=0; i < NUM_CUSTOM_ELEMENTS; i++)
862 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
864 for (j=0; j < ei->num_change_pages; j++)
866 if (!ei->change_page[j].can_change)
869 for (k=0; k < NUM_CHANGE_EVENTS; k++)
871 /* only add event page for the first page found with this event */
872 if (ei->change_page[j].events & CH_EVENT_BIT(k) &&
873 !(ei->change_events & CH_EVENT_BIT(k)))
875 ei->change_events |= CH_EVENT_BIT(k);
876 ei->event_page_nr[k] = j;
877 ei->event_page[k] = &ei->change_page[j];
885 /* add change events from custom element configuration */
886 for (i=0; i < NUM_CUSTOM_ELEMENTS; i++)
888 int element = EL_CUSTOM_START + i;
890 /* only add custom elements that change after fixed/random frame delay */
891 if (CAN_CHANGE(element) && HAS_CHANGE_EVENT(element, CE_DELAY))
892 element_info[element].change_events |= CH_EVENT_BIT(CE_DELAY);
896 /* ---------- initialize trigger events ---------------------------------- */
898 /* initialize trigger events information */
899 for (i=0; i<MAX_NUM_ELEMENTS; i++)
900 trigger_events[i] = EP_BITMASK_DEFAULT;
903 /* add trigger events from element change event properties */
904 for (i=0; i<MAX_NUM_ELEMENTS; i++)
906 struct ElementInfo *ei = &element_info[i];
908 for (j=0; j < ei->num_change_pages; j++)
910 if (!ei->change_page[j].can_change)
913 if (ei->change_page[j].events & CH_EVENT_BIT(CE_BY_OTHER_ACTION))
915 int trigger_element = ei->change_page[j].trigger_element;
917 trigger_events[trigger_element] |= ei->change_page[j].events;
922 /* add trigger events from element change event properties */
923 for (i=0; i<MAX_NUM_ELEMENTS; i++)
924 if (HAS_CHANGE_EVENT(i, CE_BY_OTHER_ACTION))
925 trigger_events[element_info[i].change->trigger_element] |=
926 element_info[i].change->events;
929 /* ---------- initialize push delay -------------------------------------- */
931 /* initialize push delay values to default */
932 for (i=0; i<MAX_NUM_ELEMENTS; i++)
934 if (!IS_CUSTOM_ELEMENT(i))
936 element_info[i].push_delay_fixed = game.default_push_delay_fixed;
937 element_info[i].push_delay_random = game.default_push_delay_random;
941 /* set push delay value for certain elements from pre-defined list */
942 for (i=0; push_delay_list[i].element != EL_UNDEFINED; i++)
944 int e = push_delay_list[i].element;
946 element_info[e].push_delay_fixed = push_delay_list[i].push_delay_fixed;
947 element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
950 /* ---------- initialize move stepsize ----------------------------------- */
952 /* initialize move stepsize values to default */
953 for (i=0; i<MAX_NUM_ELEMENTS; i++)
954 if (!IS_CUSTOM_ELEMENT(i))
955 element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
957 /* set move stepsize value for certain elements from pre-defined list */
958 for (i=0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
960 int e = move_stepsize_list[i].element;
962 element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
965 /* ---------- initialize gem count --------------------------------------- */
967 /* initialize gem count values for each element */
968 for (i=0; i<MAX_NUM_ELEMENTS; i++)
969 if (!IS_CUSTOM_ELEMENT(i))
970 element_info[i].collect_count = 0;
972 /* add gem count values for all elements from pre-defined list */
973 for (i=0; collect_count_list[i].element != EL_UNDEFINED; i++)
974 element_info[collect_count_list[i].element].collect_count =
975 collect_count_list[i].count;
980 =============================================================================
982 -----------------------------------------------------------------------------
983 initialize and start new game
984 =============================================================================
989 boolean emulate_bd = TRUE; /* unless non-BOULDERDASH elements found */
990 boolean emulate_sb = TRUE; /* unless non-SOKOBAN elements found */
991 boolean emulate_sp = TRUE; /* unless non-SUPAPLEX elements found */
998 #if USE_NEW_AMOEBA_CODE
999 printf("Using new amoeba code.\n");
1001 printf("Using old amoeba code.\n");
1006 /* don't play tapes over network */
1007 network_playing = (options.network && !tape.playing);
1009 for (i=0; i<MAX_PLAYERS; i++)
1011 struct PlayerInfo *player = &stored_player[i];
1013 player->index_nr = i;
1014 player->element_nr = EL_PLAYER_1 + i;
1016 player->present = FALSE;
1017 player->active = FALSE;
1020 player->effective_action = 0;
1021 player->programmed_action = 0;
1024 player->gems_still_needed = level.gems_needed;
1025 player->sokobanfields_still_needed = 0;
1026 player->lights_still_needed = 0;
1027 player->friends_still_needed = 0;
1030 player->key[j] = FALSE;
1032 player->dynabomb_count = 0;
1033 player->dynabomb_size = 1;
1034 player->dynabombs_left = 0;
1035 player->dynabomb_xl = FALSE;
1037 player->MovDir = MV_NO_MOVING;
1040 player->GfxDir = MV_NO_MOVING;
1041 player->GfxAction = ACTION_DEFAULT;
1043 player->StepFrame = 0;
1045 player->use_murphy_graphic = FALSE;
1047 player->actual_frame_counter = 0;
1049 player->last_move_dir = MV_NO_MOVING;
1051 player->is_waiting = FALSE;
1052 player->is_moving = FALSE;
1053 player->is_digging = FALSE;
1054 player->is_snapping = FALSE;
1055 player->is_collecting = FALSE;
1056 player->is_pushing = FALSE;
1057 player->is_switching = FALSE;
1059 player->switch_x = -1;
1060 player->switch_y = -1;
1062 player->show_envelope = 0;
1064 player->move_delay = game.initial_move_delay;
1065 player->move_delay_value = game.initial_move_delay_value;
1067 player->push_delay = 0;
1068 player->push_delay_value = game.initial_push_delay_value;
1070 player->last_jx = player->last_jy = 0;
1071 player->jx = player->jy = 0;
1073 player->shield_normal_time_left = 0;
1074 player->shield_deadly_time_left = 0;
1076 player->inventory_size = 0;
1078 DigField(player, 0, 0, 0, 0, DF_NO_PUSH);
1079 SnapField(player, 0, 0);
1081 player->LevelSolved = FALSE;
1082 player->GameOver = FALSE;
1085 network_player_action_received = FALSE;
1087 #if defined(PLATFORM_UNIX)
1088 /* initial null action */
1089 if (network_playing)
1090 SendToServer_MovePlayer(MV_NO_MOVING);
1098 TimeLeft = level.time;
1100 ScreenMovDir = MV_NO_MOVING;
1104 ScrollStepSize = 0; /* will be correctly initialized by ScrollScreen() */
1106 AllPlayersGone = FALSE;
1108 game.yamyam_content_nr = 0;
1109 game.magic_wall_active = FALSE;
1110 game.magic_wall_time_left = 0;
1111 game.light_time_left = 0;
1112 game.timegate_time_left = 0;
1113 game.switchgate_pos = 0;
1114 game.balloon_dir = MV_NO_MOVING;
1115 game.gravity = level.initial_gravity;
1116 game.explosions_delayed = TRUE;
1118 game.envelope_active = FALSE;
1122 game.belt_dir[i] = MV_NO_MOVING;
1123 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
1126 for (i=0; i<MAX_NUM_AMOEBA; i++)
1127 AmoebaCnt[i] = AmoebaCnt2[i] = 0;
1129 for (x=0; x<lev_fieldx; x++)
1131 for (y=0; y<lev_fieldy; y++)
1133 Feld[x][y] = level.field[x][y];
1134 MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
1135 ChangeDelay[x][y] = 0;
1136 ChangePage[x][y] = -1;
1137 Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
1139 WasJustMoving[x][y] = 0;
1140 WasJustFalling[x][y] = 0;
1142 Pushed[x][y] = FALSE;
1144 Changed[x][y] = CE_BITMASK_DEFAULT;
1145 ChangeEvent[x][y] = CE_BITMASK_DEFAULT;
1147 ExplodePhase[x][y] = 0;
1148 ExplodeField[x][y] = EX_NO_EXPLOSION;
1151 GfxAction[x][y] = ACTION_DEFAULT;
1152 GfxRandom[x][y] = INIT_GFX_RANDOM();
1153 GfxElement[x][y] = EL_UNDEFINED;
1157 for(y=0; y<lev_fieldy; y++)
1159 for(x=0; x<lev_fieldx; x++)
1161 if (emulate_bd && !IS_BD_ELEMENT(Feld[x][y]))
1163 if (emulate_sb && !IS_SB_ELEMENT(Feld[x][y]))
1165 if (emulate_sp && !IS_SP_ELEMENT(Feld[x][y]))
1168 InitField(x, y, TRUE);
1174 game.emulation = (emulate_bd ? EMU_BOULDERDASH :
1175 emulate_sb ? EMU_SOKOBAN :
1176 emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
1178 /* correct non-moving belts to start moving left */
1180 if (game.belt_dir[i] == MV_NO_MOVING)
1181 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
1183 /* check if any connected player was not found in playfield */
1184 for (i=0; i<MAX_PLAYERS; i++)
1186 struct PlayerInfo *player = &stored_player[i];
1188 if (player->connected && !player->present)
1190 for (j=0; j<MAX_PLAYERS; j++)
1192 struct PlayerInfo *some_player = &stored_player[j];
1193 int jx = some_player->jx, jy = some_player->jy;
1195 /* assign first free player found that is present in the playfield */
1196 if (some_player->present && !some_player->connected)
1198 player->present = TRUE;
1199 player->active = TRUE;
1200 some_player->present = FALSE;
1202 StorePlayer[jx][jy] = player->element_nr;
1203 player->jx = player->last_jx = jx;
1204 player->jy = player->last_jy = jy;
1214 /* when playing a tape, eliminate all players who do not participate */
1216 for (i=0; i<MAX_PLAYERS; i++)
1218 if (stored_player[i].active && !tape.player_participates[i])
1220 struct PlayerInfo *player = &stored_player[i];
1221 int jx = player->jx, jy = player->jy;
1223 player->active = FALSE;
1224 StorePlayer[jx][jy] = 0;
1225 Feld[jx][jy] = EL_EMPTY;
1229 else if (!options.network && !setup.team_mode) /* && !tape.playing */
1231 /* when in single player mode, eliminate all but the first active player */
1233 for (i=0; i<MAX_PLAYERS; i++)
1235 if (stored_player[i].active)
1237 for (j=i+1; j<MAX_PLAYERS; j++)
1239 if (stored_player[j].active)
1241 struct PlayerInfo *player = &stored_player[j];
1242 int jx = player->jx, jy = player->jy;
1244 player->active = FALSE;
1245 StorePlayer[jx][jy] = 0;
1246 Feld[jx][jy] = EL_EMPTY;
1253 /* when recording the game, store which players take part in the game */
1256 for (i=0; i<MAX_PLAYERS; i++)
1257 if (stored_player[i].active)
1258 tape.player_participates[i] = TRUE;
1263 for (i=0; i<MAX_PLAYERS; i++)
1265 struct PlayerInfo *player = &stored_player[i];
1267 printf("Player %d: present == %d, connected == %d, active == %d.\n",
1272 if (local_player == player)
1273 printf("Player %d is local player.\n", i+1);
1277 if (BorderElement == EL_EMPTY)
1280 SBX_Right = lev_fieldx - SCR_FIELDX;
1282 SBY_Lower = lev_fieldy - SCR_FIELDY;
1287 SBX_Right = lev_fieldx - SCR_FIELDX + 1;
1289 SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
1292 if (lev_fieldx + (SBX_Left == -1 ? 2 : 0) <= SCR_FIELDX)
1293 SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
1295 if (lev_fieldy + (SBY_Upper == -1 ? 2 : 0) <= SCR_FIELDY)
1296 SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
1298 /* if local player not found, look for custom element that might create
1299 the player (make some assumptions about the right custom element) */
1300 if (!local_player->present)
1302 int start_x = 0, start_y = 0;
1303 int found_rating = 0;
1304 int found_element = EL_UNDEFINED;
1306 for(y=0; y < lev_fieldy; y++) for(x=0; x < lev_fieldx; x++)
1308 int element = Feld[x][y];
1313 if (!IS_CUSTOM_ELEMENT(element))
1316 if (CAN_CHANGE(element))
1318 for (i=0; i < element_info[element].num_change_pages; i++)
1320 content = element_info[element].change_page[i].target_element;
1321 is_player = ELEM_IS_PLAYER(content);
1323 if (is_player && (found_rating < 3 || element < found_element))
1329 found_element = element;
1334 for(yy=0; yy < 3; yy++) for(xx=0; xx < 3; xx++)
1336 content = element_info[element].content[xx][yy];
1337 is_player = ELEM_IS_PLAYER(content);
1339 if (is_player && (found_rating < 2 || element < found_element))
1341 start_x = x + xx - 1;
1342 start_y = y + yy - 1;
1345 found_element = element;
1348 if (!CAN_CHANGE(element))
1351 for (i=0; i < element_info[element].num_change_pages; i++)
1353 content = element_info[element].change_page[i].content[xx][yy];
1354 is_player = ELEM_IS_PLAYER(content);
1356 if (is_player && (found_rating < 1 || element < found_element))
1358 start_x = x + xx - 1;
1359 start_y = y + yy - 1;
1362 found_element = element;
1368 scroll_x = (start_x < SBX_Left + MIDPOSX ? SBX_Left :
1369 start_x > SBX_Right + MIDPOSX ? SBX_Right :
1372 scroll_y = (start_y < SBY_Upper + MIDPOSY ? SBY_Upper :
1373 start_y > SBY_Lower + MIDPOSY ? SBY_Lower :
1379 scroll_x = (local_player->jx < SBX_Left + MIDPOSX ? SBX_Left :
1380 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
1381 local_player->jx - MIDPOSX);
1383 scroll_y = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
1384 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
1385 local_player->jy - MIDPOSY);
1387 scroll_x = SBX_Left;
1388 scroll_y = SBY_Upper;
1389 if (local_player->jx >= SBX_Left + MIDPOSX)
1390 scroll_x = (local_player->jx <= SBX_Right + MIDPOSX ?
1391 local_player->jx - MIDPOSX :
1393 if (local_player->jy >= SBY_Upper + MIDPOSY)
1394 scroll_y = (local_player->jy <= SBY_Lower + MIDPOSY ?
1395 local_player->jy - MIDPOSY :
1400 CloseDoor(DOOR_CLOSE_1);
1405 /* after drawing the level, correct some elements */
1406 if (game.timegate_time_left == 0)
1407 CloseAllOpenTimegates();
1409 if (setup.soft_scrolling)
1410 BlitBitmap(fieldbuffer, backbuffer, FX, FY, SXSIZE, SYSIZE, SX, SY);
1412 redraw_mask |= REDRAW_FROM_BACKBUFFER;
1415 /* copy default game door content to main double buffer */
1416 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
1417 DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
1420 DrawText(DX + XX_LEVEL, DY + YY_LEVEL, int2str(level_nr, 2), FONT_TEXT_2);
1423 DrawTextExt(drawto, DX + XX_EMERALDS, DY + YY_EMERALDS,
1424 int2str(level_nr, 3), FONT_LEVEL_NUMBER, BLIT_OPAQUE);
1425 BlitBitmap(drawto, drawto,
1426 DX + XX_EMERALDS, DY + YY_EMERALDS + 1,
1427 getFontWidth(FONT_LEVEL_NUMBER) * 3,
1428 getFontHeight(FONT_LEVEL_NUMBER) - 1,
1429 DX + XX_LEVEL - 1, DY + YY_LEVEL + 1);
1432 DrawGameDoorValues();
1436 game_gadget[SOUND_CTRL_ID_MUSIC]->checked = setup.sound_music;
1437 game_gadget[SOUND_CTRL_ID_LOOPS]->checked = setup.sound_loops;
1438 game_gadget[SOUND_CTRL_ID_SIMPLE]->checked = setup.sound_simple;
1442 /* copy actual game door content to door double buffer for OpenDoor() */
1443 BlitBitmap(drawto, bitmap_db_door,
1444 DX, DY, DXSIZE, DYSIZE, DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1);
1446 OpenDoor(DOOR_OPEN_ALL);
1448 PlaySoundStereo(SND_GAME_STARTING, SOUND_MIDDLE);
1449 if (setup.sound_music)
1450 PlayMusic(level_nr);
1452 KeyboardAutoRepeatOffUnlessAutoplay();
1457 printf("Player %d %sactive.\n",
1458 i + 1, (stored_player[i].active ? "" : "not "));
1462 void InitMovDir(int x, int y)
1464 int i, element = Feld[x][y];
1465 static int xy[4][2] =
1472 static int direction[3][4] =
1474 { MV_RIGHT, MV_UP, MV_LEFT, MV_DOWN },
1475 { MV_LEFT, MV_DOWN, MV_RIGHT, MV_UP },
1476 { MV_LEFT, MV_RIGHT, MV_UP, MV_DOWN }
1485 Feld[x][y] = EL_BUG;
1486 MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
1489 case EL_SPACESHIP_RIGHT:
1490 case EL_SPACESHIP_UP:
1491 case EL_SPACESHIP_LEFT:
1492 case EL_SPACESHIP_DOWN:
1493 Feld[x][y] = EL_SPACESHIP;
1494 MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
1497 case EL_BD_BUTTERFLY_RIGHT:
1498 case EL_BD_BUTTERFLY_UP:
1499 case EL_BD_BUTTERFLY_LEFT:
1500 case EL_BD_BUTTERFLY_DOWN:
1501 Feld[x][y] = EL_BD_BUTTERFLY;
1502 MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
1505 case EL_BD_FIREFLY_RIGHT:
1506 case EL_BD_FIREFLY_UP:
1507 case EL_BD_FIREFLY_LEFT:
1508 case EL_BD_FIREFLY_DOWN:
1509 Feld[x][y] = EL_BD_FIREFLY;
1510 MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
1513 case EL_PACMAN_RIGHT:
1515 case EL_PACMAN_LEFT:
1516 case EL_PACMAN_DOWN:
1517 Feld[x][y] = EL_PACMAN;
1518 MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
1521 case EL_SP_SNIKSNAK:
1522 MovDir[x][y] = MV_UP;
1525 case EL_SP_ELECTRON:
1526 MovDir[x][y] = MV_LEFT;
1533 Feld[x][y] = EL_MOLE;
1534 MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
1538 if (IS_CUSTOM_ELEMENT(element))
1540 if (element_info[element].move_direction_initial != MV_NO_MOVING)
1541 MovDir[x][y] = element_info[element].move_direction_initial;
1542 else if (element_info[element].move_pattern == MV_ALL_DIRECTIONS ||
1543 element_info[element].move_pattern == MV_TURNING_LEFT ||
1544 element_info[element].move_pattern == MV_TURNING_RIGHT)
1545 MovDir[x][y] = 1 << RND(4);
1546 else if (element_info[element].move_pattern == MV_HORIZONTAL)
1547 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
1548 else if (element_info[element].move_pattern == MV_VERTICAL)
1549 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
1550 else if (element_info[element].move_pattern & MV_ANY_DIRECTION)
1551 MovDir[x][y] = element_info[element].move_pattern;
1552 else if (element_info[element].move_pattern == MV_ALONG_LEFT_SIDE ||
1553 element_info[element].move_pattern == MV_ALONG_RIGHT_SIDE)
1557 int x1 = x + xy[i][0];
1558 int y1 = y + xy[i][1];
1560 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
1562 if (element_info[element].move_pattern == MV_ALONG_RIGHT_SIDE)
1563 MovDir[x][y] = direction[0][i];
1565 MovDir[x][y] = direction[1][i];
1574 MovDir[x][y] = 1 << RND(4);
1576 if (element != EL_BUG &&
1577 element != EL_SPACESHIP &&
1578 element != EL_BD_BUTTERFLY &&
1579 element != EL_BD_FIREFLY)
1584 int x1 = x + xy[i][0];
1585 int y1 = y + xy[i][1];
1587 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
1589 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
1591 MovDir[x][y] = direction[0][i];
1594 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
1595 element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
1597 MovDir[x][y] = direction[1][i];
1607 void InitAmoebaNr(int x, int y)
1610 int group_nr = AmoebeNachbarNr(x, y);
1614 for (i=1; i<MAX_NUM_AMOEBA; i++)
1616 if (AmoebaCnt[i] == 0)
1624 AmoebaNr[x][y] = group_nr;
1625 AmoebaCnt[group_nr]++;
1626 AmoebaCnt2[group_nr]++;
1632 boolean raise_level = FALSE;
1634 if (local_player->MovPos)
1638 if (tape.auto_play) /* tape might already be stopped here */
1639 tape.auto_play_level_solved = TRUE;
1641 if (tape.playing && tape.auto_play)
1642 tape.auto_play_level_solved = TRUE;
1645 local_player->LevelSolved = FALSE;
1647 PlaySoundStereo(SND_GAME_WINNING, SOUND_MIDDLE);
1651 if (!tape.playing && setup.sound_loops)
1652 PlaySoundExt(SND_GAME_LEVELTIME_BONUS, SOUND_MAX_VOLUME, SOUND_MIDDLE,
1653 SND_CTRL_PLAY_LOOP);
1655 while (TimeLeft > 0)
1657 if (!tape.playing && !setup.sound_loops)
1658 PlaySoundStereo(SND_GAME_LEVELTIME_BONUS, SOUND_MIDDLE);
1659 if (TimeLeft > 0 && !(TimeLeft % 10))
1660 RaiseScore(level.score[SC_TIME_BONUS]);
1661 if (TimeLeft > 100 && !(TimeLeft % 10))
1665 DrawText(DX_TIME, DY_TIME, int2str(TimeLeft, 3), FONT_TEXT_2);
1672 if (!tape.playing && setup.sound_loops)
1673 StopSound(SND_GAME_LEVELTIME_BONUS);
1675 else if (level.time == 0) /* level without time limit */
1677 if (!tape.playing && setup.sound_loops)
1678 PlaySoundExt(SND_GAME_LEVELTIME_BONUS, SOUND_MAX_VOLUME, SOUND_MIDDLE,
1679 SND_CTRL_PLAY_LOOP);
1681 while (TimePlayed < 999)
1683 if (!tape.playing && !setup.sound_loops)
1684 PlaySoundStereo(SND_GAME_LEVELTIME_BONUS, SOUND_MIDDLE);
1685 if (TimePlayed < 999 && !(TimePlayed % 10))
1686 RaiseScore(level.score[SC_TIME_BONUS]);
1687 if (TimePlayed < 900 && !(TimePlayed % 10))
1691 DrawText(DX_TIME, DY_TIME, int2str(TimePlayed, 3), FONT_TEXT_2);
1698 if (!tape.playing && setup.sound_loops)
1699 StopSound(SND_GAME_LEVELTIME_BONUS);
1702 /* close exit door after last player */
1703 if ((Feld[ExitX][ExitY] == EL_EXIT_OPEN ||
1704 Feld[ExitX][ExitY] == EL_SP_EXIT_OPEN) && AllPlayersGone)
1706 int element = Feld[ExitX][ExitY];
1708 Feld[ExitX][ExitY] = (element == EL_EXIT_OPEN ? EL_EXIT_CLOSING :
1709 EL_SP_EXIT_CLOSING);
1711 PlaySoundLevelElementAction(ExitX, ExitY, element, ACTION_CLOSING);
1714 /* Hero disappears */
1715 DrawLevelField(ExitX, ExitY);
1721 CloseDoor(DOOR_CLOSE_1);
1726 SaveTape(tape.level_nr); /* Ask to save tape */
1729 if (level_nr == leveldir_current->handicap_level)
1731 leveldir_current->handicap_level++;
1732 SaveLevelSetup_SeriesInfo();
1735 if (level_editor_test_game)
1736 local_player->score = -1; /* no highscore when playing from editor */
1737 else if (level_nr < leveldir_current->last_level)
1738 raise_level = TRUE; /* advance to next level */
1740 if ((hi_pos = NewHiScore()) >= 0)
1742 game_status = GAME_MODE_SCORES;
1743 DrawHallOfFame(hi_pos);
1752 game_status = GAME_MODE_MAIN;
1769 LoadScore(level_nr);
1771 if (strcmp(setup.player_name, EMPTY_PLAYER_NAME) == 0 ||
1772 local_player->score < highscore[MAX_SCORE_ENTRIES - 1].Score)
1775 for (k=0; k<MAX_SCORE_ENTRIES; k++)
1777 if (local_player->score > highscore[k].Score)
1779 /* player has made it to the hall of fame */
1781 if (k < MAX_SCORE_ENTRIES - 1)
1783 int m = MAX_SCORE_ENTRIES - 1;
1786 for (l=k; l<MAX_SCORE_ENTRIES; l++)
1787 if (!strcmp(setup.player_name, highscore[l].Name))
1789 if (m == k) /* player's new highscore overwrites his old one */
1795 strcpy(highscore[l].Name, highscore[l - 1].Name);
1796 highscore[l].Score = highscore[l - 1].Score;
1803 strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
1804 highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
1805 highscore[k].Score = local_player->score;
1811 else if (!strncmp(setup.player_name, highscore[k].Name,
1812 MAX_PLAYER_NAME_LEN))
1813 break; /* player already there with a higher score */
1819 SaveScore(level_nr);
1824 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
1826 if (player->GfxAction != action || player->GfxDir != dir)
1829 printf("Player frame reset! (%d => %d, %d => %d)\n",
1830 player->GfxAction, action, player->GfxDir, dir);
1833 player->GfxAction = action;
1834 player->GfxDir = dir;
1836 player->StepFrame = 0;
1840 static void ResetRandomAnimationValue(int x, int y)
1842 GfxRandom[x][y] = INIT_GFX_RANDOM();
1845 static void ResetGfxAnimation(int x, int y)
1848 GfxAction[x][y] = ACTION_DEFAULT;
1851 void InitMovingField(int x, int y, int direction)
1853 int element = Feld[x][y];
1854 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
1855 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
1859 if (!WasJustMoving[x][y] || direction != MovDir[x][y])
1860 ResetGfxAnimation(x, y);
1862 MovDir[newx][newy] = MovDir[x][y] = direction;
1864 if (Feld[newx][newy] == EL_EMPTY)
1865 Feld[newx][newy] = EL_BLOCKED;
1867 if (direction == MV_DOWN && CAN_FALL(element))
1868 GfxAction[x][y] = ACTION_FALLING;
1870 GfxAction[x][y] = ACTION_MOVING;
1872 GfxFrame[newx][newy] = GfxFrame[x][y];
1873 GfxAction[newx][newy] = GfxAction[x][y];
1874 GfxRandom[newx][newy] = GfxRandom[x][y];
1877 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
1879 int direction = MovDir[x][y];
1880 int newx = x + (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
1881 int newy = y + (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
1887 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
1889 int oldx = x, oldy = y;
1890 int direction = MovDir[x][y];
1892 if (direction == MV_LEFT)
1894 else if (direction == MV_RIGHT)
1896 else if (direction == MV_UP)
1898 else if (direction == MV_DOWN)
1901 *comes_from_x = oldx;
1902 *comes_from_y = oldy;
1905 int MovingOrBlocked2Element(int x, int y)
1907 int element = Feld[x][y];
1909 if (element == EL_BLOCKED)
1913 Blocked2Moving(x, y, &oldx, &oldy);
1914 return Feld[oldx][oldy];
1920 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
1922 /* like MovingOrBlocked2Element(), but if element is moving
1923 and (x,y) is the field the moving element is just leaving,
1924 return EL_BLOCKED instead of the element value */
1925 int element = Feld[x][y];
1927 if (IS_MOVING(x, y))
1929 if (element == EL_BLOCKED)
1933 Blocked2Moving(x, y, &oldx, &oldy);
1934 return Feld[oldx][oldy];
1943 static void RemoveField(int x, int y)
1945 Feld[x][y] = EL_EMPTY;
1952 ChangeDelay[x][y] = 0;
1953 ChangePage[x][y] = -1;
1954 Pushed[x][y] = FALSE;
1956 GfxElement[x][y] = EL_UNDEFINED;
1957 GfxAction[x][y] = ACTION_DEFAULT;
1960 void RemoveMovingField(int x, int y)
1962 int oldx = x, oldy = y, newx = x, newy = y;
1963 int element = Feld[x][y];
1964 int next_element = EL_UNDEFINED;
1966 if (element != EL_BLOCKED && !IS_MOVING(x, y))
1969 if (IS_MOVING(x, y))
1971 Moving2Blocked(x, y, &newx, &newy);
1972 if (Feld[newx][newy] != EL_BLOCKED)
1975 else if (element == EL_BLOCKED)
1977 Blocked2Moving(x, y, &oldx, &oldy);
1978 if (!IS_MOVING(oldx, oldy))
1982 if (element == EL_BLOCKED &&
1983 (Feld[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
1984 Feld[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
1985 Feld[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
1986 Feld[oldx][oldy] == EL_AMOEBA_DROPPING))
1987 next_element = get_next_element(Feld[oldx][oldy]);
1989 RemoveField(oldx, oldy);
1990 RemoveField(newx, newy);
1992 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
1994 if (next_element != EL_UNDEFINED)
1995 Feld[oldx][oldy] = next_element;
1997 DrawLevelField(oldx, oldy);
1998 DrawLevelField(newx, newy);
2001 void DrawDynamite(int x, int y)
2003 int sx = SCREENX(x), sy = SCREENY(y);
2004 int graphic = el2img(Feld[x][y]);
2007 if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
2010 if (IS_WALKABLE_INSIDE(Back[x][y]))
2014 DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
2015 else if (Store[x][y])
2016 DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
2018 frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
2021 if (Back[x][y] || Store[x][y])
2022 DrawGraphicThruMask(sx, sy, graphic, frame);
2024 DrawGraphic(sx, sy, graphic, frame);
2026 if (game.emulation == EMU_SUPAPLEX)
2027 DrawGraphic(sx, sy, IMG_SP_DISK_RED, frame);
2028 else if (Store[x][y])
2029 DrawGraphicThruMask(sx, sy, graphic, frame);
2031 DrawGraphic(sx, sy, graphic, frame);
2035 void CheckDynamite(int x, int y)
2037 if (MovDelay[x][y] != 0) /* dynamite is still waiting to explode */
2041 if (MovDelay[x][y] != 0)
2044 PlaySoundLevelActionIfLoop(x, y, ACTION_ACTIVE);
2051 StopSoundLevelActionIfLoop(x, y, ACTION_ACTIVE);
2053 if (Feld[x][y] == EL_DYNAMITE_ACTIVE ||
2054 Feld[x][y] == EL_SP_DISK_RED_ACTIVE)
2055 StopSound(SND_DYNAMITE_ACTIVE);
2057 StopSound(SND_DYNABOMB_ACTIVE);
2063 void RelocatePlayer(int x, int y, int element)
2065 struct PlayerInfo *player = &stored_player[element - EL_PLAYER_1];
2068 RemoveField(x, y); /* temporarily remove newly placed player */
2069 DrawLevelField(x, y);
2072 if (player->present)
2074 while (player->MovPos)
2076 ScrollPlayer(player, SCROLL_GO_ON);
2077 ScrollScreen(NULL, SCROLL_GO_ON);
2083 Delay(GAME_FRAME_DELAY);
2086 DrawPlayer(player); /* needed here only to cleanup last field */
2087 DrawLevelField(player->jx, player->jy); /* remove player graphic */
2089 player->is_moving = FALSE;
2092 Feld[x][y] = element;
2093 InitPlayerField(x, y, element, TRUE);
2095 if (player == local_player)
2097 int scroll_xx = -999, scroll_yy = -999;
2099 while (scroll_xx != scroll_x || scroll_yy != scroll_y)
2102 int fx = FX, fy = FY;
2104 scroll_xx = (local_player->jx < SBX_Left + MIDPOSX ? SBX_Left :
2105 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
2106 local_player->jx - MIDPOSX);
2108 scroll_yy = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
2109 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
2110 local_player->jy - MIDPOSY);
2112 dx = (scroll_xx < scroll_x ? +1 : scroll_xx > scroll_x ? -1 : 0);
2113 dy = (scroll_yy < scroll_y ? +1 : scroll_yy > scroll_y ? -1 : 0);
2118 fx += dx * TILEX / 2;
2119 fy += dy * TILEY / 2;
2121 ScrollLevel(dx, dy);
2124 /* scroll in two steps of half tile size to make things smoother */
2125 BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
2127 Delay(GAME_FRAME_DELAY);
2129 /* scroll second step to align at full tile size */
2131 Delay(GAME_FRAME_DELAY);
2136 void Explode(int ex, int ey, int phase, int mode)
2140 int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
2141 int last_phase = num_phase * delay;
2142 int half_phase = (num_phase / 2) * delay;
2143 int first_phase_after_start = EX_PHASE_START + 1;
2145 if (game.explosions_delayed)
2147 ExplodeField[ex][ey] = mode;
2151 if (phase == EX_PHASE_START) /* initialize 'Store[][]' field */
2153 int center_element = Feld[ex][ey];
2156 /* --- This is only really needed (and now handled) in "Impact()". --- */
2157 /* do not explode moving elements that left the explode field in time */
2158 if (game.engine_version >= VERSION_IDENT(2,2,0,7) &&
2159 center_element == EL_EMPTY && (mode == EX_NORMAL || mode == EX_CENTER))
2163 if (mode == EX_NORMAL || mode == EX_CENTER)
2164 PlaySoundLevelAction(ex, ey, ACTION_EXPLODING);
2166 /* remove things displayed in background while burning dynamite */
2167 if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
2170 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
2172 /* put moving element to center field (and let it explode there) */
2173 center_element = MovingOrBlocked2Element(ex, ey);
2174 RemoveMovingField(ex, ey);
2175 Feld[ex][ey] = center_element;
2178 for (y = ey - 1; y <= ey + 1; y++) for(x = ex - 1; x <= ex + 1; x++)
2180 int xx = x - ex + 1;
2181 int yy = y - ey + 1;
2184 if (!IN_LEV_FIELD(x, y) ||
2185 ((mode != EX_NORMAL || center_element == EL_AMOEBA_TO_DIAMOND) &&
2186 (x != ex || y != ey)))
2189 element = Feld[x][y];
2191 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
2193 element = MovingOrBlocked2Element(x, y);
2195 if (!IS_EXPLOSION_PROOF(element))
2196 RemoveMovingField(x, y);
2202 if (IS_EXPLOSION_PROOF(element))
2205 /* indestructible elements can only explode in center (but not flames) */
2206 if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey)) ||
2207 element == EL_FLAMES)
2212 if ((IS_INDESTRUCTIBLE(element) &&
2213 (game.engine_version < VERSION_IDENT(2,2,0,0) ||
2214 (!IS_WALKABLE_OVER(element) && !IS_WALKABLE_UNDER(element)))) ||
2215 element == EL_FLAMES)
2219 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
2221 if (IS_ACTIVE_BOMB(element))
2223 /* re-activate things under the bomb like gate or penguin */
2224 Feld[x][y] = (Store[x][y] ? Store[x][y] : EL_EMPTY);
2231 /* save walkable background elements while explosion on same tile */
2233 if (IS_INDESTRUCTIBLE(element))
2234 Back[x][y] = element;
2236 if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element))
2237 Back[x][y] = element;
2240 /* ignite explodable elements reached by other explosion */
2241 if (element == EL_EXPLOSION)
2242 element = Store2[x][y];
2245 if (AmoebaNr[x][y] &&
2246 (element == EL_AMOEBA_FULL ||
2247 element == EL_BD_AMOEBA ||
2248 element == EL_AMOEBA_GROWING))
2250 AmoebaCnt[AmoebaNr[x][y]]--;
2251 AmoebaCnt2[AmoebaNr[x][y]]--;
2257 if (IS_PLAYER(ex, ey) && !PLAYER_PROTECTED(ex, ey))
2259 switch(StorePlayer[ex][ey])
2262 Store[x][y] = EL_EMERALD_RED;
2265 Store[x][y] = EL_EMERALD;
2268 Store[x][y] = EL_EMERALD_PURPLE;
2272 Store[x][y] = EL_EMERALD_YELLOW;
2276 if (game.emulation == EMU_SUPAPLEX)
2277 Store[x][y] = EL_EMPTY;
2279 else if (center_element == EL_MOLE)
2280 Store[x][y] = EL_EMERALD_RED;
2281 else if (center_element == EL_PENGUIN)
2282 Store[x][y] = EL_EMERALD_PURPLE;
2283 else if (center_element == EL_BUG)
2284 Store[x][y] = ((x == ex && y == ey) ? EL_DIAMOND : EL_EMERALD);
2285 else if (center_element == EL_BD_BUTTERFLY)
2286 Store[x][y] = EL_BD_DIAMOND;
2287 else if (center_element == EL_SP_ELECTRON)
2288 Store[x][y] = EL_SP_INFOTRON;
2289 else if (center_element == EL_AMOEBA_TO_DIAMOND)
2290 Store[x][y] = level.amoeba_content;
2291 else if (center_element == EL_YAMYAM)
2292 Store[x][y] = level.yamyam_content[game.yamyam_content_nr][xx][yy];
2293 else if (IS_CUSTOM_ELEMENT(center_element) &&
2294 element_info[center_element].content[xx][yy] != EL_EMPTY)
2295 Store[x][y] = element_info[center_element].content[xx][yy];
2296 else if (element == EL_WALL_EMERALD)
2297 Store[x][y] = EL_EMERALD;
2298 else if (element == EL_WALL_DIAMOND)
2299 Store[x][y] = EL_DIAMOND;
2300 else if (element == EL_WALL_BD_DIAMOND)
2301 Store[x][y] = EL_BD_DIAMOND;
2302 else if (element == EL_WALL_EMERALD_YELLOW)
2303 Store[x][y] = EL_EMERALD_YELLOW;
2304 else if (element == EL_WALL_EMERALD_RED)
2305 Store[x][y] = EL_EMERALD_RED;
2306 else if (element == EL_WALL_EMERALD_PURPLE)
2307 Store[x][y] = EL_EMERALD_PURPLE;
2308 else if (element == EL_WALL_PEARL)
2309 Store[x][y] = EL_PEARL;
2310 else if (element == EL_WALL_CRYSTAL)
2311 Store[x][y] = EL_CRYSTAL;
2312 else if (IS_CUSTOM_ELEMENT(element) && !CAN_EXPLODE(element))
2313 Store[x][y] = element_info[element].content[1][1];
2315 Store[x][y] = EL_EMPTY;
2317 if (x != ex || y != ey ||
2318 center_element == EL_AMOEBA_TO_DIAMOND || mode == EX_BORDER)
2319 Store2[x][y] = element;
2322 if (AmoebaNr[x][y] &&
2323 (element == EL_AMOEBA_FULL ||
2324 element == EL_BD_AMOEBA ||
2325 element == EL_AMOEBA_GROWING))
2327 AmoebaCnt[AmoebaNr[x][y]]--;
2328 AmoebaCnt2[AmoebaNr[x][y]]--;
2334 MovDir[x][y] = MovPos[x][y] = 0;
2339 Feld[x][y] = EL_EXPLOSION;
2341 GfxElement[x][y] = center_element;
2343 GfxElement[x][y] = EL_UNDEFINED;
2346 ExplodePhase[x][y] = 1;
2350 if (center_element == EL_YAMYAM)
2351 game.yamyam_content_nr =
2352 (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
2363 ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
2367 /* activate this even in non-DEBUG version until cause for crash in
2368 getGraphicAnimationFrame() (see below) is found and eliminated */
2372 if (GfxElement[x][y] == EL_UNDEFINED)
2375 printf("Explode(): x = %d, y = %d: GfxElement == EL_UNDEFINED\n", x, y);
2376 printf("Explode(): This should never happen!\n");
2379 GfxElement[x][y] = EL_EMPTY;
2383 if (phase == first_phase_after_start)
2385 int element = Store2[x][y];
2387 if (element == EL_BLACK_ORB)
2389 Feld[x][y] = Store2[x][y];
2394 else if (phase == half_phase)
2396 int element = Store2[x][y];
2398 if (IS_PLAYER(x, y))
2399 KillHeroUnlessProtected(x, y);
2400 else if (CAN_EXPLODE_BY_FIRE(element))
2402 Feld[x][y] = Store2[x][y];
2406 else if (element == EL_AMOEBA_TO_DIAMOND)
2407 AmoebeUmwandeln(x, y);
2410 if (phase == last_phase)
2414 element = Feld[x][y] = Store[x][y];
2415 Store[x][y] = Store2[x][y] = 0;
2416 GfxElement[x][y] = EL_UNDEFINED;
2418 if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
2419 element = Feld[x][y] = Back[x][y];
2422 MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
2423 ChangeDelay[x][y] = 0;
2424 ChangePage[x][y] = -1;
2426 InitField(x, y, FALSE);
2427 if (CAN_MOVE(element))
2429 DrawLevelField(x, y);
2431 TestIfElementTouchesCustomElement(x, y);
2433 if (GFX_CRUMBLED(element))
2434 DrawLevelFieldCrumbledSandNeighbours(x, y);
2436 if (IS_PLAYER(x, y) && !PLAYERINFO(x,y)->present)
2437 StorePlayer[x][y] = 0;
2439 if (ELEM_IS_PLAYER(element))
2440 RelocatePlayer(x, y, element);
2442 else if (phase >= delay && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
2445 int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
2447 int stored = Store[x][y];
2448 int graphic = (game.emulation != EMU_SUPAPLEX ? IMG_EXPLOSION :
2449 stored == EL_SP_INFOTRON ? IMG_SP_EXPLOSION_INFOTRON :
2452 int frame = getGraphicAnimationFrame(graphic, phase - delay);
2455 DrawLevelFieldCrumbledSand(x, y);
2457 if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
2459 DrawLevelElement(x, y, Back[x][y]);
2460 DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
2462 else if (IS_WALKABLE_UNDER(Back[x][y]))
2464 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
2465 DrawLevelElementThruMask(x, y, Back[x][y]);
2467 else if (!IS_WALKABLE_INSIDE(Back[x][y]))
2468 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
2472 void DynaExplode(int ex, int ey)
2475 int dynabomb_size = 1;
2476 boolean dynabomb_xl = FALSE;
2477 struct PlayerInfo *player;
2478 static int xy[4][2] =
2486 if (IS_ACTIVE_BOMB(Feld[ex][ey]))
2488 player = &stored_player[Feld[ex][ey] - EL_DYNABOMB_PLAYER_1_ACTIVE];
2489 dynabomb_size = player->dynabomb_size;
2490 dynabomb_xl = player->dynabomb_xl;
2491 player->dynabombs_left++;
2494 Explode(ex, ey, EX_PHASE_START, EX_CENTER);
2498 for (j=1; j<=dynabomb_size; j++)
2500 int x = ex + j * xy[i % 4][0];
2501 int y = ey + j * xy[i % 4][1];
2504 if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
2507 element = Feld[x][y];
2509 /* do not restart explosions of fields with active bombs */
2510 if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
2513 Explode(x, y, EX_PHASE_START, EX_BORDER);
2515 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
2516 if (element != EL_EMPTY &&
2517 element != EL_SAND &&
2518 element != EL_EXPLOSION &&
2525 void Bang(int x, int y)
2528 int element = MovingOrBlocked2Element(x, y);
2530 int element = Feld[x][y];
2534 if (IS_PLAYER(x, y) && !PLAYER_PROTECTED(x, y))
2536 if (IS_PLAYER(x, y))
2539 struct PlayerInfo *player = PLAYERINFO(x, y);
2541 element = Feld[x][y] = (player->use_murphy_graphic ? EL_SP_MURPHY :
2542 player->element_nr);
2547 PlaySoundLevelAction(x, y, ACTION_EXPLODING);
2549 if (game.emulation == EMU_SUPAPLEX)
2550 PlaySoundLevel(x, y, SND_SP_ELEMENT_EXPLODING);
2552 PlaySoundLevel(x, y, SND_ELEMENT_EXPLODING);
2557 if (IS_PLAYER(x, y)) /* remove objects that might cause smaller explosion */
2565 case EL_BD_BUTTERFLY:
2568 case EL_DARK_YAMYAM:
2572 RaiseScoreElement(element);
2573 Explode(x, y, EX_PHASE_START, EX_NORMAL);
2575 case EL_DYNABOMB_PLAYER_1_ACTIVE:
2576 case EL_DYNABOMB_PLAYER_2_ACTIVE:
2577 case EL_DYNABOMB_PLAYER_3_ACTIVE:
2578 case EL_DYNABOMB_PLAYER_4_ACTIVE:
2579 case EL_DYNABOMB_INCREASE_NUMBER:
2580 case EL_DYNABOMB_INCREASE_SIZE:
2581 case EL_DYNABOMB_INCREASE_POWER:
2586 case EL_LAMP_ACTIVE:
2587 if (IS_PLAYER(x, y))
2588 Explode(x, y, EX_PHASE_START, EX_NORMAL);
2590 Explode(x, y, EX_PHASE_START, EX_CENTER);
2593 if (CAN_EXPLODE_1X1(element))
2594 Explode(x, y, EX_PHASE_START, EX_CENTER);
2596 Explode(x, y, EX_PHASE_START, EX_NORMAL);
2600 CheckTriggeredElementChange(x, y, element, CE_OTHER_IS_EXPLODING);
2603 void SplashAcid(int x, int y)
2605 int element = Feld[x][y];
2607 if (element != EL_ACID_SPLASH_LEFT &&
2608 element != EL_ACID_SPLASH_RIGHT)
2610 PlaySoundLevel(x, y, SND_ACID_SPLASHING);
2612 if (IN_LEV_FIELD(x-1, y) && IS_FREE(x-1, y) &&
2613 (!IN_LEV_FIELD(x-1, y-1) ||
2614 !CAN_FALL(MovingOrBlocked2Element(x-1, y-1))))
2615 Feld[x-1][y] = EL_ACID_SPLASH_LEFT;
2617 if (IN_LEV_FIELD(x+1, y) && IS_FREE(x+1, y) &&
2618 (!IN_LEV_FIELD(x+1, y-1) ||
2619 !CAN_FALL(MovingOrBlocked2Element(x+1, y-1))))
2620 Feld[x+1][y] = EL_ACID_SPLASH_RIGHT;
2624 static void InitBeltMovement()
2626 static int belt_base_element[4] =
2628 EL_CONVEYOR_BELT_1_LEFT,
2629 EL_CONVEYOR_BELT_2_LEFT,
2630 EL_CONVEYOR_BELT_3_LEFT,
2631 EL_CONVEYOR_BELT_4_LEFT
2633 static int belt_base_active_element[4] =
2635 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
2636 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
2637 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
2638 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
2643 /* set frame order for belt animation graphic according to belt direction */
2650 int element = belt_base_active_element[belt_nr] + j;
2651 int graphic = el2img(element);
2653 if (game.belt_dir[i] == MV_LEFT)
2654 graphic_info[graphic].anim_mode &= ~ANIM_REVERSE;
2656 graphic_info[graphic].anim_mode |= ANIM_REVERSE;
2660 for(y=0; y<lev_fieldy; y++)
2662 for(x=0; x<lev_fieldx; x++)
2664 int element = Feld[x][y];
2668 if (IS_BELT(element) && game.belt_dir[i] != MV_NO_MOVING)
2670 int e_belt_nr = getBeltNrFromBeltElement(element);
2673 if (e_belt_nr == belt_nr)
2675 int belt_part = Feld[x][y] - belt_base_element[belt_nr];
2677 Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
2685 static void ToggleBeltSwitch(int x, int y)
2687 static int belt_base_element[4] =
2689 EL_CONVEYOR_BELT_1_LEFT,
2690 EL_CONVEYOR_BELT_2_LEFT,
2691 EL_CONVEYOR_BELT_3_LEFT,
2692 EL_CONVEYOR_BELT_4_LEFT
2694 static int belt_base_active_element[4] =
2696 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
2697 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
2698 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
2699 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
2701 static int belt_base_switch_element[4] =
2703 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
2704 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
2705 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
2706 EL_CONVEYOR_BELT_4_SWITCH_LEFT
2708 static int belt_move_dir[4] =
2716 int element = Feld[x][y];
2717 int belt_nr = getBeltNrFromBeltSwitchElement(element);
2718 int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
2719 int belt_dir = belt_move_dir[belt_dir_nr];
2722 if (!IS_BELT_SWITCH(element))
2725 game.belt_dir_nr[belt_nr] = belt_dir_nr;
2726 game.belt_dir[belt_nr] = belt_dir;
2728 if (belt_dir_nr == 3)
2731 /* set frame order for belt animation graphic according to belt direction */
2734 int element = belt_base_active_element[belt_nr] + i;
2735 int graphic = el2img(element);
2737 if (belt_dir == MV_LEFT)
2738 graphic_info[graphic].anim_mode &= ~ANIM_REVERSE;
2740 graphic_info[graphic].anim_mode |= ANIM_REVERSE;
2743 for (yy=0; yy<lev_fieldy; yy++)
2745 for (xx=0; xx<lev_fieldx; xx++)
2747 int element = Feld[xx][yy];
2749 if (IS_BELT_SWITCH(element))
2751 int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
2753 if (e_belt_nr == belt_nr)
2755 Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
2756 DrawLevelField(xx, yy);
2759 else if (IS_BELT(element) && belt_dir != MV_NO_MOVING)
2761 int e_belt_nr = getBeltNrFromBeltElement(element);
2763 if (e_belt_nr == belt_nr)
2765 int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
2767 Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
2768 DrawLevelField(xx, yy);
2771 else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NO_MOVING)
2773 int e_belt_nr = getBeltNrFromBeltActiveElement(element);
2775 if (e_belt_nr == belt_nr)
2777 int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
2779 Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
2780 DrawLevelField(xx, yy);
2787 static void ToggleSwitchgateSwitch(int x, int y)
2791 game.switchgate_pos = !game.switchgate_pos;
2793 for (yy=0; yy<lev_fieldy; yy++)
2795 for (xx=0; xx<lev_fieldx; xx++)
2797 int element = Feld[xx][yy];
2799 if (element == EL_SWITCHGATE_SWITCH_UP ||
2800 element == EL_SWITCHGATE_SWITCH_DOWN)
2802 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
2803 DrawLevelField(xx, yy);
2805 else if (element == EL_SWITCHGATE_OPEN ||
2806 element == EL_SWITCHGATE_OPENING)
2808 Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
2810 PlaySoundLevelAction(xx, yy, ACTION_CLOSING);
2812 PlaySoundLevel(xx, yy, SND_SWITCHGATE_CLOSING);
2815 else if (element == EL_SWITCHGATE_CLOSED ||
2816 element == EL_SWITCHGATE_CLOSING)
2818 Feld[xx][yy] = EL_SWITCHGATE_OPENING;
2820 PlaySoundLevelAction(xx, yy, ACTION_OPENING);
2822 PlaySoundLevel(xx, yy, SND_SWITCHGATE_OPENING);
2829 static int getInvisibleActiveFromInvisibleElement(int element)
2831 return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
2832 element == EL_INVISIBLE_WALL ? EL_INVISIBLE_WALL_ACTIVE :
2833 element == EL_INVISIBLE_SAND ? EL_INVISIBLE_SAND_ACTIVE :
2837 static int getInvisibleFromInvisibleActiveElement(int element)
2839 return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
2840 element == EL_INVISIBLE_WALL_ACTIVE ? EL_INVISIBLE_WALL :
2841 element == EL_INVISIBLE_SAND_ACTIVE ? EL_INVISIBLE_SAND :
2845 static void RedrawAllLightSwitchesAndInvisibleElements()
2849 for (y=0; y<lev_fieldy; y++)
2851 for (x=0; x<lev_fieldx; x++)
2853 int element = Feld[x][y];
2855 if (element == EL_LIGHT_SWITCH &&
2856 game.light_time_left > 0)
2858 Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
2859 DrawLevelField(x, y);
2861 else if (element == EL_LIGHT_SWITCH_ACTIVE &&
2862 game.light_time_left == 0)
2864 Feld[x][y] = EL_LIGHT_SWITCH;
2865 DrawLevelField(x, y);
2867 else if (element == EL_INVISIBLE_STEELWALL ||
2868 element == EL_INVISIBLE_WALL ||
2869 element == EL_INVISIBLE_SAND)
2871 if (game.light_time_left > 0)
2872 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
2874 DrawLevelField(x, y);
2876 else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
2877 element == EL_INVISIBLE_WALL_ACTIVE ||
2878 element == EL_INVISIBLE_SAND_ACTIVE)
2880 if (game.light_time_left == 0)
2881 Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
2883 DrawLevelField(x, y);
2889 static void ToggleLightSwitch(int x, int y)
2891 int element = Feld[x][y];
2893 game.light_time_left =
2894 (element == EL_LIGHT_SWITCH ?
2895 level.time_light * FRAMES_PER_SECOND : 0);
2897 RedrawAllLightSwitchesAndInvisibleElements();
2900 static void ActivateTimegateSwitch(int x, int y)
2904 game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
2906 for (yy=0; yy<lev_fieldy; yy++)
2908 for (xx=0; xx<lev_fieldx; xx++)
2910 int element = Feld[xx][yy];
2912 if (element == EL_TIMEGATE_CLOSED ||
2913 element == EL_TIMEGATE_CLOSING)
2915 Feld[xx][yy] = EL_TIMEGATE_OPENING;
2916 PlaySoundLevel(xx, yy, SND_TIMEGATE_OPENING);
2920 else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
2922 Feld[xx][yy] = EL_TIMEGATE_SWITCH;
2923 DrawLevelField(xx, yy);
2930 Feld[x][y] = EL_TIMEGATE_SWITCH_ACTIVE;
2933 inline static int getElementMoveStepsize(int x, int y)
2935 int element = Feld[x][y];
2936 int direction = MovDir[x][y];
2937 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
2938 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
2939 int horiz_move = (dx != 0);
2940 int sign = (horiz_move ? dx : dy);
2941 int step = sign * element_info[element].move_stepsize;
2943 /* special values for move stepsize for spring and things on conveyor belt */
2946 if (CAN_FALL(element) &&
2947 y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
2948 step = sign * MOVE_STEPSIZE_NORMAL / 2;
2949 else if (element == EL_SPRING)
2950 step = sign * MOVE_STEPSIZE_NORMAL * 2;
2956 void Impact(int x, int y)
2958 boolean lastline = (y == lev_fieldy-1);
2959 boolean object_hit = FALSE;
2960 boolean impact = (lastline || object_hit);
2961 int element = Feld[x][y];
2962 int smashed = EL_UNDEFINED;
2964 if (!lastline) /* check if element below was hit */
2966 if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
2969 object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
2970 MovDir[x][y + 1] != MV_DOWN ||
2971 MovPos[x][y + 1] <= TILEY / 2));
2973 /* do not smash moving elements that left the smashed field in time */
2974 if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
2975 ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
2979 smashed = MovingOrBlocked2Element(x, y + 1);
2981 impact = (lastline || object_hit);
2984 if (!lastline && smashed == EL_ACID) /* element falls into acid */
2990 /* only reset graphic animation if graphic really changes after impact */
2992 el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
2994 ResetGfxAnimation(x, y);
2995 DrawLevelField(x, y);
2998 if (impact && CAN_EXPLODE_IMPACT(element))
3003 else if (impact && element == EL_PEARL)
3005 Feld[x][y] = EL_PEARL_BREAKING;
3006 PlaySoundLevel(x, y, SND_PEARL_BREAKING);
3009 else if (impact && CheckElementChange(x, y, element, CE_IMPACT))
3011 PlaySoundLevelElementAction(x, y, element, ACTION_IMPACT);
3016 if (impact && element == EL_AMOEBA_DROP)
3018 if (object_hit && IS_PLAYER(x, y + 1))
3019 KillHeroUnlessProtected(x, y + 1);
3020 else if (object_hit && smashed == EL_PENGUIN)
3024 Feld[x][y] = EL_AMOEBA_GROWING;
3025 Store[x][y] = EL_AMOEBA_WET;
3027 ResetRandomAnimationValue(x, y);
3032 if (object_hit) /* check which object was hit */
3034 if (CAN_PASS_MAGIC_WALL(element) &&
3035 (smashed == EL_MAGIC_WALL ||
3036 smashed == EL_BD_MAGIC_WALL))
3039 int activated_magic_wall =
3040 (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
3041 EL_BD_MAGIC_WALL_ACTIVE);
3043 /* activate magic wall / mill */
3044 for (yy=0; yy<lev_fieldy; yy++)
3045 for (xx=0; xx<lev_fieldx; xx++)
3046 if (Feld[xx][yy] == smashed)
3047 Feld[xx][yy] = activated_magic_wall;
3049 game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
3050 game.magic_wall_active = TRUE;
3052 PlaySoundLevel(x, y, (smashed == EL_MAGIC_WALL ?
3053 SND_MAGIC_WALL_ACTIVATING :
3054 SND_BD_MAGIC_WALL_ACTIVATING));
3057 if (IS_PLAYER(x, y + 1))
3059 if (CAN_SMASH_PLAYER(element))
3061 KillHeroUnlessProtected(x, y + 1);
3065 else if (smashed == EL_PENGUIN)
3067 if (CAN_SMASH_PLAYER(element))
3073 else if (element == EL_BD_DIAMOND)
3075 if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
3081 else if ((element == EL_SP_INFOTRON ||
3082 element == EL_SP_ZONK) &&
3083 (smashed == EL_SP_SNIKSNAK ||
3084 smashed == EL_SP_ELECTRON ||
3085 smashed == EL_SP_DISK_ORANGE))
3091 else if (CAN_SMASH_ENEMIES(element) && IS_CLASSIC_ENEMY(smashed))
3097 else if (CAN_SMASH_EVERYTHING(element))
3099 if (IS_CLASSIC_ENEMY(smashed) ||
3100 CAN_EXPLODE_SMASHED(smashed))
3105 else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
3107 if (smashed == EL_LAMP ||
3108 smashed == EL_LAMP_ACTIVE)
3113 else if (smashed == EL_NUT)
3115 Feld[x][y + 1] = EL_NUT_BREAKING;
3116 PlaySoundLevel(x, y, SND_NUT_BREAKING);
3117 RaiseScoreElement(EL_NUT);
3120 else if (smashed == EL_PEARL)
3122 Feld[x][y + 1] = EL_PEARL_BREAKING;
3123 PlaySoundLevel(x, y, SND_PEARL_BREAKING);
3126 else if (smashed == EL_DIAMOND)
3128 Feld[x][y + 1] = EL_DIAMOND_BREAKING;
3129 PlaySoundLevel(x, y, SND_DIAMOND_BREAKING);
3132 else if (IS_BELT_SWITCH(smashed))
3134 ToggleBeltSwitch(x, y + 1);
3136 else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
3137 smashed == EL_SWITCHGATE_SWITCH_DOWN)
3139 ToggleSwitchgateSwitch(x, y + 1);
3141 else if (smashed == EL_LIGHT_SWITCH ||
3142 smashed == EL_LIGHT_SWITCH_ACTIVE)
3144 ToggleLightSwitch(x, y + 1);
3148 CheckElementChange(x, y + 1, smashed, CE_SMASHED);
3150 CheckTriggeredElementSideChange(x, y + 1, smashed, CH_SIDE_TOP,
3151 CE_OTHER_IS_SWITCHING);
3152 CheckElementSideChange(x, y + 1, smashed, CH_SIDE_TOP,
3158 CheckElementChange(x, y + 1, smashed, CE_SMASHED);
3163 /* play sound of magic wall / mill */
3165 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
3166 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE))
3168 if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
3169 PlaySoundLevel(x, y, SND_MAGIC_WALL_FILLING);
3170 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
3171 PlaySoundLevel(x, y, SND_BD_MAGIC_WALL_FILLING);
3176 /* play sound of object that hits the ground */
3177 if (lastline || object_hit)
3178 PlaySoundLevelElementAction(x, y, element, ACTION_IMPACT);
3181 void TurnRound(int x, int y)
3193 { 0, 0 }, { 0, 0 }, { 0, 0 },
3198 int left, right, back;
3202 { MV_DOWN, MV_UP, MV_RIGHT },
3203 { MV_UP, MV_DOWN, MV_LEFT },
3205 { MV_LEFT, MV_RIGHT, MV_DOWN },
3209 { MV_RIGHT, MV_LEFT, MV_UP }
3212 int element = Feld[x][y];
3213 int old_move_dir = MovDir[x][y];
3214 int left_dir = turn[old_move_dir].left;
3215 int right_dir = turn[old_move_dir].right;
3216 int back_dir = turn[old_move_dir].back;
3218 int left_dx = move_xy[left_dir].x, left_dy = move_xy[left_dir].y;
3219 int right_dx = move_xy[right_dir].x, right_dy = move_xy[right_dir].y;
3220 int move_dx = move_xy[old_move_dir].x, move_dy = move_xy[old_move_dir].y;
3221 int back_dx = move_xy[back_dir].x, back_dy = move_xy[back_dir].y;
3223 int left_x = x + left_dx, left_y = y + left_dy;
3224 int right_x = x + right_dx, right_y = y + right_dy;
3225 int move_x = x + move_dx, move_y = y + move_dy;
3229 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
3231 TestIfBadThingTouchesOtherBadThing(x, y);
3233 if (ENEMY_CAN_ENTER_FIELD(right_x, right_y))
3234 MovDir[x][y] = right_dir;
3235 else if (!ENEMY_CAN_ENTER_FIELD(move_x, move_y))
3236 MovDir[x][y] = left_dir;
3238 if (element == EL_BUG && MovDir[x][y] != old_move_dir)
3240 else if (element == EL_BD_BUTTERFLY) /* && MovDir[x][y] == left_dir) */
3243 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
3244 element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
3246 TestIfBadThingTouchesOtherBadThing(x, y);
3248 if (ENEMY_CAN_ENTER_FIELD(left_x, left_y))
3249 MovDir[x][y] = left_dir;
3250 else if (!ENEMY_CAN_ENTER_FIELD(move_x, move_y))
3251 MovDir[x][y] = right_dir;
3253 if ((element == EL_SPACESHIP ||
3254 element == EL_SP_SNIKSNAK ||
3255 element == EL_SP_ELECTRON)
3256 && MovDir[x][y] != old_move_dir)
3258 else if (element == EL_BD_FIREFLY) /* && MovDir[x][y] == right_dir) */
3261 else if (element == EL_YAMYAM)
3263 boolean can_turn_left = YAMYAM_CAN_ENTER_FIELD(left_x, left_y);
3264 boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(right_x, right_y);
3266 if (can_turn_left && can_turn_right)
3267 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
3268 else if (can_turn_left)
3269 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
3270 else if (can_turn_right)
3271 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
3273 MovDir[x][y] = back_dir;
3275 MovDelay[x][y] = 16 + 16 * RND(3);
3277 else if (element == EL_DARK_YAMYAM)
3279 boolean can_turn_left = DARK_YAMYAM_CAN_ENTER_FIELD(left_x, left_y);
3280 boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(right_x, right_y);
3282 if (can_turn_left && can_turn_right)
3283 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
3284 else if (can_turn_left)
3285 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
3286 else if (can_turn_right)
3287 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
3289 MovDir[x][y] = back_dir;
3291 MovDelay[x][y] = 16 + 16 * RND(3);
3293 else if (element == EL_PACMAN)
3295 boolean can_turn_left = PACMAN_CAN_ENTER_FIELD(left_x, left_y);
3296 boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(right_x, right_y);
3298 if (can_turn_left && can_turn_right)
3299 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
3300 else if (can_turn_left)
3301 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
3302 else if (can_turn_right)
3303 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
3305 MovDir[x][y] = back_dir;
3307 MovDelay[x][y] = 6 + RND(40);
3309 else if (element == EL_PIG)
3311 boolean can_turn_left = PIG_CAN_ENTER_FIELD(left_x, left_y);
3312 boolean can_turn_right = PIG_CAN_ENTER_FIELD(right_x, right_y);
3313 boolean can_move_on = PIG_CAN_ENTER_FIELD(move_x, move_y);
3314 boolean should_turn_left, should_turn_right, should_move_on;
3316 int rnd = RND(rnd_value);
3318 should_turn_left = (can_turn_left &&
3320 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
3321 y + back_dy + left_dy)));
3322 should_turn_right = (can_turn_right &&
3324 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
3325 y + back_dy + right_dy)));
3326 should_move_on = (can_move_on &&
3329 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
3330 y + move_dy + left_dy) ||
3331 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
3332 y + move_dy + right_dy)));
3334 if (should_turn_left || should_turn_right || should_move_on)
3336 if (should_turn_left && should_turn_right && should_move_on)
3337 MovDir[x][y] = (rnd < rnd_value / 3 ? left_dir :
3338 rnd < 2 * rnd_value / 3 ? right_dir :
3340 else if (should_turn_left && should_turn_right)
3341 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
3342 else if (should_turn_left && should_move_on)
3343 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
3344 else if (should_turn_right && should_move_on)
3345 MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
3346 else if (should_turn_left)
3347 MovDir[x][y] = left_dir;
3348 else if (should_turn_right)
3349 MovDir[x][y] = right_dir;
3350 else if (should_move_on)
3351 MovDir[x][y] = old_move_dir;
3353 else if (can_move_on && rnd > rnd_value / 8)
3354 MovDir[x][y] = old_move_dir;
3355 else if (can_turn_left && can_turn_right)
3356 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
3357 else if (can_turn_left && rnd > rnd_value / 8)
3358 MovDir[x][y] = left_dir;
3359 else if (can_turn_right && rnd > rnd_value/8)
3360 MovDir[x][y] = right_dir;
3362 MovDir[x][y] = back_dir;
3364 xx = x + move_xy[MovDir[x][y]].x;
3365 yy = y + move_xy[MovDir[x][y]].y;
3367 if (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy]))
3368 MovDir[x][y] = old_move_dir;
3372 else if (element == EL_DRAGON)
3374 boolean can_turn_left = IN_LEV_FIELD_AND_IS_FREE(left_x, left_y);
3375 boolean can_turn_right = IN_LEV_FIELD_AND_IS_FREE(right_x, right_y);
3376 boolean can_move_on = IN_LEV_FIELD_AND_IS_FREE(move_x, move_y);
3378 int rnd = RND(rnd_value);
3380 if (can_move_on && rnd > rnd_value / 8)
3381 MovDir[x][y] = old_move_dir;
3382 else if (can_turn_left && can_turn_right)
3383 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
3384 else if (can_turn_left && rnd > rnd_value / 8)
3385 MovDir[x][y] = left_dir;
3386 else if (can_turn_right && rnd > rnd_value / 8)
3387 MovDir[x][y] = right_dir;
3389 MovDir[x][y] = back_dir;
3391 xx = x + move_xy[MovDir[x][y]].x;
3392 yy = y + move_xy[MovDir[x][y]].y;
3394 if (!IS_FREE(xx, yy))
3395 MovDir[x][y] = old_move_dir;
3399 else if (element == EL_MOLE)
3401 boolean can_move_on =
3402 (MOLE_CAN_ENTER_FIELD(move_x, move_y,
3403 IS_AMOEBOID(Feld[move_x][move_y]) ||
3404 Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
3407 boolean can_turn_left =
3408 (MOLE_CAN_ENTER_FIELD(left_x, left_y,
3409 IS_AMOEBOID(Feld[left_x][left_y])));
3411 boolean can_turn_right =
3412 (MOLE_CAN_ENTER_FIELD(right_x, right_y,
3413 IS_AMOEBOID(Feld[right_x][right_y])));
3415 if (can_turn_left && can_turn_right)
3416 MovDir[x][y] = (RND(2) ? left_dir : right_dir);
3417 else if (can_turn_left)
3418 MovDir[x][y] = left_dir;
3420 MovDir[x][y] = right_dir;
3423 if (MovDir[x][y] != old_move_dir)
3426 else if (element == EL_BALLOON)
3428 MovDir[x][y] = game.balloon_dir;
3431 else if (element == EL_SPRING)
3433 if (MovDir[x][y] & MV_HORIZONTAL &&
3434 (!IN_LEV_FIELD_AND_IS_FREE(move_x, move_y) ||
3435 IN_LEV_FIELD_AND_IS_FREE(x, y + 1)))
3436 MovDir[x][y] = MV_NO_MOVING;
3440 else if (element == EL_ROBOT ||
3441 element == EL_SATELLITE ||
3442 element == EL_PENGUIN)
3444 int attr_x = -1, attr_y = -1;
3455 for (i=0; i<MAX_PLAYERS; i++)
3457 struct PlayerInfo *player = &stored_player[i];
3458 int jx = player->jx, jy = player->jy;
3460 if (!player->active)
3464 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
3472 if (element == EL_ROBOT && ZX >= 0 && ZY >= 0)
3478 if (element == EL_PENGUIN)
3481 static int xy[4][2] =
3491 int ex = x + xy[i % 4][0];
3492 int ey = y + xy[i % 4][1];
3494 if (IN_LEV_FIELD(ex, ey) && Feld[ex][ey] == EL_EXIT_OPEN)
3503 MovDir[x][y] = MV_NO_MOVING;
3505 MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
3506 else if (attr_x > x)
3507 MovDir[x][y] |= (AllPlayersGone ? MV_LEFT : MV_RIGHT);
3509 MovDir[x][y] |= (AllPlayersGone ? MV_DOWN : MV_UP);
3510 else if (attr_y > y)
3511 MovDir[x][y] |= (AllPlayersGone ? MV_UP : MV_DOWN);
3513 if (element == EL_ROBOT)
3517 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
3518 MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
3519 Moving2Blocked(x, y, &newx, &newy);
3521 if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
3522 MovDelay[x][y] = 8 + 8 * !RND(3);
3524 MovDelay[x][y] = 16;
3526 else if (element == EL_PENGUIN)
3532 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
3534 boolean first_horiz = RND(2);
3535 int new_move_dir = MovDir[x][y];
3538 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
3539 Moving2Blocked(x, y, &newx, &newy);
3541 if (PENGUIN_CAN_ENTER_FIELD(newx, newy))
3545 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
3546 Moving2Blocked(x, y, &newx, &newy);
3548 if (PENGUIN_CAN_ENTER_FIELD(newx, newy))
3551 MovDir[x][y] = old_move_dir;
3555 else /* (element == EL_SATELLITE) */
3561 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
3563 boolean first_horiz = RND(2);
3564 int new_move_dir = MovDir[x][y];
3567 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
3568 Moving2Blocked(x, y, &newx, &newy);
3570 if (ELEMENT_CAN_ENTER_FIELD_OR_ACID_2(newx, newy))
3574 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
3575 Moving2Blocked(x, y, &newx, &newy);
3577 if (ELEMENT_CAN_ENTER_FIELD_OR_ACID_2(newx, newy))
3580 MovDir[x][y] = old_move_dir;
3585 else if (element_info[element].move_pattern == MV_ALL_DIRECTIONS ||
3586 element_info[element].move_pattern == MV_TURNING_LEFT ||
3587 element_info[element].move_pattern == MV_TURNING_RIGHT)
3589 boolean can_turn_left = ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
3590 boolean can_turn_right = ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
3592 if (element_info[element].move_pattern == MV_TURNING_LEFT)
3593 MovDir[x][y] = left_dir;
3594 else if (element_info[element].move_pattern == MV_TURNING_RIGHT)
3595 MovDir[x][y] = right_dir;
3596 else if (can_turn_left && can_turn_right)
3597 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
3598 else if (can_turn_left)
3599 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
3600 else if (can_turn_right)
3601 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
3603 MovDir[x][y] = back_dir;
3605 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
3607 else if (element_info[element].move_pattern == MV_HORIZONTAL ||
3608 element_info[element].move_pattern == MV_VERTICAL)
3610 if (element_info[element].move_pattern & old_move_dir)
3611 MovDir[x][y] = back_dir;
3612 else if (element_info[element].move_pattern == MV_HORIZONTAL)
3613 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
3614 else if (element_info[element].move_pattern == MV_VERTICAL)
3615 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
3617 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
3619 else if (element_info[element].move_pattern & MV_ANY_DIRECTION)
3621 MovDir[x][y] = element_info[element].move_pattern;
3622 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
3624 else if (element_info[element].move_pattern == MV_ALONG_LEFT_SIDE)
3626 if (ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
3627 MovDir[x][y] = left_dir;
3628 else if (!ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
3629 MovDir[x][y] = right_dir;
3631 if (MovDir[x][y] != old_move_dir)
3632 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
3634 else if (element_info[element].move_pattern == MV_ALONG_RIGHT_SIDE)
3636 if (ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
3637 MovDir[x][y] = right_dir;
3638 else if (!ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
3639 MovDir[x][y] = left_dir;
3641 if (MovDir[x][y] != old_move_dir)
3642 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
3644 else if (element_info[element].move_pattern == MV_TOWARDS_PLAYER ||
3645 element_info[element].move_pattern == MV_AWAY_FROM_PLAYER)
3647 int attr_x = -1, attr_y = -1;
3650 (element_info[element].move_pattern == MV_AWAY_FROM_PLAYER);
3661 for (i=0; i<MAX_PLAYERS; i++)
3663 struct PlayerInfo *player = &stored_player[i];
3664 int jx = player->jx, jy = player->jy;
3666 if (!player->active)
3670 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
3678 MovDir[x][y] = MV_NO_MOVING;
3680 MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
3681 else if (attr_x > x)
3682 MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
3684 MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
3685 else if (attr_y > y)
3686 MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
3688 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
3690 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
3692 boolean first_horiz = RND(2);
3693 int new_move_dir = MovDir[x][y];
3696 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
3697 Moving2Blocked(x, y, &newx, &newy);
3699 if (ELEMENT_CAN_ENTER_FIELD_OR_ACID(element, newx, newy))
3703 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
3704 Moving2Blocked(x, y, &newx, &newy);
3706 if (ELEMENT_CAN_ENTER_FIELD_OR_ACID(element, newx, newy))
3709 MovDir[x][y] = old_move_dir;
3712 else if (element_info[element].move_pattern == MV_WHEN_PUSHED)
3714 if (!IN_LEV_FIELD_AND_IS_FREE(move_x, move_y))
3715 MovDir[x][y] = MV_NO_MOVING;
3721 static boolean JustBeingPushed(int x, int y)
3725 for (i=0; i<MAX_PLAYERS; i++)
3727 struct PlayerInfo *player = &stored_player[i];
3729 if (player->active && player->is_pushing && player->MovPos)
3731 int next_jx = player->jx + (player->jx - player->last_jx);
3732 int next_jy = player->jy + (player->jy - player->last_jy);
3734 if (x == next_jx && y == next_jy)
3742 void StartMoving(int x, int y)
3744 boolean use_spring_bug = (game.engine_version < VERSION_IDENT(2,2,0,0));
3745 boolean started_moving = FALSE; /* some elements can fall _and_ move */
3746 int element = Feld[x][y];
3751 /* !!! this should be handled more generic (not only for mole) !!! */
3752 if (element != EL_MOLE && GfxAction[x][y] != ACTION_DIGGING)
3753 GfxAction[x][y] = ACTION_DEFAULT;
3755 if (CAN_FALL(element) && y < lev_fieldy - 1)
3757 if ((x > 0 && IS_PLAYER(x - 1, y)) ||
3758 (x < lev_fieldx-1 && IS_PLAYER(x + 1, y)))
3759 if (JustBeingPushed(x, y))
3762 if (element == EL_QUICKSAND_FULL)
3764 if (IS_FREE(x, y + 1))
3766 InitMovingField(x, y, MV_DOWN);
3767 started_moving = TRUE;
3769 Feld[x][y] = EL_QUICKSAND_EMPTYING;
3770 Store[x][y] = EL_ROCK;
3772 PlaySoundLevelAction(x, y, ACTION_EMPTYING);
3774 PlaySoundLevel(x, y, SND_QUICKSAND_EMPTYING);
3777 else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
3779 if (!MovDelay[x][y])
3780 MovDelay[x][y] = TILEY + 1;
3789 Feld[x][y] = EL_QUICKSAND_EMPTY;
3790 Feld[x][y + 1] = EL_QUICKSAND_FULL;
3791 Store[x][y + 1] = Store[x][y];
3794 PlaySoundLevelAction(x, y, ACTION_FILLING);
3796 PlaySoundLevel(x, y, SND_QUICKSAND_FILLING);
3800 else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
3801 Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
3803 InitMovingField(x, y, MV_DOWN);
3804 started_moving = TRUE;
3806 Feld[x][y] = EL_QUICKSAND_FILLING;
3807 Store[x][y] = element;
3809 PlaySoundLevelAction(x, y, ACTION_FILLING);
3811 PlaySoundLevel(x, y, SND_QUICKSAND_FILLING);
3814 else if (element == EL_MAGIC_WALL_FULL)
3816 if (IS_FREE(x, y + 1))
3818 InitMovingField(x, y, MV_DOWN);
3819 started_moving = TRUE;
3821 Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
3822 Store[x][y] = EL_CHANGED(Store[x][y]);
3824 else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
3826 if (!MovDelay[x][y])
3827 MovDelay[x][y] = TILEY/4 + 1;
3836 Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
3837 Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
3838 Store[x][y + 1] = EL_CHANGED(Store[x][y]);
3842 else if (element == EL_BD_MAGIC_WALL_FULL)
3844 if (IS_FREE(x, y + 1))
3846 InitMovingField(x, y, MV_DOWN);
3847 started_moving = TRUE;
3849 Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
3850 Store[x][y] = EL_CHANGED2(Store[x][y]);
3852 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
3854 if (!MovDelay[x][y])
3855 MovDelay[x][y] = TILEY/4 + 1;
3864 Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
3865 Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
3866 Store[x][y + 1] = EL_CHANGED2(Store[x][y]);
3870 else if (CAN_PASS_MAGIC_WALL(element) &&
3871 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
3872 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE))
3874 InitMovingField(x, y, MV_DOWN);
3875 started_moving = TRUE;
3878 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
3879 EL_BD_MAGIC_WALL_FILLING);
3880 Store[x][y] = element;
3883 else if (CAN_SMASH(element) && Feld[x][y + 1] == EL_ACID)
3885 else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
3890 InitMovingField(x, y, MV_DOWN);
3891 started_moving = TRUE;
3893 Store[x][y] = EL_ACID;
3895 /* !!! TEST !!! better use "_FALLING" etc. !!! */
3896 GfxAction[x][y + 1] = ACTION_ACTIVE;
3900 else if ((game.engine_version < VERSION_IDENT(2,2,0,7) &&
3901 CAN_SMASH(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
3902 (Feld[x][y + 1] == EL_BLOCKED)) ||
3903 (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
3904 CAN_SMASH(element) && WasJustFalling[x][y] &&
3905 (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))))
3909 else if (game.engine_version < VERSION_IDENT(2,2,0,7) &&
3910 CAN_SMASH(element) && Feld[x][y + 1] == EL_BLOCKED &&
3911 WasJustMoving[x][y] && !Pushed[x][y + 1])
3913 else if (CAN_SMASH(element) && Feld[x][y + 1] == EL_BLOCKED &&
3914 WasJustMoving[x][y])
3919 /* this is needed for a special case not covered by calling "Impact()"
3920 from "ContinueMoving()": if an element moves to a tile directly below
3921 another element which was just falling on that tile (which was empty
3922 in the previous frame), the falling element above would just stop
3923 instead of smashing the element below (in previous version, the above
3924 element was just checked for "moving" instead of "falling", resulting
3925 in incorrect smashes caused by horizontal movement of the above
3926 element; also, the case of the player being the element to smash was
3927 simply not covered here... :-/ ) */
3931 else if (IS_FREE(x, y + 1) && element == EL_SPRING && use_spring_bug)
3933 if (MovDir[x][y] == MV_NO_MOVING)
3935 InitMovingField(x, y, MV_DOWN);
3936 started_moving = TRUE;
3939 else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
3941 if (WasJustFalling[x][y]) /* prevent animation from being restarted */
3942 MovDir[x][y] = MV_DOWN;
3944 InitMovingField(x, y, MV_DOWN);
3945 started_moving = TRUE;
3947 else if (element == EL_AMOEBA_DROP)
3949 Feld[x][y] = EL_AMOEBA_GROWING;
3950 Store[x][y] = EL_AMOEBA_WET;
3952 /* Store[x][y + 1] must be zero, because:
3953 (EL_QUICKSAND_FULL -> EL_ROCK): Store[x][y + 1] == EL_QUICKSAND_EMPTY
3956 #if OLD_GAME_BEHAVIOUR
3957 else if (IS_SLIPPERY(Feld[x][y + 1]) && !Store[x][y + 1])
3959 else if (IS_SLIPPERY(Feld[x][y + 1]) && !Store[x][y + 1] &&
3960 !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
3961 element != EL_DX_SUPABOMB)
3964 else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
3965 (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
3966 !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
3967 element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
3970 boolean can_fall_left = (x > 0 && IS_FREE(x - 1, y) &&
3971 (IS_FREE(x - 1, y + 1) ||
3972 Feld[x - 1][y + 1] == EL_ACID));
3973 boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
3974 (IS_FREE(x + 1, y + 1) ||
3975 Feld[x + 1][y + 1] == EL_ACID));
3976 boolean can_fall_any = (can_fall_left || can_fall_right);
3977 boolean can_fall_both = (can_fall_left && can_fall_right);
3979 if (can_fall_any && IS_CUSTOM_ELEMENT(Feld[x][y + 1]))
3981 int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
3983 if (slippery_type == SLIPPERY_ONLY_LEFT)
3984 can_fall_right = FALSE;
3985 else if (slippery_type == SLIPPERY_ONLY_RIGHT)
3986 can_fall_left = FALSE;
3987 else if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
3988 can_fall_right = FALSE;
3989 else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
3990 can_fall_left = FALSE;
3992 can_fall_any = (can_fall_left || can_fall_right);
3993 can_fall_both = (can_fall_left && can_fall_right);
3998 if (can_fall_both &&
3999 (game.emulation != EMU_BOULDERDASH &&
4000 element != EL_BD_ROCK && element != EL_BD_DIAMOND))
4001 can_fall_left = !(can_fall_right = RND(2));
4003 InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
4004 started_moving = TRUE;
4007 else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
4009 boolean left_is_free = (x > 0 && IS_FREE(x - 1, y));
4010 boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
4011 int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
4012 int belt_dir = game.belt_dir[belt_nr];
4014 if ((belt_dir == MV_LEFT && left_is_free) ||
4015 (belt_dir == MV_RIGHT && right_is_free))
4017 InitMovingField(x, y, belt_dir);
4018 started_moving = TRUE;
4020 GfxAction[x][y] = ACTION_DEFAULT;
4025 /* not "else if" because of elements that can fall and move (EL_SPRING) */
4026 if (CAN_MOVE(element) && !started_moving)
4031 if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
4034 if ((element == EL_SATELLITE ||
4035 element == EL_BALLOON ||
4036 element == EL_SPRING)
4037 && JustBeingPushed(x, y))
4043 if (element == EL_SPRING && MovDir[x][y] == MV_DOWN)
4044 Feld[x][y + 1] = EL_EMPTY; /* was set to EL_BLOCKED above */
4046 if (element == EL_SPRING && MovDir[x][y] != MV_NO_MOVING)
4048 Moving2Blocked(x, y, &newx, &newy);
4049 if (Feld[newx][newy] == EL_BLOCKED)
4050 Feld[newx][newy] = EL_EMPTY; /* was set to EL_BLOCKED above */
4055 if (!MovDelay[x][y]) /* start new movement phase */
4057 /* all objects that can change their move direction after each step
4058 (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall */
4060 if (element != EL_YAMYAM &&
4061 element != EL_DARK_YAMYAM &&
4062 element != EL_PACMAN &&
4063 !(element_info[element].move_pattern & MV_ANY_DIRECTION) &&
4064 element_info[element].move_pattern != MV_TURNING_LEFT &&
4065 element_info[element].move_pattern != MV_TURNING_RIGHT)
4069 if (MovDelay[x][y] && (element == EL_BUG ||
4070 element == EL_SPACESHIP ||
4071 element == EL_SP_SNIKSNAK ||
4072 element == EL_SP_ELECTRON ||
4073 element == EL_MOLE))
4074 DrawLevelField(x, y);
4078 if (MovDelay[x][y]) /* wait some time before next movement */
4083 if (element == EL_YAMYAM)
4086 el_act_dir2img(EL_YAMYAM, ACTION_WAITING, MV_LEFT));
4087 DrawLevelElementAnimation(x, y, element);
4091 if (MovDelay[x][y]) /* element still has to wait some time */
4094 /* !!! PLACE THIS SOMEWHERE AFTER "TurnRound()" !!! */
4095 ResetGfxAnimation(x, y);
4097 GfxAction[x][y] = ACTION_WAITING;
4100 if (element == EL_ROBOT ||
4102 element == EL_PACMAN ||
4104 element == EL_YAMYAM ||
4105 element == EL_DARK_YAMYAM)
4108 DrawLevelElementAnimation(x, y, element);
4110 DrawLevelElementAnimationIfNeeded(x, y, element);
4112 PlaySoundLevelAction(x, y, ACTION_WAITING);
4114 else if (element == EL_SP_ELECTRON)
4115 DrawLevelElementAnimationIfNeeded(x, y, element);
4116 else if (element == EL_DRAGON)
4119 int dir = MovDir[x][y];
4120 int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
4121 int dy = (dir == MV_UP ? -1 : dir == MV_DOWN ? +1 : 0);
4122 int graphic = (dir == MV_LEFT ? IMG_FLAMES_1_LEFT :
4123 dir == MV_RIGHT ? IMG_FLAMES_1_RIGHT :
4124 dir == MV_UP ? IMG_FLAMES_1_UP :
4125 dir == MV_DOWN ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
4126 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
4129 printf("::: %d, %d\n", GfxAction[x][y], GfxFrame[x][y]);
4132 GfxAction[x][y] = ACTION_ATTACKING;
4134 if (IS_PLAYER(x, y))
4135 DrawPlayerField(x, y);
4137 DrawLevelField(x, y);
4139 PlaySoundLevelActionIfLoop(x, y, ACTION_ATTACKING);
4141 for (i=1; i <= 3; i++)
4143 int xx = x + i * dx;
4144 int yy = y + i * dy;
4145 int sx = SCREENX(xx);
4146 int sy = SCREENY(yy);
4147 int flame_graphic = graphic + (i - 1);
4149 if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
4154 int flamed = MovingOrBlocked2Element(xx, yy);
4156 if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_FIRE(flamed))
4159 RemoveMovingField(xx, yy);
4161 Feld[xx][yy] = EL_FLAMES;
4162 if (IN_SCR_FIELD(sx, sy))
4164 DrawLevelFieldCrumbledSand(xx, yy);
4165 DrawGraphic(sx, sy, flame_graphic, frame);
4170 if (Feld[xx][yy] == EL_FLAMES)
4171 Feld[xx][yy] = EL_EMPTY;
4172 DrawLevelField(xx, yy);
4177 if (MovDelay[x][y]) /* element still has to wait some time */
4179 PlaySoundLevelAction(x, y, ACTION_WAITING);
4184 /* special case of "moving" animation of waiting elements (FIX THIS !!!);
4185 for all other elements GfxAction will be set by InitMovingField() */
4186 if (element == EL_BD_BUTTERFLY || element == EL_BD_FIREFLY)
4187 GfxAction[x][y] = ACTION_MOVING;
4190 /* now make next step */
4192 Moving2Blocked(x, y, &newx, &newy); /* get next screen position */
4194 if (DONT_COLLIDE_WITH(element) &&
4195 IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
4196 !PLAYER_PROTECTED(newx, newy))
4199 TestIfBadThingRunsIntoHero(x, y, MovDir[x][y]);
4202 /* player killed by element which is deadly when colliding with */
4204 KillHero(PLAYERINFO(newx, newy));
4209 else if ((element == EL_PENGUIN ||
4210 element == EL_ROBOT ||
4211 element == EL_SATELLITE ||
4212 element == EL_BALLOON ||
4213 IS_CUSTOM_ELEMENT(element)) &&
4214 IN_LEV_FIELD(newx, newy) &&
4215 MovDir[x][y] == MV_DOWN && Feld[newx][newy] == EL_ACID)
4218 Store[x][y] = EL_ACID;
4220 else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
4222 if (Feld[newx][newy] == EL_EXIT_OPEN)
4224 Feld[x][y] = EL_EMPTY;
4225 DrawLevelField(x, y);
4227 PlaySoundLevel(newx, newy, SND_PENGUIN_PASSING);
4228 if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
4229 DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
4231 local_player->friends_still_needed--;
4232 if (!local_player->friends_still_needed &&
4233 !local_player->GameOver && AllPlayersGone)
4234 local_player->LevelSolved = local_player->GameOver = TRUE;
4238 else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
4240 if (DigField(local_player, newx, newy, 0, 0, DF_DIG) == MF_MOVING)
4241 DrawLevelField(newx, newy);
4243 MovDir[x][y] = MV_NO_MOVING;
4245 else if (!IS_FREE(newx, newy))
4247 GfxAction[x][y] = ACTION_WAITING;
4249 if (IS_PLAYER(x, y))
4250 DrawPlayerField(x, y);
4252 DrawLevelField(x, y);
4256 else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
4258 if (IS_FOOD_PIG(Feld[newx][newy]))
4260 if (IS_MOVING(newx, newy))
4261 RemoveMovingField(newx, newy);
4264 Feld[newx][newy] = EL_EMPTY;
4265 DrawLevelField(newx, newy);
4268 PlaySoundLevel(x, y, SND_PIG_DIGGING);
4270 else if (!IS_FREE(newx, newy))
4272 if (IS_PLAYER(x, y))
4273 DrawPlayerField(x, y);
4275 DrawLevelField(x, y);
4279 else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
4281 if (!IS_FREE(newx, newy))
4283 if (IS_PLAYER(x, y))
4284 DrawPlayerField(x, y);
4286 DrawLevelField(x, y);
4292 boolean wanna_flame = !RND(10);
4293 int dx = newx - x, dy = newy - y;
4294 int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
4295 int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
4296 int element1 = (IN_LEV_FIELD(newx1, newy1) ?
4297 MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
4298 int element2 = (IN_LEV_FIELD(newx2, newy2) ?
4299 MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
4302 IS_CLASSIC_ENEMY(element1) ||
4303 IS_CLASSIC_ENEMY(element2)) &&
4304 element1 != EL_DRAGON && element2 != EL_DRAGON &&
4305 element1 != EL_FLAMES && element2 != EL_FLAMES)
4308 ResetGfxAnimation(x, y);
4309 GfxAction[x][y] = ACTION_ATTACKING;
4312 if (IS_PLAYER(x, y))
4313 DrawPlayerField(x, y);
4315 DrawLevelField(x, y);
4317 PlaySoundLevel(x, y, SND_DRAGON_ATTACKING);
4319 MovDelay[x][y] = 50;
4321 Feld[newx][newy] = EL_FLAMES;
4322 if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
4323 Feld[newx1][newy1] = EL_FLAMES;
4324 if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
4325 Feld[newx2][newy2] = EL_FLAMES;
4331 else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
4332 Feld[newx][newy] == EL_DIAMOND)
4334 if (IS_MOVING(newx, newy))
4335 RemoveMovingField(newx, newy);
4338 Feld[newx][newy] = EL_EMPTY;
4339 DrawLevelField(newx, newy);
4342 PlaySoundLevel(x, y, SND_YAMYAM_DIGGING);
4344 else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
4345 IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
4347 if (AmoebaNr[newx][newy])
4349 AmoebaCnt2[AmoebaNr[newx][newy]]--;
4350 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
4351 Feld[newx][newy] == EL_BD_AMOEBA)
4352 AmoebaCnt[AmoebaNr[newx][newy]]--;
4355 if (IS_MOVING(newx, newy))
4356 RemoveMovingField(newx, newy);
4359 Feld[newx][newy] = EL_EMPTY;
4360 DrawLevelField(newx, newy);
4363 PlaySoundLevel(x, y, SND_DARK_YAMYAM_DIGGING);
4365 else if ((element == EL_PACMAN || element == EL_MOLE)
4366 && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
4368 if (AmoebaNr[newx][newy])
4370 AmoebaCnt2[AmoebaNr[newx][newy]]--;
4371 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
4372 Feld[newx][newy] == EL_BD_AMOEBA)
4373 AmoebaCnt[AmoebaNr[newx][newy]]--;
4376 if (element == EL_MOLE)
4378 Feld[newx][newy] = EL_AMOEBA_SHRINKING;
4379 PlaySoundLevel(x, y, SND_MOLE_DIGGING);
4381 ResetGfxAnimation(x, y);
4382 GfxAction[x][y] = ACTION_DIGGING;
4383 DrawLevelField(x, y);
4385 MovDelay[newx][newy] = 0; /* start amoeba shrinking delay */
4386 return; /* wait for shrinking amoeba */
4388 else /* element == EL_PACMAN */
4390 Feld[newx][newy] = EL_EMPTY;
4391 DrawLevelField(newx, newy);
4392 PlaySoundLevel(x, y, SND_PACMAN_DIGGING);
4395 else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
4396 (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
4397 (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
4399 /* wait for shrinking amoeba to completely disappear */
4402 else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
4404 /* object was running against a wall */
4409 if (GFX_ELEMENT(element) != EL_SAND) /* !!! FIX THIS (crumble) !!! */
4410 DrawLevelElementAnimation(x, y, element);
4412 if (element == EL_BUG ||
4413 element == EL_SPACESHIP ||
4414 element == EL_SP_SNIKSNAK)
4415 DrawLevelField(x, y);
4416 else if (element == EL_MOLE)
4417 DrawLevelField(x, y);
4418 else if (element == EL_BD_BUTTERFLY ||
4419 element == EL_BD_FIREFLY)
4420 DrawLevelElementAnimationIfNeeded(x, y, element);
4421 else if (element == EL_SATELLITE)
4422 DrawLevelElementAnimationIfNeeded(x, y, element);
4423 else if (element == EL_SP_ELECTRON)
4424 DrawLevelElementAnimationIfNeeded(x, y, element);
4427 if (DONT_TOUCH(element))
4428 TestIfBadThingTouchesHero(x, y);
4431 PlaySoundLevelAction(x, y, ACTION_WAITING);
4437 InitMovingField(x, y, MovDir[x][y]);
4439 PlaySoundLevelAction(x, y, ACTION_MOVING);
4443 ContinueMoving(x, y);
4446 void ContinueMoving(int x, int y)
4448 int element = Feld[x][y];
4449 int direction = MovDir[x][y];
4450 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
4451 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
4452 int newx = x + dx, newy = y + dy;
4453 int nextx = newx + dx, nexty = newy + dy;
4454 boolean pushed = Pushed[x][y];
4456 MovPos[x][y] += getElementMoveStepsize(x, y);
4458 if (pushed) /* special case: moving object pushed by player */
4459 MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
4461 if (ABS(MovPos[x][y]) < TILEX)
4463 DrawLevelField(x, y);
4465 return; /* element is still moving */
4468 /* element reached destination field */
4470 Feld[x][y] = EL_EMPTY;
4471 Feld[newx][newy] = element;
4472 MovPos[x][y] = 0; /* force "not moving" for "crumbled sand" */
4474 if (element == EL_MOLE)
4476 Feld[x][y] = EL_SAND;
4478 DrawLevelFieldCrumbledSandNeighbours(x, y);
4480 else if (element == EL_QUICKSAND_FILLING)
4482 element = Feld[newx][newy] = get_next_element(element);
4483 Store[newx][newy] = Store[x][y];
4485 else if (element == EL_QUICKSAND_EMPTYING)
4487 Feld[x][y] = get_next_element(element);
4488 element = Feld[newx][newy] = Store[x][y];
4490 else if (element == EL_MAGIC_WALL_FILLING)
4492 element = Feld[newx][newy] = get_next_element(element);
4493 if (!game.magic_wall_active)
4494 element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
4495 Store[newx][newy] = Store[x][y];
4497 else if (element == EL_MAGIC_WALL_EMPTYING)
4499 Feld[x][y] = get_next_element(element);
4500 if (!game.magic_wall_active)
4501 Feld[x][y] = EL_MAGIC_WALL_DEAD;
4502 element = Feld[newx][newy] = Store[x][y];
4504 else if (element == EL_BD_MAGIC_WALL_FILLING)
4506 element = Feld[newx][newy] = get_next_element(element);
4507 if (!game.magic_wall_active)
4508 element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
4509 Store[newx][newy] = Store[x][y];
4511 else if (element == EL_BD_MAGIC_WALL_EMPTYING)
4513 Feld[x][y] = get_next_element(element);
4514 if (!game.magic_wall_active)
4515 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
4516 element = Feld[newx][newy] = Store[x][y];
4518 else if (element == EL_AMOEBA_DROPPING)
4520 Feld[x][y] = get_next_element(element);
4521 element = Feld[newx][newy] = Store[x][y];
4523 else if (element == EL_SOKOBAN_OBJECT)
4526 Feld[x][y] = Back[x][y];
4528 if (Back[newx][newy])
4529 Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
4531 Back[x][y] = Back[newx][newy] = 0;
4533 else if (Store[x][y] == EL_ACID)
4535 element = Feld[newx][newy] = EL_ACID;
4539 MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
4540 MovDelay[newx][newy] = 0;
4542 /* copy element change control values to new field */
4543 ChangeDelay[newx][newy] = ChangeDelay[x][y];
4544 ChangePage[newx][newy] = ChangePage[x][y];
4545 Changed[newx][newy] = Changed[x][y];
4546 ChangeEvent[newx][newy] = ChangeEvent[x][y];
4548 ChangeDelay[x][y] = 0;
4549 ChangePage[x][y] = -1;
4550 Changed[x][y] = CE_BITMASK_DEFAULT;
4551 ChangeEvent[x][y] = CE_BITMASK_DEFAULT;
4553 /* copy animation control values to new field */
4554 GfxFrame[newx][newy] = GfxFrame[x][y];
4555 GfxAction[newx][newy] = GfxAction[x][y]; /* keep action one frame */
4556 GfxRandom[newx][newy] = GfxRandom[x][y]; /* keep same random value */
4558 Pushed[x][y] = Pushed[newx][newy] = FALSE;
4560 ResetGfxAnimation(x, y); /* reset animation values for old field */
4563 /* 2.1.1 (does not work correctly for spring) */
4564 if (!CAN_MOVE(element))
4565 MovDir[newx][newy] = 0;
4569 /* (does not work for falling objects that slide horizontally) */
4570 if (CAN_FALL(element) && MovDir[newx][newy] == MV_DOWN)
4571 MovDir[newx][newy] = 0;
4574 if (!CAN_MOVE(element) ||
4575 (element == EL_SPRING && MovDir[newx][newy] == MV_DOWN))
4576 MovDir[newx][newy] = 0;
4579 if (!CAN_MOVE(element) ||
4580 (CAN_FALL(element) && direction == MV_DOWN))
4581 MovDir[newx][newy] = 0;
4586 DrawLevelField(x, y);
4587 DrawLevelField(newx, newy);
4589 Stop[newx][newy] = TRUE; /* ignore this element until the next frame */
4591 /* prevent pushed element from moving on in pushed direction */
4592 if (pushed && CAN_MOVE(element) &&
4593 element_info[element].move_pattern & MV_ANY_DIRECTION &&
4594 !(element_info[element].move_pattern & direction))
4595 TurnRound(newx, newy);
4597 if (!pushed) /* special case: moving object pushed by player */
4599 WasJustMoving[newx][newy] = 3;
4601 if (CAN_FALL(element) && direction == MV_DOWN)
4602 WasJustFalling[newx][newy] = 3;
4605 if (DONT_TOUCH(element)) /* object may be nasty to player or others */
4607 TestIfBadThingTouchesHero(newx, newy);
4608 TestIfBadThingTouchesFriend(newx, newy);
4609 TestIfBadThingTouchesOtherBadThing(newx, newy);
4611 else if (element == EL_PENGUIN)
4612 TestIfFriendTouchesBadThing(newx, newy);
4614 if (CAN_FALL(element) && direction == MV_DOWN &&
4615 (newy == lev_fieldy - 1 || !IS_FREE(x, newy + 1)))
4619 TestIfElementTouchesCustomElement(x, y); /* for empty space */
4623 if (ChangePage[newx][newy] != -1) /* delayed change */
4624 ChangeElement(newx, newy, ChangePage[newx][newy]);
4627 if (!IN_LEV_FIELD(nextx, nexty) || !IS_FREE(nextx, nexty))
4628 CheckElementSideChange(newx, newy, Feld[newx][newy], direction,
4631 TestIfPlayerTouchesCustomElement(newx, newy);
4632 TestIfElementTouchesCustomElement(newx, newy);
4635 int AmoebeNachbarNr(int ax, int ay)
4638 int element = Feld[ax][ay];
4640 static int xy[4][2] =
4650 int x = ax + xy[i][0];
4651 int y = ay + xy[i][1];
4653 if (!IN_LEV_FIELD(x, y))
4656 if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
4657 group_nr = AmoebaNr[x][y];
4663 void AmoebenVereinigen(int ax, int ay)
4665 int i, x, y, xx, yy;
4666 int new_group_nr = AmoebaNr[ax][ay];
4667 static int xy[4][2] =
4675 if (new_group_nr == 0)
4683 if (!IN_LEV_FIELD(x, y))
4686 if ((Feld[x][y] == EL_AMOEBA_FULL ||
4687 Feld[x][y] == EL_BD_AMOEBA ||
4688 Feld[x][y] == EL_AMOEBA_DEAD) &&
4689 AmoebaNr[x][y] != new_group_nr)
4691 int old_group_nr = AmoebaNr[x][y];
4693 if (old_group_nr == 0)
4696 AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
4697 AmoebaCnt[old_group_nr] = 0;
4698 AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
4699 AmoebaCnt2[old_group_nr] = 0;
4701 for (yy=0; yy<lev_fieldy; yy++)
4703 for (xx=0; xx<lev_fieldx; xx++)
4705 if (AmoebaNr[xx][yy] == old_group_nr)
4706 AmoebaNr[xx][yy] = new_group_nr;
4713 void AmoebeUmwandeln(int ax, int ay)
4717 if (Feld[ax][ay] == EL_AMOEBA_DEAD)
4719 int group_nr = AmoebaNr[ax][ay];
4724 printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
4725 printf("AmoebeUmwandeln(): This should never happen!\n");
4730 for (y=0; y<lev_fieldy; y++)
4732 for (x=0; x<lev_fieldx; x++)
4734 if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
4737 Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
4741 PlaySoundLevel(ax, ay, (IS_GEM(level.amoeba_content) ?
4742 SND_AMOEBA_TURNING_TO_GEM :
4743 SND_AMOEBA_TURNING_TO_ROCK));
4748 static int xy[4][2] =
4761 if (!IN_LEV_FIELD(x, y))
4764 if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
4766 PlaySoundLevel(x, y, (IS_GEM(level.amoeba_content) ?
4767 SND_AMOEBA_TURNING_TO_GEM :
4768 SND_AMOEBA_TURNING_TO_ROCK));
4775 void AmoebeUmwandelnBD(int ax, int ay, int new_element)
4778 int group_nr = AmoebaNr[ax][ay];
4779 boolean done = FALSE;
4784 printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
4785 printf("AmoebeUmwandelnBD(): This should never happen!\n");
4790 for (y=0; y<lev_fieldy; y++)
4792 for (x=0; x<lev_fieldx; x++)
4794 if (AmoebaNr[x][y] == group_nr &&
4795 (Feld[x][y] == EL_AMOEBA_DEAD ||
4796 Feld[x][y] == EL_BD_AMOEBA ||
4797 Feld[x][y] == EL_AMOEBA_GROWING))
4800 Feld[x][y] = new_element;
4801 InitField(x, y, FALSE);
4802 DrawLevelField(x, y);
4809 PlaySoundLevel(ax, ay, (new_element == EL_BD_ROCK ?
4810 SND_BD_AMOEBA_TURNING_TO_ROCK :
4811 SND_BD_AMOEBA_TURNING_TO_GEM));
4814 void AmoebeWaechst(int x, int y)
4816 static unsigned long sound_delay = 0;
4817 static unsigned long sound_delay_value = 0;
4819 if (!MovDelay[x][y]) /* start new growing cycle */
4823 if (DelayReached(&sound_delay, sound_delay_value))
4826 PlaySoundLevelElementAction(x, y, Store[x][y], ACTION_GROWING);
4828 if (Store[x][y] == EL_BD_AMOEBA)
4829 PlaySoundLevel(x, y, SND_BD_AMOEBA_GROWING);
4831 PlaySoundLevel(x, y, SND_AMOEBA_GROWING);
4833 sound_delay_value = 30;
4837 if (MovDelay[x][y]) /* wait some time before growing bigger */
4840 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
4842 int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
4843 6 - MovDelay[x][y]);
4845 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
4848 if (!MovDelay[x][y])
4850 Feld[x][y] = Store[x][y];
4852 DrawLevelField(x, y);
4857 void AmoebaDisappearing(int x, int y)
4859 static unsigned long sound_delay = 0;
4860 static unsigned long sound_delay_value = 0;
4862 if (!MovDelay[x][y]) /* start new shrinking cycle */
4866 if (DelayReached(&sound_delay, sound_delay_value))
4867 sound_delay_value = 30;
4870 if (MovDelay[x][y]) /* wait some time before shrinking */
4873 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
4875 int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
4876 6 - MovDelay[x][y]);
4878 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
4881 if (!MovDelay[x][y])
4883 Feld[x][y] = EL_EMPTY;
4884 DrawLevelField(x, y);
4886 /* don't let mole enter this field in this cycle;
4887 (give priority to objects falling to this field from above) */
4893 void AmoebeAbleger(int ax, int ay)
4896 int element = Feld[ax][ay];
4897 int graphic = el2img(element);
4898 int newax = ax, neway = ay;
4899 static int xy[4][2] =
4907 if (!level.amoeba_speed)
4909 Feld[ax][ay] = EL_AMOEBA_DEAD;
4910 DrawLevelField(ax, ay);
4914 if (IS_ANIMATED(graphic))
4915 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
4917 if (!MovDelay[ax][ay]) /* start making new amoeba field */
4918 MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
4920 if (MovDelay[ax][ay]) /* wait some time before making new amoeba */
4923 if (MovDelay[ax][ay])
4927 if (element == EL_AMOEBA_WET) /* object is an acid / amoeba drop */
4930 int x = ax + xy[start][0];
4931 int y = ay + xy[start][1];
4933 if (!IN_LEV_FIELD(x, y))
4936 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
4937 if (IS_FREE(x, y) ||
4938 Feld[x][y] == EL_SAND || Feld[x][y] == EL_QUICKSAND_EMPTY)
4944 if (newax == ax && neway == ay)
4947 else /* normal or "filled" (BD style) amoeba */
4950 boolean waiting_for_player = FALSE;
4954 int j = (start + i) % 4;
4955 int x = ax + xy[j][0];
4956 int y = ay + xy[j][1];
4958 if (!IN_LEV_FIELD(x, y))
4961 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
4962 if (IS_FREE(x, y) ||
4963 Feld[x][y] == EL_SAND || Feld[x][y] == EL_QUICKSAND_EMPTY)
4969 else if (IS_PLAYER(x, y))
4970 waiting_for_player = TRUE;
4973 if (newax == ax && neway == ay) /* amoeba cannot grow */
4975 if (i == 4 && (!waiting_for_player || game.emulation == EMU_BOULDERDASH))
4977 Feld[ax][ay] = EL_AMOEBA_DEAD;
4978 DrawLevelField(ax, ay);
4979 AmoebaCnt[AmoebaNr[ax][ay]]--;
4981 if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0) /* amoeba is completely dead */
4983 if (element == EL_AMOEBA_FULL)
4984 AmoebeUmwandeln(ax, ay);
4985 else if (element == EL_BD_AMOEBA)
4986 AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
4991 else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
4993 /* amoeba gets larger by growing in some direction */
4995 int new_group_nr = AmoebaNr[ax][ay];
4998 if (new_group_nr == 0)
5000 printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
5001 printf("AmoebeAbleger(): This should never happen!\n");
5006 AmoebaNr[newax][neway] = new_group_nr;
5007 AmoebaCnt[new_group_nr]++;
5008 AmoebaCnt2[new_group_nr]++;
5010 /* if amoeba touches other amoeba(s) after growing, unify them */
5011 AmoebenVereinigen(newax, neway);
5013 if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
5015 AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
5021 if (element != EL_AMOEBA_WET || neway < ay || !IS_FREE(newax, neway) ||
5022 (neway == lev_fieldy - 1 && newax != ax))
5024 Feld[newax][neway] = EL_AMOEBA_GROWING; /* creation of new amoeba */
5025 Store[newax][neway] = element;
5027 else if (neway == ay)
5029 Feld[newax][neway] = EL_AMOEBA_DROP; /* drop left/right of amoeba */
5031 PlaySoundLevelAction(newax, neway, ACTION_GROWING);
5033 PlaySoundLevel(newax, neway, SND_AMOEBA_GROWING);
5038 InitMovingField(ax, ay, MV_DOWN); /* drop dripping from amoeba */
5039 Feld[ax][ay] = EL_AMOEBA_DROPPING;
5040 Store[ax][ay] = EL_AMOEBA_DROP;
5041 ContinueMoving(ax, ay);
5045 DrawLevelField(newax, neway);
5048 void Life(int ax, int ay)
5051 static int life[4] = { 2, 3, 3, 3 }; /* parameters for "game of life" */
5053 int element = Feld[ax][ay];
5054 int graphic = el2img(element);
5055 boolean changed = FALSE;
5057 if (IS_ANIMATED(graphic))
5058 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
5063 if (!MovDelay[ax][ay]) /* start new "game of life" cycle */
5064 MovDelay[ax][ay] = life_time;
5066 if (MovDelay[ax][ay]) /* wait some time before next cycle */
5069 if (MovDelay[ax][ay])
5073 for (y1=-1; y1<2; y1++) for(x1=-1; x1<2; x1++)
5075 int xx = ax+x1, yy = ay+y1;
5078 if (!IN_LEV_FIELD(xx, yy))
5081 for (y2=-1; y2<2; y2++) for (x2=-1; x2<2; x2++)
5083 int x = xx+x2, y = yy+y2;
5085 if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
5088 if (((Feld[x][y] == element ||
5089 (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y))) &&
5091 (IS_FREE(x, y) && Stop[x][y]))
5095 if (xx == ax && yy == ay) /* field in the middle */
5097 if (nachbarn < life[0] || nachbarn > life[1])
5099 Feld[xx][yy] = EL_EMPTY;
5101 DrawLevelField(xx, yy);
5102 Stop[xx][yy] = TRUE;
5106 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
5107 else if (IS_FREE(xx, yy) || Feld[xx][yy] == EL_SAND)
5108 { /* free border field */
5109 if (nachbarn >= life[2] && nachbarn <= life[3])
5111 Feld[xx][yy] = element;
5112 MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
5114 DrawLevelField(xx, yy);
5115 Stop[xx][yy] = TRUE;
5122 PlaySoundLevel(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
5123 SND_GAME_OF_LIFE_GROWING);
5126 static void InitRobotWheel(int x, int y)
5128 ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
5131 static void RunRobotWheel(int x, int y)
5133 PlaySoundLevel(x, y, SND_ROBOT_WHEEL_ACTIVE);
5136 static void StopRobotWheel(int x, int y)
5138 if (ZX == x && ZY == y)
5142 static void InitTimegateWheel(int x, int y)
5144 ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
5147 static void RunTimegateWheel(int x, int y)
5149 PlaySoundLevel(x, y, SND_TIMEGATE_SWITCH_ACTIVE);
5152 void CheckExit(int x, int y)
5154 if (local_player->gems_still_needed > 0 ||
5155 local_player->sokobanfields_still_needed > 0 ||
5156 local_player->lights_still_needed > 0)
5158 int element = Feld[x][y];
5159 int graphic = el2img(element);
5161 if (IS_ANIMATED(graphic))
5162 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
5167 if (AllPlayersGone) /* do not re-open exit door closed after last player */
5170 Feld[x][y] = EL_EXIT_OPENING;
5172 PlaySoundLevelNearest(x, y, SND_CLASS_EXIT_OPENING);
5175 void CheckExitSP(int x, int y)
5177 if (local_player->gems_still_needed > 0)
5179 int element = Feld[x][y];
5180 int graphic = el2img(element);
5182 if (IS_ANIMATED(graphic))
5183 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
5188 if (AllPlayersGone) /* do not re-open exit door closed after last player */
5191 Feld[x][y] = EL_SP_EXIT_OPENING;
5193 PlaySoundLevelNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
5196 static void CloseAllOpenTimegates()
5200 for (y=0; y<lev_fieldy; y++)
5202 for (x=0; x<lev_fieldx; x++)
5204 int element = Feld[x][y];
5206 if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
5208 Feld[x][y] = EL_TIMEGATE_CLOSING;
5210 PlaySoundLevelAction(x, y, ACTION_CLOSING);
5212 PlaySoundLevel(x, y, SND_TIMEGATE_CLOSING);
5219 void EdelsteinFunkeln(int x, int y)
5221 if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
5224 if (Feld[x][y] == EL_BD_DIAMOND)
5227 if (MovDelay[x][y] == 0) /* next animation frame */
5228 MovDelay[x][y] = 11 * !SimpleRND(500);
5230 if (MovDelay[x][y] != 0) /* wait some time before next frame */
5234 if (setup.direct_draw && MovDelay[x][y])
5235 SetDrawtoField(DRAW_BUFFERED);
5237 DrawLevelElementAnimation(x, y, Feld[x][y]);
5239 if (MovDelay[x][y] != 0)
5241 int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
5242 10 - MovDelay[x][y]);
5244 DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
5246 if (setup.direct_draw)
5250 dest_x = FX + SCREENX(x) * TILEX;
5251 dest_y = FY + SCREENY(y) * TILEY;
5253 BlitBitmap(drawto_field, window,
5254 dest_x, dest_y, TILEX, TILEY, dest_x, dest_y);
5255 SetDrawtoField(DRAW_DIRECT);
5261 void MauerWaechst(int x, int y)
5265 if (!MovDelay[x][y]) /* next animation frame */
5266 MovDelay[x][y] = 3 * delay;
5268 if (MovDelay[x][y]) /* wait some time before next frame */
5272 if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
5274 int graphic = el_dir2img(Feld[x][y], MovDir[x][y]);
5275 int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
5277 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5280 if (!MovDelay[x][y])
5282 if (MovDir[x][y] == MV_LEFT)
5284 if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
5285 DrawLevelField(x - 1, y);
5287 else if (MovDir[x][y] == MV_RIGHT)
5289 if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
5290 DrawLevelField(x + 1, y);
5292 else if (MovDir[x][y] == MV_UP)
5294 if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
5295 DrawLevelField(x, y - 1);
5299 if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
5300 DrawLevelField(x, y + 1);
5303 Feld[x][y] = Store[x][y];
5305 MovDir[x][y] = MV_NO_MOVING;
5306 DrawLevelField(x, y);
5311 void MauerAbleger(int ax, int ay)
5313 int element = Feld[ax][ay];
5314 int graphic = el2img(element);
5315 boolean oben_frei = FALSE, unten_frei = FALSE;
5316 boolean links_frei = FALSE, rechts_frei = FALSE;
5317 boolean oben_massiv = FALSE, unten_massiv = FALSE;
5318 boolean links_massiv = FALSE, rechts_massiv = FALSE;
5319 boolean new_wall = FALSE;
5321 if (IS_ANIMATED(graphic))
5322 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
5324 if (!MovDelay[ax][ay]) /* start building new wall */
5325 MovDelay[ax][ay] = 6;
5327 if (MovDelay[ax][ay]) /* wait some time before building new wall */
5330 if (MovDelay[ax][ay])
5334 if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
5336 if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
5338 if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
5340 if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
5343 if (element == EL_EXPANDABLE_WALL_VERTICAL ||
5344 element == EL_EXPANDABLE_WALL_ANY)
5348 Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
5349 Store[ax][ay-1] = element;
5350 MovDir[ax][ay-1] = MV_UP;
5351 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
5352 DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
5353 IMG_EXPANDABLE_WALL_GROWING_UP, 0);
5358 Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
5359 Store[ax][ay+1] = element;
5360 MovDir[ax][ay+1] = MV_DOWN;
5361 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
5362 DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
5363 IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
5368 if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
5369 element == EL_EXPANDABLE_WALL_ANY ||
5370 element == EL_EXPANDABLE_WALL)
5374 Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
5375 Store[ax-1][ay] = element;
5376 MovDir[ax-1][ay] = MV_LEFT;
5377 if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
5378 DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
5379 IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
5385 Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
5386 Store[ax+1][ay] = element;
5387 MovDir[ax+1][ay] = MV_RIGHT;
5388 if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
5389 DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
5390 IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
5395 if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
5396 DrawLevelField(ax, ay);
5398 if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
5400 if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
5401 unten_massiv = TRUE;
5402 if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
5403 links_massiv = TRUE;
5404 if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
5405 rechts_massiv = TRUE;
5407 if (((oben_massiv && unten_massiv) ||
5408 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
5409 element == EL_EXPANDABLE_WALL) &&
5410 ((links_massiv && rechts_massiv) ||
5411 element == EL_EXPANDABLE_WALL_VERTICAL))
5412 Feld[ax][ay] = EL_WALL;
5416 PlaySoundLevelAction(ax, ay, ACTION_GROWING);
5418 PlaySoundLevel(ax, ay, SND_EXPANDABLE_WALL_GROWING);
5422 void CheckForDragon(int x, int y)
5425 boolean dragon_found = FALSE;
5426 static int xy[4][2] =
5438 int xx = x + j*xy[i][0], yy = y + j*xy[i][1];
5440 if (IN_LEV_FIELD(xx, yy) &&
5441 (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
5443 if (Feld[xx][yy] == EL_DRAGON)
5444 dragon_found = TRUE;
5457 int xx = x + j*xy[i][0], yy = y + j*xy[i][1];
5459 if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
5461 Feld[xx][yy] = EL_EMPTY;
5462 DrawLevelField(xx, yy);
5471 static void InitBuggyBase(int x, int y)
5473 int element = Feld[x][y];
5474 int activating_delay = FRAMES_PER_SECOND / 4;
5477 (element == EL_SP_BUGGY_BASE ?
5478 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
5479 element == EL_SP_BUGGY_BASE_ACTIVATING ?
5481 element == EL_SP_BUGGY_BASE_ACTIVE ?
5482 1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
5485 static void WarnBuggyBase(int x, int y)
5488 static int xy[4][2] =
5498 int xx = x + xy[i][0], yy = y + xy[i][1];
5500 if (IS_PLAYER(xx, yy))
5502 PlaySoundLevel(x, y, SND_SP_BUGGY_BASE_ACTIVE);
5509 static void InitTrap(int x, int y)
5511 ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
5514 static void ActivateTrap(int x, int y)
5516 PlaySoundLevel(x, y, SND_TRAP_ACTIVATING);
5519 static void ChangeActiveTrap(int x, int y)
5521 int graphic = IMG_TRAP_ACTIVE;
5523 /* if new animation frame was drawn, correct crumbled sand border */
5524 if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
5525 DrawLevelFieldCrumbledSand(x, y);
5528 static void ChangeElementNowExt(int x, int y, int target_element)
5530 /* check if element under player changes from accessible to unaccessible
5531 (needed for special case of dropping element which then changes) */
5532 if (IS_PLAYER(x, y) && !PLAYER_PROTECTED(x, y) &&
5533 IS_ACCESSIBLE(Feld[x][y]) && !IS_ACCESSIBLE(target_element))
5540 Feld[x][y] = target_element;
5542 Changed[x][y] |= ChangeEvent[x][y]; /* ignore same changes in this frame */
5544 ResetGfxAnimation(x, y);
5545 ResetRandomAnimationValue(x, y);
5547 InitField(x, y, FALSE);
5548 if (CAN_MOVE(Feld[x][y]))
5551 DrawLevelField(x, y);
5553 if (GFX_CRUMBLED(Feld[x][y]))
5554 DrawLevelFieldCrumbledSandNeighbours(x, y);
5556 TestIfBadThingTouchesHero(x, y);
5557 TestIfPlayerTouchesCustomElement(x, y);
5558 TestIfElementTouchesCustomElement(x, y);
5560 if (ELEM_IS_PLAYER(target_element))
5561 RelocatePlayer(x, y, target_element);
5564 static boolean ChangeElementNow(int x, int y, int element, int page)
5566 struct ElementChangeInfo *change = &element_info[element].change_page[page];
5568 /* always use default change event to prevent running into a loop */
5569 if (ChangeEvent[x][y] == CE_BITMASK_DEFAULT)
5570 ChangeEvent[x][y] = CH_EVENT_BIT(CE_DELAY);
5572 /* do not change already changed elements with same change event */
5574 if (Changed[x][y] & ChangeEvent[x][y])
5581 Changed[x][y] |= ChangeEvent[x][y]; /* ignore same changes in this frame */
5583 CheckTriggeredElementChange(x, y, Feld[x][y], CE_OTHER_IS_CHANGING);
5585 if (change->explode)
5592 if (change->use_content)
5594 boolean complete_change = TRUE;
5595 boolean can_change[3][3];
5598 for (yy = 0; yy < 3; yy++) for(xx = 0; xx < 3 ; xx++)
5600 boolean half_destructible;
5601 int ex = x + xx - 1;
5602 int ey = y + yy - 1;
5605 can_change[xx][yy] = TRUE;
5607 if (ex == x && ey == y) /* do not check changing element itself */
5610 if (change->content[xx][yy] == EL_EMPTY_SPACE)
5612 can_change[xx][yy] = FALSE; /* do not change empty borders */
5617 if (!IN_LEV_FIELD(ex, ey))
5619 can_change[xx][yy] = FALSE;
5620 complete_change = FALSE;
5627 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
5628 e = MovingOrBlocked2Element(ex, ey);
5630 half_destructible = (IS_FREE(ex, ey) || IS_DIGGABLE(e));
5632 if ((change->power <= CP_NON_DESTRUCTIVE && !IS_FREE(ex, ey)) ||
5633 (change->power <= CP_HALF_DESTRUCTIVE && !half_destructible) ||
5634 (change->power <= CP_FULL_DESTRUCTIVE && IS_INDESTRUCTIBLE(e)))
5636 can_change[xx][yy] = FALSE;
5637 complete_change = FALSE;
5641 if (!change->only_complete || complete_change)
5643 boolean something_has_changed = FALSE;
5645 if (change->only_complete && change->use_random_change &&
5646 RND(100) < change->random)
5649 for (yy = 0; yy < 3; yy++) for(xx = 0; xx < 3 ; xx++)
5651 int ex = x + xx - 1;
5652 int ey = y + yy - 1;
5654 if (can_change[xx][yy] && (!change->use_random_change ||
5655 RND(100) < change->random))
5657 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
5658 RemoveMovingField(ex, ey);
5660 ChangeEvent[ex][ey] = ChangeEvent[x][y];
5662 ChangeElementNowExt(ex, ey, change->content[xx][yy]);
5664 something_has_changed = TRUE;
5666 /* for symmetry reasons, freeze newly created border elements */
5667 if (ex != x || ey != y)
5668 Stop[ex][ey] = TRUE; /* no more moving in this frame */
5672 if (something_has_changed)
5673 PlaySoundLevelElementAction(x, y, element, ACTION_CHANGING);
5678 ChangeElementNowExt(x, y, change->target_element);
5680 PlaySoundLevelElementAction(x, y, element, ACTION_CHANGING);
5686 static void ChangeElement(int x, int y, int page)
5688 int element = MovingOrBlocked2Element(x, y);
5689 struct ElementInfo *ei = &element_info[element];
5690 struct ElementChangeInfo *change = &ei->change_page[page];
5694 if (!CAN_CHANGE(element))
5697 printf("ChangeElement(): %d,%d: element = %d ('%s')\n",
5698 x, y, element, element_info[element].token_name);
5699 printf("ChangeElement(): This should never happen!\n");
5705 if (ChangeDelay[x][y] == 0) /* initialize element change */
5707 ChangeDelay[x][y] = ( change->delay_fixed * change->delay_frames +
5708 RND(change->delay_random * change->delay_frames)) + 1;
5710 ResetGfxAnimation(x, y);
5711 ResetRandomAnimationValue(x, y);
5713 if (change->pre_change_function)
5714 change->pre_change_function(x, y);
5717 ChangeDelay[x][y]--;
5719 if (ChangeDelay[x][y] != 0) /* continue element change */
5721 int graphic = el_act_dir2img(element, GfxAction[x][y], MovDir[x][y]);
5723 if (IS_ANIMATED(graphic))
5724 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
5726 if (change->change_function)
5727 change->change_function(x, y);
5729 else /* finish element change */
5731 if (ChangePage[x][y] != -1) /* remember page from delayed change */
5733 page = ChangePage[x][y];
5734 ChangePage[x][y] = -1;
5737 if (IS_MOVING(x, y)) /* never change a running system ;-) */
5739 ChangeDelay[x][y] = 1; /* try change after next move step */
5740 ChangePage[x][y] = page; /* remember page to use for change */
5745 if (ChangeElementNow(x, y, element, page))
5747 if (change->post_change_function)
5748 change->post_change_function(x, y);
5753 static boolean CheckTriggeredElementSideChange(int lx, int ly,
5754 int trigger_element,
5760 if (!(trigger_events[trigger_element] & CH_EVENT_BIT(trigger_event)))
5763 for (i=0; i < NUM_CUSTOM_ELEMENTS; i++)
5765 int element = EL_CUSTOM_START + i;
5767 boolean change_element = FALSE;
5770 if (!CAN_CHANGE(element) || !HAS_ANY_CHANGE_EVENT(element, trigger_event))
5773 for (j=0; j < element_info[element].num_change_pages; j++)
5775 struct ElementChangeInfo *change = &element_info[element].change_page[j];
5777 if (change->can_change &&
5779 change->events & CH_EVENT_BIT(trigger_event) &&
5781 change->sides & trigger_side &&
5782 change->trigger_element == trigger_element)
5785 if (!(change->events & CH_EVENT_BIT(trigger_event)))
5786 printf("::: !!! %d triggers %d: using wrong page %d [event %d]\n",
5787 trigger_element-EL_CUSTOM_START+1, i+1, j, trigger_event);
5790 change_element = TRUE;
5797 if (!change_element)
5800 for (y=0; y<lev_fieldy; y++) for (x=0; x<lev_fieldx; x++)
5803 if (x == lx && y == ly) /* do not change trigger element itself */
5807 if (Feld[x][y] == element)
5809 ChangeDelay[x][y] = 1;
5810 ChangeEvent[x][y] = CH_EVENT_BIT(trigger_event);
5811 ChangeElement(x, y, page);
5819 static boolean CheckTriggeredElementChange(int lx, int ly, int trigger_element,
5822 return CheckTriggeredElementSideChange(lx, ly, trigger_element, CH_SIDE_ANY,
5826 static boolean CheckElementSideChange(int x, int y, int element, int side,
5827 int trigger_event, int page)
5829 if (!CAN_CHANGE(element) || !HAS_ANY_CHANGE_EVENT(element, trigger_event))
5832 if (Feld[x][y] == EL_BLOCKED)
5834 Blocked2Moving(x, y, &x, &y);
5835 element = Feld[x][y];
5839 page = element_info[element].event_page_nr[trigger_event];
5841 if (!(element_info[element].change_page[page].sides & side))
5844 ChangeDelay[x][y] = 1;
5845 ChangeEvent[x][y] = CH_EVENT_BIT(trigger_event);
5846 ChangeElement(x, y, page);
5851 static boolean CheckElementChange(int x, int y, int element, int trigger_event)
5853 return CheckElementSideChange(x, y, element, CH_SIDE_ANY, trigger_event, -1);
5856 static void PlayerActions(struct PlayerInfo *player, byte player_action)
5858 static byte stored_player_action[MAX_PLAYERS];
5859 static int num_stored_actions = 0;
5860 boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
5861 int left = player_action & JOY_LEFT;
5862 int right = player_action & JOY_RIGHT;
5863 int up = player_action & JOY_UP;
5864 int down = player_action & JOY_DOWN;
5865 int button1 = player_action & JOY_BUTTON_1;
5866 int button2 = player_action & JOY_BUTTON_2;
5867 int dx = (left ? -1 : right ? 1 : 0);
5868 int dy = (up ? -1 : down ? 1 : 0);
5870 stored_player_action[player->index_nr] = 0;
5871 num_stored_actions++;
5873 if (!player->active || tape.pausing)
5879 snapped = SnapField(player, dx, dy);
5883 dropped = DropElement(player);
5885 moved = MovePlayer(player, dx, dy);
5888 if (tape.single_step && tape.recording && !tape.pausing)
5890 if (button1 || (dropped && !moved))
5892 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
5893 SnapField(player, 0, 0); /* stop snapping */
5897 stored_player_action[player->index_nr] = player_action;
5901 /* no actions for this player (no input at player's configured device) */
5903 DigField(player, 0, 0, 0, 0, DF_NO_PUSH);
5904 SnapField(player, 0, 0);
5905 CheckGravityMovement(player);
5907 if (player->MovPos == 0)
5908 InitPlayerGfxAnimation(player, ACTION_DEFAULT, player->MovDir);
5910 if (player->MovPos == 0) /* needed for tape.playing */
5911 player->is_moving = FALSE;
5914 if (tape.recording && num_stored_actions >= MAX_PLAYERS)
5916 TapeRecordAction(stored_player_action);
5917 num_stored_actions = 0;
5923 static unsigned long action_delay = 0;
5924 unsigned long action_delay_value;
5925 int magic_wall_x = 0, magic_wall_y = 0;
5926 int i, x, y, element, graphic;
5927 byte *recorded_player_action;
5928 byte summarized_player_action = 0;
5930 if (game_status != GAME_MODE_PLAYING)
5933 action_delay_value =
5934 (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
5936 if (tape.playing && tape.index_search && !tape.pausing)
5937 action_delay_value = 0;
5939 /* ---------- main game synchronization point ---------- */
5941 WaitUntilDelayReached(&action_delay, action_delay_value);
5943 if (network_playing && !network_player_action_received)
5947 printf("DEBUG: try to get network player actions in time\n");
5951 #if defined(PLATFORM_UNIX)
5952 /* last chance to get network player actions without main loop delay */
5956 if (game_status != GAME_MODE_PLAYING)
5959 if (!network_player_action_received)
5963 printf("DEBUG: failed to get network player actions in time\n");
5973 recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
5975 for (i=0; i<MAX_PLAYERS; i++)
5977 summarized_player_action |= stored_player[i].action;
5979 if (!network_playing)
5980 stored_player[i].effective_action = stored_player[i].action;
5983 #if defined(PLATFORM_UNIX)
5984 if (network_playing)
5985 SendToServer_MovePlayer(summarized_player_action);
5988 if (!options.network && !setup.team_mode)
5989 local_player->effective_action = summarized_player_action;
5991 for (i=0; i<MAX_PLAYERS; i++)
5993 int actual_player_action = stored_player[i].effective_action;
5995 if (stored_player[i].programmed_action)
5996 actual_player_action = stored_player[i].programmed_action;
5998 if (recorded_player_action)
5999 actual_player_action = recorded_player_action[i];
6001 PlayerActions(&stored_player[i], actual_player_action);
6002 ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
6005 network_player_action_received = FALSE;
6007 ScrollScreen(NULL, SCROLL_GO_ON);
6013 for (i=0; i<MAX_PLAYERS; i++)
6014 stored_player[i].Frame++;
6018 if (game.engine_version < VERSION_IDENT(2,2,0,7))
6020 for (i=0; i<MAX_PLAYERS; i++)
6022 struct PlayerInfo *player = &stored_player[i];
6026 if (player->active && player->is_pushing && player->is_moving &&
6029 ContinueMoving(x, y);
6031 /* continue moving after pushing (this is actually a bug) */
6032 if (!IS_MOVING(x, y))
6041 for (y=0; y<lev_fieldy; y++) for (x=0; x<lev_fieldx; x++)
6043 Changed[x][y] = CE_BITMASK_DEFAULT;
6044 ChangeEvent[x][y] = CE_BITMASK_DEFAULT;
6047 if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
6049 printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
6050 printf("GameActions(): This should never happen!\n");
6052 ChangePage[x][y] = -1;
6057 if (WasJustMoving[x][y] > 0)
6058 WasJustMoving[x][y]--;
6059 if (WasJustFalling[x][y] > 0)
6060 WasJustFalling[x][y]--;
6065 /* reset finished pushing action (not done in ContinueMoving() to allow
6066 continous pushing animation for elements with zero push delay) */
6067 if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
6069 ResetGfxAnimation(x, y);
6070 DrawLevelField(x, y);
6075 if (IS_BLOCKED(x, y))
6079 Blocked2Moving(x, y, &oldx, &oldy);
6080 if (!IS_MOVING(oldx, oldy))
6082 printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
6083 printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
6084 printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
6085 printf("GameActions(): This should never happen!\n");
6091 for (y=0; y<lev_fieldy; y++) for (x=0; x<lev_fieldx; x++)
6093 element = Feld[x][y];
6095 graphic = el_act_dir2img(element, GfxAction[x][y], MovDir[x][y]);
6097 graphic = el2img(element);
6103 printf("::: %d,%d: %d [%d]\n", x, y, element, FrameCounter);
6105 element = graphic = 0;
6109 if (graphic_info[graphic].anim_global_sync)
6110 GfxFrame[x][y] = FrameCounter;
6112 if (ANIM_MODE(graphic) == ANIM_RANDOM &&
6113 IS_NEXT_FRAME(GfxFrame[x][y], graphic))
6114 ResetRandomAnimationValue(x, y);
6116 SetRandomAnimationValue(x, y);
6119 PlaySoundLevelActionIfLoop(x, y, GfxAction[x][y]);
6122 if (IS_INACTIVE(element))
6124 if (IS_ANIMATED(graphic))
6125 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
6131 /* this may take place after moving, so 'element' may have changed */
6132 if (IS_CHANGING(x, y))
6135 ChangeElement(x, y, ChangePage[x][y] != -1 ? ChangePage[x][y] :
6136 element_info[element].event_page_nr[CE_DELAY]);
6138 ChangeElement(x, y, element_info[element].event_page_nr[CE_DELAY]);
6141 element = Feld[x][y];
6142 graphic = el_act_dir2img(element, GfxAction[x][y], MovDir[x][y]);
6146 if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
6151 graphic = el_act_dir2img(element, GfxAction[x][y], MovDir[x][y]);
6153 if (element == EL_MOLE)
6154 printf("::: %d, %d, %d [%d]\n",
6155 IS_ANIMATED(graphic), IS_MOVING(x, y), Stop[x][y],
6159 if (element == EL_YAMYAM)
6160 printf("::: %d, %d, %d\n",
6161 IS_ANIMATED(graphic), IS_MOVING(x, y), Stop[x][y]);
6165 if (IS_ANIMATED(graphic) &&
6169 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
6172 if (element == EL_MOLE)
6173 printf("::: %d, %d\n", graphic, GfxFrame[x][y]);
6177 if (IS_GEM(element) || element == EL_SP_INFOTRON)
6178 EdelsteinFunkeln(x, y);
6180 else if ((element == EL_ACID ||
6181 element == EL_EXIT_OPEN ||
6182 element == EL_SP_EXIT_OPEN ||
6183 element == EL_SP_TERMINAL ||
6184 element == EL_SP_TERMINAL_ACTIVE ||
6185 element == EL_EXTRA_TIME ||
6186 element == EL_SHIELD_NORMAL ||
6187 element == EL_SHIELD_DEADLY) &&
6188 IS_ANIMATED(graphic))
6189 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
6190 else if (IS_MOVING(x, y))
6191 ContinueMoving(x, y);
6192 else if (IS_ACTIVE_BOMB(element))
6193 CheckDynamite(x, y);
6195 else if (element == EL_EXPLOSION && !game.explosions_delayed)
6196 Explode(x, y, ExplodePhase[x][y], EX_NORMAL);
6198 else if (element == EL_AMOEBA_GROWING)
6199 AmoebeWaechst(x, y);
6200 else if (element == EL_AMOEBA_SHRINKING)
6201 AmoebaDisappearing(x, y);
6203 #if !USE_NEW_AMOEBA_CODE
6204 else if (IS_AMOEBALIVE(element))
6205 AmoebeAbleger(x, y);
6208 else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
6210 else if (element == EL_EXIT_CLOSED)
6212 else if (element == EL_SP_EXIT_CLOSED)
6214 else if (element == EL_EXPANDABLE_WALL_GROWING)
6216 else if (element == EL_EXPANDABLE_WALL ||
6217 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
6218 element == EL_EXPANDABLE_WALL_VERTICAL ||
6219 element == EL_EXPANDABLE_WALL_ANY)
6221 else if (element == EL_FLAMES)
6222 CheckForDragon(x, y);
6224 else if (IS_AUTO_CHANGING(element))
6225 ChangeElement(x, y);
6227 else if (element == EL_EXPLOSION)
6228 ; /* drawing of correct explosion animation is handled separately */
6229 else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
6230 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
6233 /* this may take place after moving, so 'element' may have changed */
6234 if (IS_AUTO_CHANGING(Feld[x][y]))
6235 ChangeElement(x, y);
6238 if (IS_BELT_ACTIVE(element))
6239 PlaySoundLevelAction(x, y, ACTION_ACTIVE);
6241 if (game.magic_wall_active)
6243 int jx = local_player->jx, jy = local_player->jy;
6245 /* play the element sound at the position nearest to the player */
6246 if ((element == EL_MAGIC_WALL_FULL ||
6247 element == EL_MAGIC_WALL_ACTIVE ||
6248 element == EL_MAGIC_WALL_EMPTYING ||
6249 element == EL_BD_MAGIC_WALL_FULL ||
6250 element == EL_BD_MAGIC_WALL_ACTIVE ||
6251 element == EL_BD_MAGIC_WALL_EMPTYING) &&
6252 ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
6260 #if USE_NEW_AMOEBA_CODE
6261 /* new experimental amoeba growth stuff */
6263 if (!(FrameCounter % 8))
6266 static unsigned long random = 1684108901;
6268 for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
6271 x = (random >> 10) % lev_fieldx;
6272 y = (random >> 20) % lev_fieldy;
6274 x = RND(lev_fieldx);
6275 y = RND(lev_fieldy);
6277 element = Feld[x][y];
6279 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
6280 if (!IS_PLAYER(x,y) &&
6281 (element == EL_EMPTY ||
6282 element == EL_SAND ||
6283 element == EL_QUICKSAND_EMPTY ||
6284 element == EL_ACID_SPLASH_LEFT ||
6285 element == EL_ACID_SPLASH_RIGHT))
6287 if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
6288 (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
6289 (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
6290 (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
6291 Feld[x][y] = EL_AMOEBA_DROP;
6294 random = random * 129 + 1;
6300 if (game.explosions_delayed)
6303 game.explosions_delayed = FALSE;
6305 for (y=0; y<lev_fieldy; y++) for (x=0; x<lev_fieldx; x++)
6307 element = Feld[x][y];
6309 if (ExplodeField[x][y])
6310 Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
6311 else if (element == EL_EXPLOSION)
6312 Explode(x, y, ExplodePhase[x][y], EX_NORMAL);
6314 ExplodeField[x][y] = EX_NO_EXPLOSION;
6317 game.explosions_delayed = TRUE;
6320 if (game.magic_wall_active)
6322 if (!(game.magic_wall_time_left % 4))
6324 int element = Feld[magic_wall_x][magic_wall_y];
6326 if (element == EL_BD_MAGIC_WALL_FULL ||
6327 element == EL_BD_MAGIC_WALL_ACTIVE ||
6328 element == EL_BD_MAGIC_WALL_EMPTYING)
6329 PlaySoundLevel(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
6331 PlaySoundLevel(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
6334 if (game.magic_wall_time_left > 0)
6336 game.magic_wall_time_left--;
6337 if (!game.magic_wall_time_left)
6339 for (y=0; y<lev_fieldy; y++) for (x=0; x<lev_fieldx; x++)
6341 element = Feld[x][y];
6343 if (element == EL_MAGIC_WALL_ACTIVE ||
6344 element == EL_MAGIC_WALL_FULL)
6346 Feld[x][y] = EL_MAGIC_WALL_DEAD;
6347 DrawLevelField(x, y);
6349 else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
6350 element == EL_BD_MAGIC_WALL_FULL)
6352 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
6353 DrawLevelField(x, y);
6357 game.magic_wall_active = FALSE;
6362 if (game.light_time_left > 0)
6364 game.light_time_left--;
6366 if (game.light_time_left == 0)
6367 RedrawAllLightSwitchesAndInvisibleElements();
6370 if (game.timegate_time_left > 0)
6372 game.timegate_time_left--;
6374 if (game.timegate_time_left == 0)
6375 CloseAllOpenTimegates();
6378 for (i=0; i<MAX_PLAYERS; i++)
6380 struct PlayerInfo *player = &stored_player[i];
6382 if (SHIELD_ON(player))
6384 if (player->shield_deadly_time_left)
6385 PlaySoundLevel(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
6386 else if (player->shield_normal_time_left)
6387 PlaySoundLevel(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
6391 if (TimeFrames >= FRAMES_PER_SECOND)
6396 for (i=0; i<MAX_PLAYERS; i++)
6398 struct PlayerInfo *player = &stored_player[i];
6400 if (SHIELD_ON(player))
6402 player->shield_normal_time_left--;
6404 if (player->shield_deadly_time_left > 0)
6405 player->shield_deadly_time_left--;
6409 if (tape.recording || tape.playing)
6410 DrawVideoDisplay(VIDEO_STATE_TIME_ON, TimePlayed);
6416 if (TimeLeft <= 10 && setup.time_limit)
6417 PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, SOUND_MIDDLE);
6419 DrawText(DX_TIME, DY_TIME, int2str(TimeLeft, 3), FONT_TEXT_2);
6421 if (!TimeLeft && setup.time_limit)
6422 for (i=0; i<MAX_PLAYERS; i++)
6423 KillHero(&stored_player[i]);
6425 else if (level.time == 0 && !AllPlayersGone) /* level without time limit */
6426 DrawText(DX_TIME, DY_TIME, int2str(TimePlayed, 3), FONT_TEXT_2);
6431 if (options.debug) /* calculate frames per second */
6433 static unsigned long fps_counter = 0;
6434 static int fps_frames = 0;
6435 unsigned long fps_delay_ms = Counter() - fps_counter;
6439 if (fps_delay_ms >= 500) /* calculate fps every 0.5 seconds */
6441 global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
6444 fps_counter = Counter();
6447 redraw_mask |= REDRAW_FPS;
6451 if (stored_player[0].jx != stored_player[0].last_jx ||
6452 stored_player[0].jy != stored_player[0].last_jy)
6453 printf("::: %d, %d, %d, %d, %d\n",
6454 stored_player[0].MovDir,
6455 stored_player[0].MovPos,
6456 stored_player[0].GfxPos,
6457 stored_player[0].Frame,
6458 stored_player[0].StepFrame);
6465 for (i=0; i<MAX_PLAYERS; i++)
6468 MOVE_DELAY_NORMAL_SPEED / stored_player[i].move_delay_value;
6470 stored_player[i].Frame += move_frames;
6472 if (stored_player[i].MovPos != 0)
6473 stored_player[i].StepFrame += move_frames;
6478 if (local_player->show_envelope != 0 && local_player->MovPos == 0)
6480 ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
6482 local_player->show_envelope = 0;
6487 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
6489 int min_x = x, min_y = y, max_x = x, max_y = y;
6492 for (i=0; i<MAX_PLAYERS; i++)
6494 int jx = stored_player[i].jx, jy = stored_player[i].jy;
6496 if (!stored_player[i].active || &stored_player[i] == player)
6499 min_x = MIN(min_x, jx);
6500 min_y = MIN(min_y, jy);
6501 max_x = MAX(max_x, jx);
6502 max_y = MAX(max_y, jy);
6505 return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
6508 static boolean AllPlayersInVisibleScreen()
6512 for (i=0; i<MAX_PLAYERS; i++)
6514 int jx = stored_player[i].jx, jy = stored_player[i].jy;
6516 if (!stored_player[i].active)
6519 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
6526 void ScrollLevel(int dx, int dy)
6528 int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
6531 BlitBitmap(drawto_field, drawto_field,
6532 FX + TILEX * (dx == -1) - softscroll_offset,
6533 FY + TILEY * (dy == -1) - softscroll_offset,
6534 SXSIZE - TILEX * (dx!=0) + 2 * softscroll_offset,
6535 SYSIZE - TILEY * (dy!=0) + 2 * softscroll_offset,
6536 FX + TILEX * (dx == 1) - softscroll_offset,
6537 FY + TILEY * (dy == 1) - softscroll_offset);
6541 x = (dx == 1 ? BX1 : BX2);
6542 for (y=BY1; y <= BY2; y++)
6543 DrawScreenField(x, y);
6548 y = (dy == 1 ? BY1 : BY2);
6549 for (x=BX1; x <= BX2; x++)
6550 DrawScreenField(x, y);
6553 redraw_mask |= REDRAW_FIELD;
6556 static void CheckGravityMovement(struct PlayerInfo *player)
6558 if (game.gravity && !player->programmed_action)
6560 int move_dir_vertical = player->action & (MV_UP | MV_DOWN);
6561 int move_dir_horizontal = player->action & (MV_LEFT | MV_RIGHT);
6563 (player->last_move_dir & (MV_LEFT | MV_RIGHT) ?
6564 (move_dir_vertical ? move_dir_vertical : move_dir_horizontal) :
6565 (move_dir_horizontal ? move_dir_horizontal : move_dir_vertical));
6566 int jx = player->jx, jy = player->jy;
6567 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
6568 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
6569 int new_jx = jx + dx, new_jy = jy + dy;
6570 boolean field_under_player_is_free =
6571 (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
6572 boolean player_is_moving_to_valid_field =
6573 (IN_LEV_FIELD(new_jx, new_jy) &&
6574 (Feld[new_jx][new_jy] == EL_SP_BASE ||
6575 Feld[new_jx][new_jy] == EL_SAND));
6576 /* !!! extend EL_SAND to anything diggable !!! */
6578 if (field_under_player_is_free &&
6579 !player_is_moving_to_valid_field &&
6580 !IS_WALKABLE_INSIDE(Feld[jx][jy]))
6581 player->programmed_action = MV_DOWN;
6587 -----------------------------------------------------------------------------
6588 dx, dy: direction (non-diagonal) to try to move the player to
6589 real_dx, real_dy: direction as read from input device (can be diagonal)
6592 boolean MovePlayerOneStep(struct PlayerInfo *player,
6593 int dx, int dy, int real_dx, int real_dy)
6596 static int change_sides[4][2] =
6598 /* enter side leave side */
6599 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* moving left */
6600 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* moving right */
6601 { CH_SIDE_BOTTOM, CH_SIDE_TOP }, /* moving up */
6602 { CH_SIDE_TOP, CH_SIDE_BOTTOM } /* moving down */
6604 int move_direction = (dx == -1 ? MV_LEFT :
6605 dx == +1 ? MV_RIGHT :
6607 dy == +1 ? MV_DOWN : MV_NO_MOVING);
6608 int enter_side = change_sides[MV_DIR_BIT(move_direction)][0];
6609 int leave_side = change_sides[MV_DIR_BIT(move_direction)][1];
6611 int jx = player->jx, jy = player->jy;
6612 int new_jx = jx + dx, new_jy = jy + dy;
6616 if (!player->active || (!dx && !dy))
6617 return MF_NO_ACTION;
6619 player->MovDir = (dx < 0 ? MV_LEFT :
6622 dy > 0 ? MV_DOWN : MV_NO_MOVING);
6624 if (!IN_LEV_FIELD(new_jx, new_jy))
6625 return MF_NO_ACTION;
6627 if (!options.network && !AllPlayersInSight(player, new_jx, new_jy))
6628 return MF_NO_ACTION;
6631 element = MovingOrBlocked2Element(new_jx, new_jy);
6633 element = MovingOrBlocked2ElementIfNotLeaving(new_jx, new_jy);
6636 if (DONT_RUN_INTO(element))
6638 if (element == EL_ACID && dx == 0 && dy == 1)
6641 Feld[jx][jy] = EL_PLAYER_1;
6642 InitMovingField(jx, jy, MV_DOWN);
6643 Store[jx][jy] = EL_ACID;
6644 ContinueMoving(jx, jy);
6648 TestIfHeroRunsIntoBadThing(jx, jy, player->MovDir);
6653 can_move = DigField(player, new_jx, new_jy, real_dx, real_dy, DF_DIG);
6654 if (can_move != MF_MOVING)
6657 /* check if DigField() has caused relocation of the player */
6658 if (player->jx != jx || player->jy != jy)
6659 return MF_NO_ACTION;
6661 StorePlayer[jx][jy] = 0;
6662 player->last_jx = jx;
6663 player->last_jy = jy;
6664 player->jx = new_jx;
6665 player->jy = new_jy;
6666 StorePlayer[new_jx][new_jy] = player->element_nr;
6669 (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
6671 ScrollPlayer(player, SCROLL_INIT);
6674 if (IS_CUSTOM_ELEMENT(Feld[jx][jy]))
6676 CheckTriggeredElementSideChange(jx, jy, Feld[jx][jy], leave_side,
6677 CE_OTHER_GETS_LEFT);
6678 CheckElementSideChange(jx, jy, Feld[jx][jy], leave_side,
6679 CE_LEFT_BY_PLAYER, -1);
6682 if (IS_CUSTOM_ELEMENT(Feld[new_jx][new_jy]))
6684 CheckTriggeredElementSideChange(new_jx, new_jy, Feld[new_jx][new_jy],
6685 enter_side, CE_OTHER_GETS_ENTERED);
6686 CheckElementSideChange(new_jx, new_jy, Feld[new_jx][new_jy], enter_side,
6687 CE_ENTERED_BY_PLAYER, -1);
6694 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
6696 int jx = player->jx, jy = player->jy;
6697 int old_jx = jx, old_jy = jy;
6698 int moved = MF_NO_ACTION;
6700 if (!player->active || (!dx && !dy))
6704 if (!FrameReached(&player->move_delay, player->move_delay_value) &&
6708 if (!FrameReached(&player->move_delay, player->move_delay_value) &&
6709 !(tape.playing && tape.file_version < FILE_VERSION_2_0))
6713 /* remove the last programmed player action */
6714 player->programmed_action = 0;
6718 /* should only happen if pre-1.2 tape recordings are played */
6719 /* this is only for backward compatibility */
6721 int original_move_delay_value = player->move_delay_value;
6724 printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%ld]\n",
6728 /* scroll remaining steps with finest movement resolution */
6729 player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
6731 while (player->MovPos)
6733 ScrollPlayer(player, SCROLL_GO_ON);
6734 ScrollScreen(NULL, SCROLL_GO_ON);
6740 player->move_delay_value = original_move_delay_value;
6743 if (player->last_move_dir & (MV_LEFT | MV_RIGHT))
6745 if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
6746 moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
6750 if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
6751 moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
6757 if (moved & MF_MOVING && !ScreenMovPos &&
6758 (player == local_player || !options.network))
6760 int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
6761 int offset = (setup.scroll_delay ? 3 : 0);
6763 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
6765 /* actual player has left the screen -- scroll in that direction */
6766 if (jx != old_jx) /* player has moved horizontally */
6767 scroll_x += (jx - old_jx);
6768 else /* player has moved vertically */
6769 scroll_y += (jy - old_jy);
6773 if (jx != old_jx) /* player has moved horizontally */
6775 if ((player->MovDir == MV_LEFT && scroll_x > jx - MIDPOSX + offset) ||
6776 (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
6777 scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
6779 /* don't scroll over playfield boundaries */
6780 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
6781 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
6783 /* don't scroll more than one field at a time */
6784 scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
6786 /* don't scroll against the player's moving direction */
6787 if ((player->MovDir == MV_LEFT && scroll_x > old_scroll_x) ||
6788 (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
6789 scroll_x = old_scroll_x;
6791 else /* player has moved vertically */
6793 if ((player->MovDir == MV_UP && scroll_y > jy - MIDPOSY + offset) ||
6794 (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
6795 scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
6797 /* don't scroll over playfield boundaries */
6798 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
6799 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
6801 /* don't scroll more than one field at a time */
6802 scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
6804 /* don't scroll against the player's moving direction */
6805 if ((player->MovDir == MV_UP && scroll_y > old_scroll_y) ||
6806 (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
6807 scroll_y = old_scroll_y;
6811 if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
6813 if (!options.network && !AllPlayersInVisibleScreen())
6815 scroll_x = old_scroll_x;
6816 scroll_y = old_scroll_y;
6820 ScrollScreen(player, SCROLL_INIT);
6821 ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
6828 InitPlayerGfxAnimation(player, ACTION_DEFAULT);
6830 if (!(moved & MF_MOVING) && !player->is_pushing)
6835 player->StepFrame = 0;
6837 if (moved & MF_MOVING)
6839 if (old_jx != jx && old_jy == jy)
6840 player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
6841 else if (old_jx == jx && old_jy != jy)
6842 player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
6844 DrawLevelField(jx, jy); /* for "crumbled sand" */
6846 player->last_move_dir = player->MovDir;
6847 player->is_moving = TRUE;
6849 player->is_snapping = FALSE;
6853 player->is_switching = FALSE;
6859 static int change_sides[4][2] =
6861 /* enter side leave side */
6862 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* moving left */
6863 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* moving right */
6864 { CH_SIDE_BOTTOM, CH_SIDE_TOP }, /* moving up */
6865 { CH_SIDE_TOP, CH_SIDE_BOTTOM } /* moving down */
6867 int move_direction = player->MovDir;
6868 int enter_side = change_sides[MV_DIR_BIT(move_direction)][0];
6869 int leave_side = change_sides[MV_DIR_BIT(move_direction)][1];
6872 if (IS_CUSTOM_ELEMENT(Feld[old_jx][old_jy]))
6874 CheckTriggeredElementSideChange(old_jx, old_jy, Feld[old_jx][old_jy],
6875 leave_side, CE_OTHER_GETS_LEFT);
6876 CheckElementSideChange(old_jx, old_jy, Feld[old_jx][old_jy],
6877 leave_side, CE_LEFT_BY_PLAYER, -1);
6880 if (IS_CUSTOM_ELEMENT(Feld[jx][jy]))
6882 CheckTriggeredElementSideChange(jx, jy, Feld[jx][jy],
6883 enter_side, CE_OTHER_GETS_ENTERED);
6884 CheckElementSideChange(jx, jy, Feld[jx][jy],
6885 enter_side, CE_ENTERED_BY_PLAYER, -1);
6896 CheckGravityMovement(player);
6899 player->last_move_dir = MV_NO_MOVING;
6901 player->is_moving = FALSE;
6904 if (game.engine_version < VERSION_IDENT(3,0,7,0))
6906 TestIfHeroTouchesBadThing(jx, jy);
6907 TestIfPlayerTouchesCustomElement(jx, jy);
6910 if (!player->active)
6916 void ScrollPlayer(struct PlayerInfo *player, int mode)
6918 int jx = player->jx, jy = player->jy;
6919 int last_jx = player->last_jx, last_jy = player->last_jy;
6920 int move_stepsize = TILEX / player->move_delay_value;
6922 if (!player->active || !player->MovPos)
6925 if (mode == SCROLL_INIT)
6927 player->actual_frame_counter = FrameCounter;
6928 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
6930 if (Feld[last_jx][last_jy] == EL_EMPTY)
6931 Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
6938 else if (!FrameReached(&player->actual_frame_counter, 1))
6941 player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
6942 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
6944 if (Feld[last_jx][last_jy] == EL_PLAYER_IS_LEAVING)
6945 Feld[last_jx][last_jy] = EL_EMPTY;
6947 /* before DrawPlayer() to draw correct player graphic for this case */
6948 if (player->MovPos == 0)
6949 CheckGravityMovement(player);
6952 DrawPlayer(player); /* needed here only to cleanup last field */
6955 if (player->MovPos == 0) /* player reached destination field */
6957 if (IS_PASSABLE(Feld[last_jx][last_jy]))
6959 /* continue with normal speed after quickly moving through gate */
6960 HALVE_PLAYER_SPEED(player);
6962 /* be able to make the next move without delay */
6963 player->move_delay = 0;
6966 player->last_jx = jx;
6967 player->last_jy = jy;
6969 if (Feld[jx][jy] == EL_EXIT_OPEN ||
6970 Feld[jx][jy] == EL_SP_EXIT_OPEN ||
6971 Feld[jx][jy] == EL_SP_EXIT_OPENING) /* <-- special case */
6973 DrawPlayer(player); /* needed here only to cleanup last field */
6976 if (local_player->friends_still_needed == 0 ||
6977 IS_SP_ELEMENT(Feld[jx][jy]))
6978 player->LevelSolved = player->GameOver = TRUE;
6981 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
6983 TestIfHeroTouchesBadThing(jx, jy);
6984 TestIfPlayerTouchesCustomElement(jx, jy);
6986 TestIfElementTouchesCustomElement(jx, jy); /* for empty space */
6989 if (!player->active)
6993 if (tape.single_step && tape.recording && !tape.pausing &&
6994 !player->programmed_action)
6995 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
6999 void ScrollScreen(struct PlayerInfo *player, int mode)
7001 static unsigned long screen_frame_counter = 0;
7003 if (mode == SCROLL_INIT)
7005 /* set scrolling step size according to actual player's moving speed */
7006 ScrollStepSize = TILEX / player->move_delay_value;
7008 screen_frame_counter = FrameCounter;
7009 ScreenMovDir = player->MovDir;
7010 ScreenMovPos = player->MovPos;
7011 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
7014 else if (!FrameReached(&screen_frame_counter, 1))
7019 ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
7020 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
7021 redraw_mask |= REDRAW_FIELD;
7024 ScreenMovDir = MV_NO_MOVING;
7027 void TestIfPlayerTouchesCustomElement(int x, int y)
7029 static int xy[4][2] =
7036 static int change_sides[4][2] =
7038 /* center side border side */
7039 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
7040 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
7041 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
7042 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
7044 static int touch_dir[4] =
7051 int center_element = Feld[x][y]; /* should always be non-moving! */
7056 int xx = x + xy[i][0];
7057 int yy = y + xy[i][1];
7058 int center_side = change_sides[i][0];
7059 int border_side = change_sides[i][1];
7062 if (!IN_LEV_FIELD(xx, yy))
7065 if (IS_PLAYER(x, y))
7067 if (game.engine_version < VERSION_IDENT(3,0,7,0))
7068 border_element = Feld[xx][yy]; /* may be moving! */
7069 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
7070 border_element = Feld[xx][yy];
7071 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
7072 border_element = MovingOrBlocked2Element(xx, yy);
7074 continue; /* center and border element do not touch */
7076 CheckTriggeredElementSideChange(xx, yy, border_element, border_side,
7077 CE_OTHER_GETS_TOUCHED);
7078 CheckElementSideChange(xx, yy, border_element, border_side,
7079 CE_TOUCHED_BY_PLAYER, -1);
7081 else if (IS_PLAYER(xx, yy))
7083 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
7085 struct PlayerInfo *player = PLAYERINFO(xx, yy);
7087 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
7088 continue; /* center and border element do not touch */
7091 CheckTriggeredElementSideChange(x, y, center_element, center_side,
7092 CE_OTHER_GETS_TOUCHED);
7093 CheckElementSideChange(x, y, center_element, center_side,
7094 CE_TOUCHED_BY_PLAYER, -1);
7101 void TestIfElementTouchesCustomElement(int x, int y)
7103 static int xy[4][2] =
7110 static int change_sides[4][2] =
7112 /* center side border side */
7113 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
7114 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
7115 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
7116 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
7118 static int touch_dir[4] =
7125 boolean change_center_element = FALSE;
7126 int center_element_change_page = 0;
7127 int center_element = Feld[x][y]; /* should always be non-moving! */
7132 int xx = x + xy[i][0];
7133 int yy = y + xy[i][1];
7134 int center_side = change_sides[i][0];
7135 int border_side = change_sides[i][1];
7138 if (!IN_LEV_FIELD(xx, yy))
7141 if (game.engine_version < VERSION_IDENT(3,0,7,0))
7142 border_element = Feld[xx][yy]; /* may be moving! */
7143 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
7144 border_element = Feld[xx][yy];
7145 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
7146 border_element = MovingOrBlocked2Element(xx, yy);
7148 continue; /* center and border element do not touch */
7150 /* check for change of center element (but change it only once) */
7151 if (IS_CUSTOM_ELEMENT(center_element) &&
7152 HAS_ANY_CHANGE_EVENT(center_element, CE_OTHER_IS_TOUCHING) &&
7153 !change_center_element)
7155 for (j=0; j < element_info[center_element].num_change_pages; j++)
7157 struct ElementChangeInfo *change =
7158 &element_info[center_element].change_page[j];
7160 if (change->can_change &&
7161 change->events & CH_EVENT_BIT(CE_OTHER_IS_TOUCHING) &&
7162 change->sides & border_side &&
7163 change->trigger_element == border_element)
7165 change_center_element = TRUE;
7166 center_element_change_page = j;
7173 /* check for change of border element */
7174 if (IS_CUSTOM_ELEMENT(border_element) &&
7175 HAS_ANY_CHANGE_EVENT(border_element, CE_OTHER_IS_TOUCHING))
7177 for (j=0; j < element_info[border_element].num_change_pages; j++)
7179 struct ElementChangeInfo *change =
7180 &element_info[border_element].change_page[j];
7182 if (change->can_change &&
7183 change->events & CH_EVENT_BIT(CE_OTHER_IS_TOUCHING) &&
7184 change->sides & center_side &&
7185 change->trigger_element == center_element)
7187 CheckElementSideChange(xx, yy, border_element, CH_SIDE_ANY,
7188 CE_OTHER_IS_TOUCHING, j);
7195 if (change_center_element)
7196 CheckElementSideChange(x, y, center_element, CH_SIDE_ANY,
7197 CE_OTHER_IS_TOUCHING, center_element_change_page);
7200 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
7202 int i, kill_x = -1, kill_y = -1;
7203 static int test_xy[4][2] =
7210 static int test_dir[4] =
7220 int test_x, test_y, test_move_dir, test_element;
7222 test_x = good_x + test_xy[i][0];
7223 test_y = good_y + test_xy[i][1];
7224 if (!IN_LEV_FIELD(test_x, test_y))
7228 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NO_MOVING);
7231 test_element = Feld[test_x][test_y];
7233 test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
7236 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
7237 2nd case: DONT_TOUCH style bad thing does not move away from good thing
7239 if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
7240 (DONT_TOUCH(test_element) && test_move_dir != test_dir[i]))
7248 if (kill_x != -1 || kill_y != -1)
7250 if (IS_PLAYER(good_x, good_y))
7252 struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
7254 if (player->shield_deadly_time_left > 0)
7255 Bang(kill_x, kill_y);
7256 else if (!PLAYER_PROTECTED(good_x, good_y))
7260 Bang(good_x, good_y);
7264 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
7266 int i, kill_x = -1, kill_y = -1;
7267 int bad_element = Feld[bad_x][bad_y];
7268 static int test_xy[4][2] =
7275 static int touch_dir[4] =
7282 static int test_dir[4] =
7290 if (bad_element == EL_EXPLOSION) /* skip just exploding bad things */
7295 int test_x, test_y, test_move_dir, test_element;
7297 test_x = bad_x + test_xy[i][0];
7298 test_y = bad_y + test_xy[i][1];
7299 if (!IN_LEV_FIELD(test_x, test_y))
7303 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NO_MOVING);
7305 test_element = Feld[test_x][test_y];
7307 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
7308 2nd case: DONT_TOUCH style bad thing does not move away from good thing
7310 if ((DONT_RUN_INTO(bad_element) && bad_move_dir == test_dir[i]) ||
7311 (DONT_TOUCH(bad_element) && test_move_dir != test_dir[i]))
7313 /* good thing is player or penguin that does not move away */
7314 if (IS_PLAYER(test_x, test_y))
7316 struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
7318 if (bad_element == EL_ROBOT && player->is_moving)
7319 continue; /* robot does not kill player if he is moving */
7321 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
7323 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
7324 continue; /* center and border element do not touch */
7331 else if (test_element == EL_PENGUIN)
7340 if (kill_x != -1 || kill_y != -1)
7342 if (IS_PLAYER(kill_x, kill_y))
7344 struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
7346 if (player->shield_deadly_time_left > 0)
7348 else if (!PLAYER_PROTECTED(kill_x, kill_y))
7352 Bang(kill_x, kill_y);
7356 void TestIfHeroTouchesBadThing(int x, int y)
7358 TestIfGoodThingHitsBadThing(x, y, MV_NO_MOVING);
7361 void TestIfHeroRunsIntoBadThing(int x, int y, int move_dir)
7363 TestIfGoodThingHitsBadThing(x, y, move_dir);
7366 void TestIfBadThingTouchesHero(int x, int y)
7368 TestIfBadThingHitsGoodThing(x, y, MV_NO_MOVING);
7371 void TestIfBadThingRunsIntoHero(int x, int y, int move_dir)
7373 TestIfBadThingHitsGoodThing(x, y, move_dir);
7376 void TestIfFriendTouchesBadThing(int x, int y)
7378 TestIfGoodThingHitsBadThing(x, y, MV_NO_MOVING);
7381 void TestIfBadThingTouchesFriend(int x, int y)
7383 TestIfBadThingHitsGoodThing(x, y, MV_NO_MOVING);
7386 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
7388 int i, kill_x = bad_x, kill_y = bad_y;
7389 static int xy[4][2] =
7401 x = bad_x + xy[i][0];
7402 y = bad_y + xy[i][1];
7403 if (!IN_LEV_FIELD(x, y))
7406 element = Feld[x][y];
7407 if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
7408 element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
7416 if (kill_x != bad_x || kill_y != bad_y)
7420 void KillHero(struct PlayerInfo *player)
7422 int jx = player->jx, jy = player->jy;
7424 if (!player->active)
7427 /* remove accessible field at the player's position */
7428 Feld[jx][jy] = EL_EMPTY;
7430 /* deactivate shield (else Bang()/Explode() would not work right) */
7431 player->shield_normal_time_left = 0;
7432 player->shield_deadly_time_left = 0;
7438 static void KillHeroUnlessProtected(int x, int y)
7440 if (!PLAYER_PROTECTED(x, y))
7441 KillHero(PLAYERINFO(x, y));
7444 void BuryHero(struct PlayerInfo *player)
7446 int jx = player->jx, jy = player->jy;
7448 if (!player->active)
7452 PlaySoundLevelElementAction(jx, jy, player->element_nr, ACTION_DYING);
7454 PlaySoundLevel(jx, jy, SND_CLASS_PLAYER_DYING);
7456 PlaySoundLevel(jx, jy, SND_GAME_LOSING);
7458 player->GameOver = TRUE;
7462 void RemoveHero(struct PlayerInfo *player)
7464 int jx = player->jx, jy = player->jy;
7465 int i, found = FALSE;
7467 player->present = FALSE;
7468 player->active = FALSE;
7470 if (!ExplodeField[jx][jy])
7471 StorePlayer[jx][jy] = 0;
7473 for (i=0; i<MAX_PLAYERS; i++)
7474 if (stored_player[i].active)
7478 AllPlayersGone = TRUE;
7485 =============================================================================
7486 checkDiagonalPushing()
7487 -----------------------------------------------------------------------------
7488 check if diagonal input device direction results in pushing of object
7489 (by checking if the alternative direction is walkable, diggable, ...)
7490 =============================================================================
7493 static boolean checkDiagonalPushing(struct PlayerInfo *player,
7494 int x, int y, int real_dx, int real_dy)
7496 int jx, jy, dx, dy, xx, yy;
7498 if (real_dx == 0 || real_dy == 0) /* no diagonal direction => push */
7501 /* diagonal direction: check alternative direction */
7506 xx = jx + (dx == 0 ? real_dx : 0);
7507 yy = jy + (dy == 0 ? real_dy : 0);
7509 return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
7513 =============================================================================
7515 -----------------------------------------------------------------------------
7516 x, y: field next to player (non-diagonal) to try to dig to
7517 real_dx, real_dy: direction as read from input device (can be diagonal)
7518 =============================================================================
7521 int DigField(struct PlayerInfo *player,
7522 int x, int y, int real_dx, int real_dy, int mode)
7524 static int change_sides[4] =
7526 CH_SIDE_RIGHT, /* moving left */
7527 CH_SIDE_LEFT, /* moving right */
7528 CH_SIDE_BOTTOM, /* moving up */
7529 CH_SIDE_TOP, /* moving down */
7531 boolean use_spring_bug = (game.engine_version < VERSION_IDENT(2,2,0,0));
7532 int jx = player->jx, jy = player->jy;
7533 int dx = x - jx, dy = y - jy;
7534 int nextx = x + dx, nexty = y + dy;
7535 int move_direction = (dx == -1 ? MV_LEFT :
7536 dx == +1 ? MV_RIGHT :
7538 dy == +1 ? MV_DOWN : MV_NO_MOVING);
7539 int dig_side = change_sides[MV_DIR_BIT(move_direction)];
7542 if (player->MovPos == 0)
7544 player->is_digging = FALSE;
7545 player->is_collecting = FALSE;
7548 if (player->MovPos == 0) /* last pushing move finished */
7549 player->is_pushing = FALSE;
7551 if (mode == DF_NO_PUSH) /* player just stopped pushing */
7553 player->is_switching = FALSE;
7554 player->push_delay = 0;
7556 return MF_NO_ACTION;
7559 if (IS_MOVING(x, y) || IS_PLAYER(x, y))
7560 return MF_NO_ACTION;
7563 if (IS_TUBE(Feld[jx][jy]) || IS_TUBE(Back[jx][jy]))
7565 if (IS_TUBE(Feld[jx][jy]) ||
7566 (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0)))
7570 int tube_element = (IS_TUBE(Feld[jx][jy]) ? Feld[jx][jy] : Back[jx][jy]);
7571 int tube_leave_directions[][2] =
7573 { EL_TUBE_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
7574 { EL_TUBE_VERTICAL, MV_UP | MV_DOWN },
7575 { EL_TUBE_HORIZONTAL, MV_LEFT | MV_RIGHT },
7576 { EL_TUBE_VERTICAL_LEFT, MV_LEFT | MV_UP | MV_DOWN },
7577 { EL_TUBE_VERTICAL_RIGHT, MV_RIGHT | MV_UP | MV_DOWN },
7578 { EL_TUBE_HORIZONTAL_UP, MV_LEFT | MV_RIGHT | MV_UP },
7579 { EL_TUBE_HORIZONTAL_DOWN, MV_LEFT | MV_RIGHT | MV_DOWN },
7580 { EL_TUBE_LEFT_UP, MV_LEFT | MV_UP },
7581 { EL_TUBE_LEFT_DOWN, MV_LEFT | MV_DOWN },
7582 { EL_TUBE_RIGHT_UP, MV_RIGHT | MV_UP },
7583 { EL_TUBE_RIGHT_DOWN, MV_RIGHT | MV_DOWN },
7584 { -1, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN }
7587 while (tube_leave_directions[i][0] != tube_element)
7590 if (tube_leave_directions[i][0] == -1) /* should not happen */
7594 if (!(tube_leave_directions[i][1] & move_direction))
7595 return MF_NO_ACTION; /* tube has no opening in this direction */
7598 element = Feld[x][y];
7600 if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
7601 game.engine_version >= VERSION_IDENT(2,2,0,0))
7602 return MF_NO_ACTION;
7606 case EL_SP_PORT_LEFT:
7607 case EL_SP_PORT_RIGHT:
7609 case EL_SP_PORT_DOWN:
7610 case EL_SP_PORT_HORIZONTAL:
7611 case EL_SP_PORT_VERTICAL:
7612 case EL_SP_PORT_ANY:
7613 case EL_SP_GRAVITY_PORT_LEFT:
7614 case EL_SP_GRAVITY_PORT_RIGHT:
7615 case EL_SP_GRAVITY_PORT_UP:
7616 case EL_SP_GRAVITY_PORT_DOWN:
7618 element != EL_SP_PORT_LEFT &&
7619 element != EL_SP_GRAVITY_PORT_LEFT &&
7620 element != EL_SP_PORT_HORIZONTAL &&
7621 element != EL_SP_PORT_ANY) ||
7623 element != EL_SP_PORT_RIGHT &&
7624 element != EL_SP_GRAVITY_PORT_RIGHT &&
7625 element != EL_SP_PORT_HORIZONTAL &&
7626 element != EL_SP_PORT_ANY) ||
7628 element != EL_SP_PORT_UP &&
7629 element != EL_SP_GRAVITY_PORT_UP &&
7630 element != EL_SP_PORT_VERTICAL &&
7631 element != EL_SP_PORT_ANY) ||
7633 element != EL_SP_PORT_DOWN &&
7634 element != EL_SP_GRAVITY_PORT_DOWN &&
7635 element != EL_SP_PORT_VERTICAL &&
7636 element != EL_SP_PORT_ANY) ||
7637 !IN_LEV_FIELD(nextx, nexty) ||
7638 !IS_FREE(nextx, nexty))
7639 return MF_NO_ACTION;
7641 if (element == EL_SP_GRAVITY_PORT_LEFT ||
7642 element == EL_SP_GRAVITY_PORT_RIGHT ||
7643 element == EL_SP_GRAVITY_PORT_UP ||
7644 element == EL_SP_GRAVITY_PORT_DOWN)
7645 game.gravity = !game.gravity;
7647 /* automatically move to the next field with double speed */
7648 player->programmed_action = move_direction;
7649 DOUBLE_PLAYER_SPEED(player);
7651 PlaySoundLevel(x, y, SND_CLASS_SP_PORT_PASSING);
7655 case EL_TUBE_VERTICAL:
7656 case EL_TUBE_HORIZONTAL:
7657 case EL_TUBE_VERTICAL_LEFT:
7658 case EL_TUBE_VERTICAL_RIGHT:
7659 case EL_TUBE_HORIZONTAL_UP:
7660 case EL_TUBE_HORIZONTAL_DOWN:
7661 case EL_TUBE_LEFT_UP:
7662 case EL_TUBE_LEFT_DOWN:
7663 case EL_TUBE_RIGHT_UP:
7664 case EL_TUBE_RIGHT_DOWN:
7667 int tube_enter_directions[][2] =
7669 { EL_TUBE_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
7670 { EL_TUBE_VERTICAL, MV_UP | MV_DOWN },
7671 { EL_TUBE_HORIZONTAL, MV_LEFT | MV_RIGHT },
7672 { EL_TUBE_VERTICAL_LEFT, MV_RIGHT | MV_UP | MV_DOWN },
7673 { EL_TUBE_VERTICAL_RIGHT, MV_LEFT | MV_UP | MV_DOWN },
7674 { EL_TUBE_HORIZONTAL_UP, MV_LEFT | MV_RIGHT | MV_DOWN },
7675 { EL_TUBE_HORIZONTAL_DOWN, MV_LEFT | MV_RIGHT | MV_UP },
7676 { EL_TUBE_LEFT_UP, MV_RIGHT | MV_DOWN },
7677 { EL_TUBE_LEFT_DOWN, MV_RIGHT | MV_UP },
7678 { EL_TUBE_RIGHT_UP, MV_LEFT | MV_DOWN },
7679 { EL_TUBE_RIGHT_DOWN, MV_LEFT | MV_UP },
7680 { -1, MV_NO_MOVING }
7683 while (tube_enter_directions[i][0] != element)
7686 if (tube_enter_directions[i][0] == -1) /* should not happen */
7690 if (!(tube_enter_directions[i][1] & move_direction))
7691 return MF_NO_ACTION; /* tube has no opening in this direction */
7693 PlaySoundLevel(x, y, SND_CLASS_TUBE_WALKING);
7699 if (IS_WALKABLE(element))
7701 int sound_action = ACTION_WALKING;
7703 if (element >= EL_GATE_1 && element <= EL_GATE_4)
7705 if (!player->key[element - EL_GATE_1])
7706 return MF_NO_ACTION;
7708 else if (element >= EL_GATE_1_GRAY && element <= EL_GATE_4_GRAY)
7710 if (!player->key[element - EL_GATE_1_GRAY])
7711 return MF_NO_ACTION;
7713 else if (element == EL_EXIT_OPEN ||
7714 element == EL_SP_EXIT_OPEN ||
7715 element == EL_SP_EXIT_OPENING)
7717 sound_action = ACTION_PASSING; /* player is passing exit */
7719 else if (element == EL_EMPTY)
7721 sound_action = ACTION_MOVING; /* nothing to walk on */
7724 /* play sound from background or player, whatever is available */
7725 if (element_info[element].sound[sound_action] != SND_UNDEFINED)
7726 PlaySoundLevelElementAction(x, y, element, sound_action);
7728 PlaySoundLevelElementAction(x, y, player->element_nr, sound_action);
7732 else if (IS_PASSABLE(element))
7734 if (!IN_LEV_FIELD(nextx, nexty) || !IS_FREE(nextx, nexty))
7735 return MF_NO_ACTION;
7738 if (CAN_MOVE(element)) /* only fixed elements can be passed! */
7739 return MF_NO_ACTION;
7742 if (element >= EL_EM_GATE_1 && element <= EL_EM_GATE_4)
7744 if (!player->key[element - EL_EM_GATE_1])
7745 return MF_NO_ACTION;
7747 else if (element >= EL_EM_GATE_1_GRAY && element <= EL_EM_GATE_4_GRAY)
7749 if (!player->key[element - EL_EM_GATE_1_GRAY])
7750 return MF_NO_ACTION;
7753 /* automatically move to the next field with double speed */
7754 player->programmed_action = move_direction;
7755 DOUBLE_PLAYER_SPEED(player);
7757 PlaySoundLevelAction(x, y, ACTION_PASSING);
7761 else if (IS_DIGGABLE(element))
7765 if (mode != DF_SNAP)
7768 GfxElement[x][y] = GFX_ELEMENT(element);
7771 (GFX_CRUMBLED(element) ? EL_SAND : GFX_ELEMENT(element));
7773 player->is_digging = TRUE;
7776 PlaySoundLevelElementAction(x, y, element, ACTION_DIGGING);
7778 CheckTriggeredElementChange(x, y, element, CE_OTHER_GETS_DIGGED);
7781 if (mode == DF_SNAP)
7782 TestIfElementTouchesCustomElement(x, y); /* for empty space */
7787 else if (IS_COLLECTIBLE(element))
7791 if (mode != DF_SNAP)
7793 GfxElement[x][y] = element;
7794 player->is_collecting = TRUE;
7797 if (element == EL_SPEED_PILL)
7798 player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
7799 else if (element == EL_EXTRA_TIME && level.time > 0)
7802 DrawText(DX_TIME, DY_TIME, int2str(TimeLeft, 3), FONT_TEXT_2);
7804 else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
7806 player->shield_normal_time_left += 10;
7807 if (element == EL_SHIELD_DEADLY)
7808 player->shield_deadly_time_left += 10;
7810 else if (element == EL_DYNAMITE || element == EL_SP_DISK_RED)
7812 if (player->inventory_size < MAX_INVENTORY_SIZE)
7813 player->inventory_element[player->inventory_size++] = element;
7815 DrawText(DX_DYNAMITE, DY_DYNAMITE,
7816 int2str(local_player->inventory_size, 3), FONT_TEXT_2);
7818 else if (element == EL_DYNABOMB_INCREASE_NUMBER)
7820 player->dynabomb_count++;
7821 player->dynabombs_left++;
7823 else if (element == EL_DYNABOMB_INCREASE_SIZE)
7825 player->dynabomb_size++;
7827 else if (element == EL_DYNABOMB_INCREASE_POWER)
7829 player->dynabomb_xl = TRUE;
7831 else if ((element >= EL_KEY_1 && element <= EL_KEY_4) ||
7832 (element >= EL_EM_KEY_1 && element <= EL_EM_KEY_4))
7834 int key_nr = (element >= EL_KEY_1 && element <= EL_KEY_4 ?
7835 element - EL_KEY_1 : element - EL_EM_KEY_1);
7837 player->key[key_nr] = TRUE;
7839 DrawMiniGraphicExt(drawto, DX_KEYS + key_nr * MINI_TILEX, DY_KEYS,
7840 el2edimg(EL_KEY_1 + key_nr));
7841 redraw_mask |= REDRAW_DOOR_1;
7843 else if (IS_ENVELOPE(element))
7846 player->show_envelope = element;
7848 ShowEnvelope(element - EL_ENVELOPE_1);
7851 else if (IS_DROPPABLE(element)) /* can be collected and dropped */
7855 for (i=0; i < element_info[element].collect_count; i++)
7856 if (player->inventory_size < MAX_INVENTORY_SIZE)
7857 player->inventory_element[player->inventory_size++] = element;
7859 DrawText(DX_DYNAMITE, DY_DYNAMITE,
7860 int2str(local_player->inventory_size, 3), FONT_TEXT_2);
7862 else if (element_info[element].collect_count > 0)
7864 local_player->gems_still_needed -=
7865 element_info[element].collect_count;
7866 if (local_player->gems_still_needed < 0)
7867 local_player->gems_still_needed = 0;
7869 DrawText(DX_EMERALDS, DY_EMERALDS,
7870 int2str(local_player->gems_still_needed, 3), FONT_TEXT_2);
7873 RaiseScoreElement(element);
7874 PlaySoundLevelElementAction(x, y, element, ACTION_COLLECTING);
7876 CheckTriggeredElementChange(x, y, element, CE_OTHER_GETS_COLLECTED);
7879 if (mode == DF_SNAP)
7880 TestIfElementTouchesCustomElement(x, y); /* for empty space */
7885 else if (IS_PUSHABLE(element))
7887 if (mode == DF_SNAP && element != EL_BD_ROCK)
7888 return MF_NO_ACTION;
7890 if (CAN_FALL(element) && dy)
7891 return MF_NO_ACTION;
7893 if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
7894 !(element == EL_SPRING && use_spring_bug))
7895 return MF_NO_ACTION;
7898 /* do not push elements already moving away faster than player */
7899 if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
7900 ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
7901 return MF_NO_ACTION;
7903 if (element == EL_SPRING && MovDir[x][y] != MV_NO_MOVING)
7904 return MF_NO_ACTION;
7908 if (game.engine_version >= VERSION_IDENT(3,0,7,1))
7910 if (player->push_delay_value == -1)
7911 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
7913 else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
7915 if (!player->is_pushing)
7916 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
7920 if (game.engine_version >= VERSION_IDENT(2,2,0,7) &&
7921 (game.engine_version < VERSION_IDENT(3,0,7,1) ||
7922 !player_is_pushing))
7923 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
7926 if (!player->is_pushing &&
7927 game.engine_version >= VERSION_IDENT(2,2,0,7))
7928 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
7932 printf("::: push delay: %ld [%d, %d] [%d]\n",
7933 player->push_delay_value, FrameCounter, game.engine_version,
7934 player->is_pushing);
7937 player->is_pushing = TRUE;
7939 if (!(IN_LEV_FIELD(nextx, nexty) &&
7940 (IS_FREE(nextx, nexty) ||
7941 (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY &&
7942 IS_SB_ELEMENT(element)))))
7943 return MF_NO_ACTION;
7945 if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
7946 return MF_NO_ACTION;
7948 if (player->push_delay == 0) /* new pushing; restart delay */
7949 player->push_delay = FrameCounter;
7951 if (!FrameReached(&player->push_delay, player->push_delay_value) &&
7952 !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
7953 element != EL_SPRING && element != EL_BALLOON)
7955 /* make sure that there is no move delay before next try to push */
7956 if (game.engine_version >= VERSION_IDENT(3,0,7,1))
7957 player->move_delay = INITIAL_MOVE_DELAY_OFF;
7959 return MF_NO_ACTION;
7963 printf("::: NOW PUSHING... [%d]\n", FrameCounter);
7966 if (IS_SB_ELEMENT(element))
7968 if (element == EL_SOKOBAN_FIELD_FULL)
7970 Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
7971 local_player->sokobanfields_still_needed++;
7974 if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
7976 Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
7977 local_player->sokobanfields_still_needed--;
7980 Feld[x][y] = EL_SOKOBAN_OBJECT;
7982 if (Back[x][y] == Back[nextx][nexty])
7983 PlaySoundLevelAction(x, y, ACTION_PUSHING);
7984 else if (Back[x][y] != 0)
7985 PlaySoundLevelElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
7988 PlaySoundLevelElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
7991 if (local_player->sokobanfields_still_needed == 0 &&
7992 game.emulation == EMU_SOKOBAN)
7994 player->LevelSolved = player->GameOver = TRUE;
7995 PlaySoundLevel(x, y, SND_GAME_SOKOBAN_SOLVING);
7999 PlaySoundLevelElementAction(x, y, element, ACTION_PUSHING);
8001 InitMovingField(x, y, move_direction);
8002 GfxAction[x][y] = ACTION_PUSHING;
8004 if (mode == DF_SNAP)
8005 ContinueMoving(x, y);
8007 MovPos[x][y] = (dx != 0 ? dx : dy);
8009 Pushed[x][y] = TRUE;
8010 Pushed[nextx][nexty] = TRUE;
8012 if (game.engine_version < VERSION_IDENT(2,2,0,7))
8013 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
8015 player->push_delay_value = -1; /* get new value later */
8017 CheckTriggeredElementSideChange(x, y, element, dig_side,
8018 CE_OTHER_GETS_PUSHED);
8019 CheckElementSideChange(x, y, element, dig_side,
8020 CE_PUSHED_BY_PLAYER, -1);
8024 else if (IS_SWITCHABLE(element))
8026 if (PLAYER_SWITCHING(player, x, y))
8029 player->is_switching = TRUE;
8030 player->switch_x = x;
8031 player->switch_y = y;
8033 PlaySoundLevelElementAction(x, y, element, ACTION_ACTIVATING);
8035 if (element == EL_ROBOT_WHEEL)
8037 Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
8041 DrawLevelField(x, y);
8043 else if (element == EL_SP_TERMINAL)
8047 for (yy=0; yy < lev_fieldy; yy++) for (xx=0; xx < lev_fieldx; xx++)
8049 if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
8051 else if (Feld[xx][yy] == EL_SP_TERMINAL)
8052 Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
8055 else if (IS_BELT_SWITCH(element))
8057 ToggleBeltSwitch(x, y);
8059 else if (element == EL_SWITCHGATE_SWITCH_UP ||
8060 element == EL_SWITCHGATE_SWITCH_DOWN)
8062 ToggleSwitchgateSwitch(x, y);
8064 else if (element == EL_LIGHT_SWITCH ||
8065 element == EL_LIGHT_SWITCH_ACTIVE)
8067 ToggleLightSwitch(x, y);
8070 PlaySoundLevel(x, y, element == EL_LIGHT_SWITCH ?
8071 SND_LIGHT_SWITCH_ACTIVATING :
8072 SND_LIGHT_SWITCH_DEACTIVATING);
8075 else if (element == EL_TIMEGATE_SWITCH)
8077 ActivateTimegateSwitch(x, y);
8079 else if (element == EL_BALLOON_SWITCH_LEFT ||
8080 element == EL_BALLOON_SWITCH_RIGHT ||
8081 element == EL_BALLOON_SWITCH_UP ||
8082 element == EL_BALLOON_SWITCH_DOWN ||
8083 element == EL_BALLOON_SWITCH_ANY)
8085 if (element == EL_BALLOON_SWITCH_ANY)
8086 game.balloon_dir = move_direction;
8088 game.balloon_dir = (element == EL_BALLOON_SWITCH_LEFT ? MV_LEFT :
8089 element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
8090 element == EL_BALLOON_SWITCH_UP ? MV_UP :
8091 element == EL_BALLOON_SWITCH_DOWN ? MV_DOWN :
8094 else if (element == EL_LAMP)
8096 Feld[x][y] = EL_LAMP_ACTIVE;
8097 local_player->lights_still_needed--;
8099 DrawLevelField(x, y);
8101 else if (element == EL_TIME_ORB_FULL)
8103 Feld[x][y] = EL_TIME_ORB_EMPTY;
8105 DrawText(DX_TIME, DY_TIME, int2str(TimeLeft, 3), FONT_TEXT_2);
8107 DrawLevelField(x, y);
8110 PlaySoundStereo(SND_TIME_ORB_FULL_COLLECTING, SOUND_MIDDLE);
8118 if (!PLAYER_SWITCHING(player, x, y))
8120 player->is_switching = TRUE;
8121 player->switch_x = x;
8122 player->switch_y = y;
8124 CheckTriggeredElementSideChange(x, y, element, dig_side,
8125 CE_OTHER_IS_SWITCHING);
8126 CheckElementSideChange(x, y, element, dig_side, CE_SWITCHED, -1);
8129 CheckTriggeredElementSideChange(x, y, element, dig_side,
8130 CE_OTHER_GETS_PRESSED);
8131 CheckElementSideChange(x, y, element, dig_side,
8132 CE_PRESSED_BY_PLAYER, -1);
8135 return MF_NO_ACTION;
8138 player->push_delay = 0;
8140 if (Feld[x][y] != element) /* really digged/collected something */
8141 player->is_collecting = !player->is_digging;
8146 boolean SnapField(struct PlayerInfo *player, int dx, int dy)
8148 int jx = player->jx, jy = player->jy;
8149 int x = jx + dx, y = jy + dy;
8150 int snap_direction = (dx == -1 ? MV_LEFT :
8151 dx == +1 ? MV_RIGHT :
8153 dy == +1 ? MV_DOWN : MV_NO_MOVING);
8155 if (player->MovPos && game.engine_version >= VERSION_IDENT(2,2,0,0))
8158 if (!player->active || !IN_LEV_FIELD(x, y))
8166 if (player->MovPos == 0)
8167 player->is_pushing = FALSE;
8169 player->is_snapping = FALSE;
8171 if (player->MovPos == 0)
8173 player->is_moving = FALSE;
8174 player->is_digging = FALSE;
8175 player->is_collecting = FALSE;
8181 if (player->is_snapping)
8184 player->MovDir = snap_direction;
8186 player->is_moving = FALSE;
8187 player->is_digging = FALSE;
8188 player->is_collecting = FALSE;
8190 if (DigField(player, x, y, 0, 0, DF_SNAP) == MF_NO_ACTION)
8193 player->is_snapping = TRUE;
8195 player->is_moving = FALSE;
8196 player->is_digging = FALSE;
8197 player->is_collecting = FALSE;
8199 DrawLevelField(x, y);
8205 boolean DropElement(struct PlayerInfo *player)
8207 int jx = player->jx, jy = player->jy;
8210 if (!player->active || player->MovPos)
8213 old_element = Feld[jx][jy];
8215 /* check if player has anything that can be dropped */
8216 if (player->inventory_size == 0 && player->dynabombs_left == 0)
8219 /* check if anything can be dropped at the current position */
8220 if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
8223 /* collected custom elements can only be dropped on empty fields */
8224 if (player->inventory_size > 0 &&
8225 IS_CUSTOM_ELEMENT(player->inventory_element[player->inventory_size - 1])
8226 && old_element != EL_EMPTY)
8229 if (old_element != EL_EMPTY)
8230 Back[jx][jy] = old_element; /* store old element on this field */
8232 MovDelay[jx][jy] = 96;
8234 ResetGfxAnimation(jx, jy);
8235 ResetRandomAnimationValue(jx, jy);
8237 if (player->inventory_size > 0)
8239 int new_element = player->inventory_element[--player->inventory_size];
8241 Feld[jx][jy] = (new_element == EL_DYNAMITE ? EL_DYNAMITE_ACTIVE :
8242 new_element == EL_SP_DISK_RED ? EL_SP_DISK_RED_ACTIVE :
8245 DrawText(DX_DYNAMITE, DY_DYNAMITE,
8246 int2str(local_player->inventory_size, 3), FONT_TEXT_2);
8248 if (IN_SCR_FIELD(SCREENX(jx), SCREENY(jy)))
8249 DrawGraphicThruMask(SCREENX(jx), SCREENY(jy), el2img(Feld[jx][jy]), 0);
8251 PlaySoundLevelAction(jx, jy, ACTION_DROPPING);
8253 CheckTriggeredElementChange(jx, jy, new_element, CE_OTHER_GETS_DROPPED);
8254 CheckElementChange(jx, jy, new_element, CE_DROPPED_BY_PLAYER);
8256 TestIfElementTouchesCustomElement(jx, jy);
8258 else /* player is dropping a dyna bomb */
8260 player->dynabombs_left--;
8263 EL_DYNABOMB_PLAYER_1_ACTIVE + (player->element_nr - EL_PLAYER_1);
8265 if (IN_SCR_FIELD(SCREENX(jx), SCREENY(jy)))
8266 DrawGraphicThruMask(SCREENX(jx), SCREENY(jy), el2img(Feld[jx][jy]), 0);
8268 PlaySoundLevelAction(jx, jy, ACTION_DROPPING);
8274 /* ------------------------------------------------------------------------- */
8275 /* game sound playing functions */
8276 /* ------------------------------------------------------------------------- */
8278 static int *loop_sound_frame = NULL;
8279 static int *loop_sound_volume = NULL;
8281 void InitPlaySoundLevel()
8283 int num_sounds = getSoundListSize();
8285 if (loop_sound_frame != NULL)
8286 free(loop_sound_frame);
8288 if (loop_sound_volume != NULL)
8289 free(loop_sound_volume);
8291 loop_sound_frame = checked_calloc(num_sounds * sizeof(int));
8292 loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
8295 static void PlaySoundLevel(int x, int y, int nr)
8297 int sx = SCREENX(x), sy = SCREENY(y);
8298 int volume, stereo_position;
8299 int max_distance = 8;
8300 int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
8302 if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
8303 (!setup.sound_loops && IS_LOOP_SOUND(nr)))
8306 if (!IN_LEV_FIELD(x, y) ||
8307 sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
8308 sy < -max_distance || sy >= SCR_FIELDY + max_distance)
8311 volume = SOUND_MAX_VOLUME;
8313 if (!IN_SCR_FIELD(sx, sy))
8315 int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
8316 int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
8318 volume -= volume * (dx > dy ? dx : dy) / max_distance;
8321 stereo_position = (SOUND_MAX_LEFT +
8322 (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
8323 (SCR_FIELDX + 2 * max_distance));
8325 if (IS_LOOP_SOUND(nr))
8327 /* This assures that quieter loop sounds do not overwrite louder ones,
8328 while restarting sound volume comparison with each new game frame. */
8330 if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
8333 loop_sound_volume[nr] = volume;
8334 loop_sound_frame[nr] = FrameCounter;
8337 PlaySoundExt(nr, volume, stereo_position, type);
8340 static void PlaySoundLevelNearest(int x, int y, int sound_action)
8342 PlaySoundLevel(x < LEVELX(BX1) ? LEVELX(BX1) :
8343 x > LEVELX(BX2) ? LEVELX(BX2) : x,
8344 y < LEVELY(BY1) ? LEVELY(BY1) :
8345 y > LEVELY(BY2) ? LEVELY(BY2) : y,
8349 static void PlaySoundLevelAction(int x, int y, int action)
8351 PlaySoundLevelElementAction(x, y, Feld[x][y], action);
8354 static void PlaySoundLevelElementAction(int x, int y, int element, int action)
8356 int sound_effect = element_info[element].sound[action];
8358 if (sound_effect != SND_UNDEFINED)
8359 PlaySoundLevel(x, y, sound_effect);
8362 static void PlaySoundLevelActionIfLoop(int x, int y, int action)
8364 int sound_effect = element_info[Feld[x][y]].sound[action];
8366 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
8367 PlaySoundLevel(x, y, sound_effect);
8370 static void StopSoundLevelActionIfLoop(int x, int y, int action)
8372 int sound_effect = element_info[Feld[x][y]].sound[action];
8374 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
8375 StopSoundExt(sound_effect, SND_CTRL_STOP_SOUND);
8378 void RaiseScore(int value)
8380 local_player->score += value;
8381 DrawText(DX_SCORE, DY_SCORE, int2str(local_player->score, 5), FONT_TEXT_2);
8384 void RaiseScoreElement(int element)
8390 case EL_EMERALD_YELLOW:
8391 case EL_EMERALD_RED:
8392 case EL_EMERALD_PURPLE:
8393 case EL_SP_INFOTRON:
8394 RaiseScore(level.score[SC_EMERALD]);
8397 RaiseScore(level.score[SC_DIAMOND]);
8400 RaiseScore(level.score[SC_CRYSTAL]);
8403 RaiseScore(level.score[SC_PEARL]);
8406 case EL_BD_BUTTERFLY:
8407 case EL_SP_ELECTRON:
8408 RaiseScore(level.score[SC_BUG]);
8412 case EL_SP_SNIKSNAK:
8413 RaiseScore(level.score[SC_SPACESHIP]);
8416 case EL_DARK_YAMYAM:
8417 RaiseScore(level.score[SC_YAMYAM]);
8420 RaiseScore(level.score[SC_ROBOT]);
8423 RaiseScore(level.score[SC_PACMAN]);
8426 RaiseScore(level.score[SC_NUT]);
8429 case EL_SP_DISK_RED:
8430 case EL_DYNABOMB_INCREASE_NUMBER:
8431 case EL_DYNABOMB_INCREASE_SIZE:
8432 case EL_DYNABOMB_INCREASE_POWER:
8433 RaiseScore(level.score[SC_DYNAMITE]);
8435 case EL_SHIELD_NORMAL:
8436 case EL_SHIELD_DEADLY:
8437 RaiseScore(level.score[SC_SHIELD]);
8440 RaiseScore(level.score[SC_TIME_BONUS]);
8446 RaiseScore(level.score[SC_KEY]);
8449 RaiseScore(element_info[element].collect_score);
8454 void RequestQuitGame(boolean ask_if_really_quit)
8456 if (AllPlayersGone ||
8457 !ask_if_really_quit ||
8458 level_editor_test_game ||
8459 Request("Do you really want to quit the game ?",
8460 REQ_ASK | REQ_STAY_CLOSED))
8462 #if defined(PLATFORM_UNIX)
8463 if (options.network)
8464 SendToServer_StopPlaying();
8468 game_status = GAME_MODE_MAIN;
8474 OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
8479 /* ---------- new game button stuff ---------------------------------------- */
8481 /* graphic position values for game buttons */
8482 #define GAME_BUTTON_XSIZE 30
8483 #define GAME_BUTTON_YSIZE 30
8484 #define GAME_BUTTON_XPOS 5
8485 #define GAME_BUTTON_YPOS 215
8486 #define SOUND_BUTTON_XPOS 5
8487 #define SOUND_BUTTON_YPOS (GAME_BUTTON_YPOS + GAME_BUTTON_YSIZE)
8489 #define GAME_BUTTON_STOP_XPOS (GAME_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
8490 #define GAME_BUTTON_PAUSE_XPOS (GAME_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
8491 #define GAME_BUTTON_PLAY_XPOS (GAME_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
8492 #define SOUND_BUTTON_MUSIC_XPOS (SOUND_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
8493 #define SOUND_BUTTON_LOOPS_XPOS (SOUND_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
8494 #define SOUND_BUTTON_SIMPLE_XPOS (SOUND_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
8501 } gamebutton_info[NUM_GAME_BUTTONS] =
8504 GAME_BUTTON_STOP_XPOS, GAME_BUTTON_YPOS,
8509 GAME_BUTTON_PAUSE_XPOS, GAME_BUTTON_YPOS,
8514 GAME_BUTTON_PLAY_XPOS, GAME_BUTTON_YPOS,
8519 SOUND_BUTTON_MUSIC_XPOS, SOUND_BUTTON_YPOS,
8520 SOUND_CTRL_ID_MUSIC,
8521 "background music on/off"
8524 SOUND_BUTTON_LOOPS_XPOS, SOUND_BUTTON_YPOS,
8525 SOUND_CTRL_ID_LOOPS,
8526 "sound loops on/off"
8529 SOUND_BUTTON_SIMPLE_XPOS, SOUND_BUTTON_YPOS,
8530 SOUND_CTRL_ID_SIMPLE,
8531 "normal sounds on/off"
8535 void CreateGameButtons()
8539 for (i=0; i<NUM_GAME_BUTTONS; i++)
8541 Bitmap *gd_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
8542 struct GadgetInfo *gi;
8545 unsigned long event_mask;
8546 int gd_xoffset, gd_yoffset;
8547 int gd_x1, gd_x2, gd_y1, gd_y2;
8550 gd_xoffset = gamebutton_info[i].x;
8551 gd_yoffset = gamebutton_info[i].y;
8552 gd_x1 = DOOR_GFX_PAGEX4 + gd_xoffset;
8553 gd_x2 = DOOR_GFX_PAGEX3 + gd_xoffset;
8555 if (id == GAME_CTRL_ID_STOP ||
8556 id == GAME_CTRL_ID_PAUSE ||
8557 id == GAME_CTRL_ID_PLAY)
8559 button_type = GD_TYPE_NORMAL_BUTTON;
8561 event_mask = GD_EVENT_RELEASED;
8562 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
8563 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
8567 button_type = GD_TYPE_CHECK_BUTTON;
8569 ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
8570 (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
8571 (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
8572 event_mask = GD_EVENT_PRESSED;
8573 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset;
8574 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
8577 gi = CreateGadget(GDI_CUSTOM_ID, id,
8578 GDI_INFO_TEXT, gamebutton_info[i].infotext,
8579 GDI_X, DX + gd_xoffset,
8580 GDI_Y, DY + gd_yoffset,
8581 GDI_WIDTH, GAME_BUTTON_XSIZE,
8582 GDI_HEIGHT, GAME_BUTTON_YSIZE,
8583 GDI_TYPE, button_type,
8584 GDI_STATE, GD_BUTTON_UNPRESSED,
8585 GDI_CHECKED, checked,
8586 GDI_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y1,
8587 GDI_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y1,
8588 GDI_ALT_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y2,
8589 GDI_ALT_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y2,
8590 GDI_EVENT_MASK, event_mask,
8591 GDI_CALLBACK_ACTION, HandleGameButtons,
8595 Error(ERR_EXIT, "cannot create gadget");
8597 game_gadget[id] = gi;
8601 void FreeGameButtons()
8605 for (i=0; i<NUM_GAME_BUTTONS; i++)
8606 FreeGadget(game_gadget[i]);
8609 static void MapGameButtons()
8613 for (i=0; i<NUM_GAME_BUTTONS; i++)
8614 MapGadget(game_gadget[i]);
8617 void UnmapGameButtons()
8621 for (i=0; i<NUM_GAME_BUTTONS; i++)
8622 UnmapGadget(game_gadget[i]);
8625 static void HandleGameButtons(struct GadgetInfo *gi)
8627 int id = gi->custom_id;
8629 if (game_status != GAME_MODE_PLAYING)
8634 case GAME_CTRL_ID_STOP:
8635 RequestQuitGame(TRUE);
8638 case GAME_CTRL_ID_PAUSE:
8639 if (options.network)
8641 #if defined(PLATFORM_UNIX)
8643 SendToServer_ContinuePlaying();
8645 SendToServer_PausePlaying();
8649 TapeTogglePause(TAPE_TOGGLE_MANUAL);
8652 case GAME_CTRL_ID_PLAY:
8655 #if defined(PLATFORM_UNIX)
8656 if (options.network)
8657 SendToServer_ContinuePlaying();
8661 tape.pausing = FALSE;
8662 DrawVideoDisplay(VIDEO_STATE_PAUSE_OFF,0);
8667 case SOUND_CTRL_ID_MUSIC:
8668 if (setup.sound_music)
8670 setup.sound_music = FALSE;
8673 else if (audio.music_available)
8675 setup.sound = setup.sound_music = TRUE;
8677 SetAudioMode(setup.sound);
8678 PlayMusic(level_nr);
8682 case SOUND_CTRL_ID_LOOPS:
8683 if (setup.sound_loops)
8684 setup.sound_loops = FALSE;
8685 else if (audio.loops_available)
8687 setup.sound = setup.sound_loops = TRUE;
8688 SetAudioMode(setup.sound);
8692 case SOUND_CTRL_ID_SIMPLE:
8693 if (setup.sound_simple)
8694 setup.sound_simple = FALSE;
8695 else if (audio.sound_available)
8697 setup.sound = setup.sound_simple = TRUE;
8698 SetAudioMode(setup.sound);