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) ? 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 changing elements ------------------------------- */
812 /* initialize changing elements information */
813 for (i=0; i < MAX_NUM_ELEMENTS; i++)
815 struct ElementInfo *ei = &element_info[i];
817 /* this pointer might have been changed in the level editor */
818 ei->change = &ei->change_page[0];
820 if (!IS_CUSTOM_ELEMENT(i))
822 ei->change->target_element = EL_EMPTY_SPACE;
823 ei->change->delay_fixed = 0;
824 ei->change->delay_random = 0;
825 ei->change->delay_frames = 1;
828 ei->change_events = CE_BITMASK_DEFAULT;
829 for (j=0; j < NUM_CHANGE_EVENTS; j++)
831 ei->event_page_nr[j] = 0;
832 ei->event_page[j] = &ei->change_page[0];
836 /* add changing elements from pre-defined list */
837 for (i=0; change_delay_list[i].element != EL_UNDEFINED; i++)
839 struct ChangingElementInfo *ch_delay = &change_delay_list[i];
840 struct ElementInfo *ei = &element_info[ch_delay->element];
842 ei->change->target_element = ch_delay->target_element;
843 ei->change->delay_fixed = ch_delay->change_delay;
845 ei->change->pre_change_function = ch_delay->pre_change_function;
846 ei->change->change_function = ch_delay->change_function;
847 ei->change->post_change_function = ch_delay->post_change_function;
849 ei->change_events |= CH_EVENT_BIT(CE_DELAY);
853 /* add change events from custom element configuration */
854 for (i=0; i < NUM_CUSTOM_ELEMENTS; i++)
856 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
858 for (j=0; j < ei->num_change_pages; j++)
860 if (!ei->change_page[j].can_change)
863 for (k=0; k < NUM_CHANGE_EVENTS; k++)
865 /* only add event page for the first page found with this event */
866 if (ei->change_page[j].events & CH_EVENT_BIT(k) &&
867 !(ei->change_events & CH_EVENT_BIT(k)))
869 ei->change_events |= CH_EVENT_BIT(k);
870 ei->event_page_nr[k] = j;
871 ei->event_page[k] = &ei->change_page[j];
879 /* add change events from custom element configuration */
880 for (i=0; i < NUM_CUSTOM_ELEMENTS; i++)
882 int element = EL_CUSTOM_START + i;
884 /* only add custom elements that change after fixed/random frame delay */
885 if (CAN_CHANGE(element) && HAS_CHANGE_EVENT(element, CE_DELAY))
886 element_info[element].change_events |= CH_EVENT_BIT(CE_DELAY);
890 /* ---------- initialize trigger events ---------------------------------- */
892 /* initialize trigger events information */
893 for (i=0; i<MAX_NUM_ELEMENTS; i++)
894 trigger_events[i] = EP_BITMASK_DEFAULT;
897 /* add trigger events from element change event properties */
898 for (i=0; i<MAX_NUM_ELEMENTS; i++)
900 struct ElementInfo *ei = &element_info[i];
902 for (j=0; j < ei->num_change_pages; j++)
904 if (!ei->change_page[j].can_change)
907 if (ei->change_page[j].events & CH_EVENT_BIT(CE_BY_OTHER_ACTION))
909 int trigger_element = ei->change_page[j].trigger_element;
911 trigger_events[trigger_element] |= ei->change_page[j].events;
916 /* add trigger events from element change event properties */
917 for (i=0; i<MAX_NUM_ELEMENTS; i++)
918 if (HAS_CHANGE_EVENT(i, CE_BY_OTHER_ACTION))
919 trigger_events[element_info[i].change->trigger_element] |=
920 element_info[i].change->events;
923 /* ---------- initialize push delay -------------------------------------- */
925 /* initialize push delay values to default */
926 for (i=0; i<MAX_NUM_ELEMENTS; i++)
928 if (!IS_CUSTOM_ELEMENT(i))
930 element_info[i].push_delay_fixed = 8;
931 element_info[i].push_delay_random = 8;
935 /* set push delay value for certain elements from pre-defined list */
936 for (i=0; push_delay_list[i].element != EL_UNDEFINED; i++)
938 int e = push_delay_list[i].element;
940 element_info[e].push_delay_fixed = push_delay_list[i].push_delay_fixed;
941 element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
944 /* ---------- initialize move stepsize ----------------------------------- */
946 /* initialize move stepsize values to default */
947 for (i=0; i<MAX_NUM_ELEMENTS; i++)
948 if (!IS_CUSTOM_ELEMENT(i))
949 element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
951 /* set move stepsize value for certain elements from pre-defined list */
952 for (i=0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
954 int e = move_stepsize_list[i].element;
956 element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
959 /* ---------- initialize gem count --------------------------------------- */
961 /* initialize gem count values for each element */
962 for (i=0; i<MAX_NUM_ELEMENTS; i++)
963 if (!IS_CUSTOM_ELEMENT(i))
964 element_info[i].collect_count = 0;
966 /* add gem count values for all elements from pre-defined list */
967 for (i=0; collect_count_list[i].element != EL_UNDEFINED; i++)
968 element_info[collect_count_list[i].element].collect_count =
969 collect_count_list[i].count;
974 =============================================================================
976 -----------------------------------------------------------------------------
977 initialize and start new game
978 =============================================================================
983 boolean emulate_bd = TRUE; /* unless non-BOULDERDASH elements found */
984 boolean emulate_sb = TRUE; /* unless non-SOKOBAN elements found */
985 boolean emulate_sp = TRUE; /* unless non-SUPAPLEX elements found */
992 #if USE_NEW_AMOEBA_CODE
993 printf("Using new amoeba code.\n");
995 printf("Using old amoeba code.\n");
1000 /* don't play tapes over network */
1001 network_playing = (options.network && !tape.playing);
1003 for (i=0; i<MAX_PLAYERS; i++)
1005 struct PlayerInfo *player = &stored_player[i];
1007 player->index_nr = i;
1008 player->element_nr = EL_PLAYER_1 + i;
1010 player->present = FALSE;
1011 player->active = FALSE;
1014 player->effective_action = 0;
1015 player->programmed_action = 0;
1018 player->gems_still_needed = level.gems_needed;
1019 player->sokobanfields_still_needed = 0;
1020 player->lights_still_needed = 0;
1021 player->friends_still_needed = 0;
1024 player->key[j] = FALSE;
1026 player->dynabomb_count = 0;
1027 player->dynabomb_size = 1;
1028 player->dynabombs_left = 0;
1029 player->dynabomb_xl = FALSE;
1031 player->MovDir = MV_NO_MOVING;
1034 player->GfxDir = MV_NO_MOVING;
1035 player->GfxAction = ACTION_DEFAULT;
1037 player->StepFrame = 0;
1039 player->use_murphy_graphic = FALSE;
1041 player->actual_frame_counter = 0;
1043 player->last_move_dir = MV_NO_MOVING;
1045 player->is_waiting = FALSE;
1046 player->is_moving = FALSE;
1047 player->is_digging = FALSE;
1048 player->is_snapping = FALSE;
1049 player->is_collecting = FALSE;
1050 player->is_pushing = FALSE;
1051 player->is_switching = FALSE;
1053 player->switch_x = -1;
1054 player->switch_y = -1;
1056 player->show_envelope = 0;
1058 player->move_delay = game.initial_move_delay;
1059 player->move_delay_value = game.initial_move_delay_value;
1061 player->push_delay = 0;
1062 player->push_delay_value = 5;
1064 player->last_jx = player->last_jy = 0;
1065 player->jx = player->jy = 0;
1067 player->shield_normal_time_left = 0;
1068 player->shield_deadly_time_left = 0;
1070 player->inventory_size = 0;
1072 DigField(player, 0, 0, 0, 0, DF_NO_PUSH);
1073 SnapField(player, 0, 0);
1075 player->LevelSolved = FALSE;
1076 player->GameOver = FALSE;
1079 network_player_action_received = FALSE;
1081 #if defined(PLATFORM_UNIX)
1082 /* initial null action */
1083 if (network_playing)
1084 SendToServer_MovePlayer(MV_NO_MOVING);
1092 TimeLeft = level.time;
1094 ScreenMovDir = MV_NO_MOVING;
1098 ScrollStepSize = 0; /* will be correctly initialized by ScrollScreen() */
1100 AllPlayersGone = FALSE;
1102 game.yamyam_content_nr = 0;
1103 game.magic_wall_active = FALSE;
1104 game.magic_wall_time_left = 0;
1105 game.light_time_left = 0;
1106 game.timegate_time_left = 0;
1107 game.switchgate_pos = 0;
1108 game.balloon_dir = MV_NO_MOVING;
1109 game.gravity = level.initial_gravity;
1110 game.explosions_delayed = TRUE;
1112 game.envelope_active = FALSE;
1116 game.belt_dir[i] = MV_NO_MOVING;
1117 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
1120 for (i=0; i<MAX_NUM_AMOEBA; i++)
1121 AmoebaCnt[i] = AmoebaCnt2[i] = 0;
1123 for (x=0; x<lev_fieldx; x++)
1125 for (y=0; y<lev_fieldy; y++)
1127 Feld[x][y] = level.field[x][y];
1128 MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
1129 ChangeDelay[x][y] = 0;
1130 ChangePage[x][y] = -1;
1131 Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
1133 WasJustMoving[x][y] = 0;
1134 WasJustFalling[x][y] = 0;
1136 Pushed[x][y] = FALSE;
1138 Changed[x][y] = CE_BITMASK_DEFAULT;
1139 ChangeEvent[x][y] = CE_BITMASK_DEFAULT;
1141 ExplodePhase[x][y] = 0;
1142 ExplodeField[x][y] = EX_NO_EXPLOSION;
1145 GfxAction[x][y] = ACTION_DEFAULT;
1146 GfxRandom[x][y] = INIT_GFX_RANDOM();
1147 GfxElement[x][y] = EL_UNDEFINED;
1151 for(y=0; y<lev_fieldy; y++)
1153 for(x=0; x<lev_fieldx; x++)
1155 if (emulate_bd && !IS_BD_ELEMENT(Feld[x][y]))
1157 if (emulate_sb && !IS_SB_ELEMENT(Feld[x][y]))
1159 if (emulate_sp && !IS_SP_ELEMENT(Feld[x][y]))
1162 InitField(x, y, TRUE);
1168 game.emulation = (emulate_bd ? EMU_BOULDERDASH :
1169 emulate_sb ? EMU_SOKOBAN :
1170 emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
1172 /* correct non-moving belts to start moving left */
1174 if (game.belt_dir[i] == MV_NO_MOVING)
1175 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
1177 /* check if any connected player was not found in playfield */
1178 for (i=0; i<MAX_PLAYERS; i++)
1180 struct PlayerInfo *player = &stored_player[i];
1182 if (player->connected && !player->present)
1184 for (j=0; j<MAX_PLAYERS; j++)
1186 struct PlayerInfo *some_player = &stored_player[j];
1187 int jx = some_player->jx, jy = some_player->jy;
1189 /* assign first free player found that is present in the playfield */
1190 if (some_player->present && !some_player->connected)
1192 player->present = TRUE;
1193 player->active = TRUE;
1194 some_player->present = FALSE;
1196 StorePlayer[jx][jy] = player->element_nr;
1197 player->jx = player->last_jx = jx;
1198 player->jy = player->last_jy = jy;
1208 /* when playing a tape, eliminate all players who do not participate */
1210 for (i=0; i<MAX_PLAYERS; i++)
1212 if (stored_player[i].active && !tape.player_participates[i])
1214 struct PlayerInfo *player = &stored_player[i];
1215 int jx = player->jx, jy = player->jy;
1217 player->active = FALSE;
1218 StorePlayer[jx][jy] = 0;
1219 Feld[jx][jy] = EL_EMPTY;
1223 else if (!options.network && !setup.team_mode) /* && !tape.playing */
1225 /* when in single player mode, eliminate all but the first active player */
1227 for (i=0; i<MAX_PLAYERS; i++)
1229 if (stored_player[i].active)
1231 for (j=i+1; j<MAX_PLAYERS; j++)
1233 if (stored_player[j].active)
1235 struct PlayerInfo *player = &stored_player[j];
1236 int jx = player->jx, jy = player->jy;
1238 player->active = FALSE;
1239 StorePlayer[jx][jy] = 0;
1240 Feld[jx][jy] = EL_EMPTY;
1247 /* when recording the game, store which players take part in the game */
1250 for (i=0; i<MAX_PLAYERS; i++)
1251 if (stored_player[i].active)
1252 tape.player_participates[i] = TRUE;
1257 for (i=0; i<MAX_PLAYERS; i++)
1259 struct PlayerInfo *player = &stored_player[i];
1261 printf("Player %d: present == %d, connected == %d, active == %d.\n",
1266 if (local_player == player)
1267 printf("Player %d is local player.\n", i+1);
1271 if (BorderElement == EL_EMPTY)
1274 SBX_Right = lev_fieldx - SCR_FIELDX;
1276 SBY_Lower = lev_fieldy - SCR_FIELDY;
1281 SBX_Right = lev_fieldx - SCR_FIELDX + 1;
1283 SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
1286 if (lev_fieldx + (SBX_Left == -1 ? 2 : 0) <= SCR_FIELDX)
1287 SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
1289 if (lev_fieldy + (SBY_Upper == -1 ? 2 : 0) <= SCR_FIELDY)
1290 SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
1292 /* if local player not found, look for custom element that might create
1293 the player (make some assumptions about the right custom element) */
1294 if (!local_player->present)
1296 int start_x = 0, start_y = 0;
1297 int found_rating = 0;
1298 int found_element = EL_UNDEFINED;
1300 for(y=0; y < lev_fieldy; y++) for(x=0; x < lev_fieldx; x++)
1302 int element = Feld[x][y];
1307 if (!IS_CUSTOM_ELEMENT(element))
1310 if (CAN_CHANGE(element))
1312 for (i=0; i < element_info[element].num_change_pages; i++)
1314 content = element_info[element].change_page[i].target_element;
1315 is_player = ELEM_IS_PLAYER(content);
1317 if (is_player && (found_rating < 3 || element < found_element))
1323 found_element = element;
1328 for(yy=0; yy < 3; yy++) for(xx=0; xx < 3; xx++)
1330 content = element_info[element].content[xx][yy];
1331 is_player = ELEM_IS_PLAYER(content);
1333 if (is_player && (found_rating < 2 || element < found_element))
1335 start_x = x + xx - 1;
1336 start_y = y + yy - 1;
1339 found_element = element;
1342 if (!CAN_CHANGE(element))
1345 for (i=0; i < element_info[element].num_change_pages; i++)
1347 content = element_info[element].change_page[i].content[xx][yy];
1348 is_player = ELEM_IS_PLAYER(content);
1350 if (is_player && (found_rating < 1 || element < found_element))
1352 start_x = x + xx - 1;
1353 start_y = y + yy - 1;
1356 found_element = element;
1362 scroll_x = (start_x < SBX_Left + MIDPOSX ? SBX_Left :
1363 start_x > SBX_Right + MIDPOSX ? SBX_Right :
1366 scroll_y = (start_y < SBY_Upper + MIDPOSY ? SBY_Upper :
1367 start_y > SBY_Lower + MIDPOSY ? SBY_Lower :
1373 scroll_x = (local_player->jx < SBX_Left + MIDPOSX ? SBX_Left :
1374 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
1375 local_player->jx - MIDPOSX);
1377 scroll_y = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
1378 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
1379 local_player->jy - MIDPOSY);
1381 scroll_x = SBX_Left;
1382 scroll_y = SBY_Upper;
1383 if (local_player->jx >= SBX_Left + MIDPOSX)
1384 scroll_x = (local_player->jx <= SBX_Right + MIDPOSX ?
1385 local_player->jx - MIDPOSX :
1387 if (local_player->jy >= SBY_Upper + MIDPOSY)
1388 scroll_y = (local_player->jy <= SBY_Lower + MIDPOSY ?
1389 local_player->jy - MIDPOSY :
1394 CloseDoor(DOOR_CLOSE_1);
1399 /* after drawing the level, correct some elements */
1400 if (game.timegate_time_left == 0)
1401 CloseAllOpenTimegates();
1403 if (setup.soft_scrolling)
1404 BlitBitmap(fieldbuffer, backbuffer, FX, FY, SXSIZE, SYSIZE, SX, SY);
1406 redraw_mask |= REDRAW_FROM_BACKBUFFER;
1409 /* copy default game door content to main double buffer */
1410 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
1411 DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
1414 DrawText(DX + XX_LEVEL, DY + YY_LEVEL, int2str(level_nr, 2), FONT_TEXT_2);
1417 DrawTextExt(drawto, DX + XX_EMERALDS, DY + YY_EMERALDS,
1418 int2str(level_nr, 3), FONT_LEVEL_NUMBER, BLIT_OPAQUE);
1419 BlitBitmap(drawto, drawto,
1420 DX + XX_EMERALDS, DY + YY_EMERALDS + 1,
1421 getFontWidth(FONT_LEVEL_NUMBER) * 3,
1422 getFontHeight(FONT_LEVEL_NUMBER) - 1,
1423 DX + XX_LEVEL - 1, DY + YY_LEVEL + 1);
1426 DrawGameDoorValues();
1430 game_gadget[SOUND_CTRL_ID_MUSIC]->checked = setup.sound_music;
1431 game_gadget[SOUND_CTRL_ID_LOOPS]->checked = setup.sound_loops;
1432 game_gadget[SOUND_CTRL_ID_SIMPLE]->checked = setup.sound_simple;
1436 /* copy actual game door content to door double buffer for OpenDoor() */
1437 BlitBitmap(drawto, bitmap_db_door,
1438 DX, DY, DXSIZE, DYSIZE, DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1);
1440 OpenDoor(DOOR_OPEN_ALL);
1442 PlaySoundStereo(SND_GAME_STARTING, SOUND_MIDDLE);
1443 if (setup.sound_music)
1444 PlayMusic(level_nr);
1446 KeyboardAutoRepeatOffUnlessAutoplay();
1451 printf("Player %d %sactive.\n",
1452 i + 1, (stored_player[i].active ? "" : "not "));
1456 void InitMovDir(int x, int y)
1458 int i, element = Feld[x][y];
1459 static int xy[4][2] =
1466 static int direction[3][4] =
1468 { MV_RIGHT, MV_UP, MV_LEFT, MV_DOWN },
1469 { MV_LEFT, MV_DOWN, MV_RIGHT, MV_UP },
1470 { MV_LEFT, MV_RIGHT, MV_UP, MV_DOWN }
1479 Feld[x][y] = EL_BUG;
1480 MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
1483 case EL_SPACESHIP_RIGHT:
1484 case EL_SPACESHIP_UP:
1485 case EL_SPACESHIP_LEFT:
1486 case EL_SPACESHIP_DOWN:
1487 Feld[x][y] = EL_SPACESHIP;
1488 MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
1491 case EL_BD_BUTTERFLY_RIGHT:
1492 case EL_BD_BUTTERFLY_UP:
1493 case EL_BD_BUTTERFLY_LEFT:
1494 case EL_BD_BUTTERFLY_DOWN:
1495 Feld[x][y] = EL_BD_BUTTERFLY;
1496 MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
1499 case EL_BD_FIREFLY_RIGHT:
1500 case EL_BD_FIREFLY_UP:
1501 case EL_BD_FIREFLY_LEFT:
1502 case EL_BD_FIREFLY_DOWN:
1503 Feld[x][y] = EL_BD_FIREFLY;
1504 MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
1507 case EL_PACMAN_RIGHT:
1509 case EL_PACMAN_LEFT:
1510 case EL_PACMAN_DOWN:
1511 Feld[x][y] = EL_PACMAN;
1512 MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
1515 case EL_SP_SNIKSNAK:
1516 MovDir[x][y] = MV_UP;
1519 case EL_SP_ELECTRON:
1520 MovDir[x][y] = MV_LEFT;
1527 Feld[x][y] = EL_MOLE;
1528 MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
1532 if (IS_CUSTOM_ELEMENT(element))
1534 if (element_info[element].move_direction_initial != MV_NO_MOVING)
1535 MovDir[x][y] = element_info[element].move_direction_initial;
1536 else if (element_info[element].move_pattern == MV_ALL_DIRECTIONS ||
1537 element_info[element].move_pattern == MV_TURNING_LEFT ||
1538 element_info[element].move_pattern == MV_TURNING_RIGHT)
1539 MovDir[x][y] = 1 << RND(4);
1540 else if (element_info[element].move_pattern == MV_HORIZONTAL)
1541 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
1542 else if (element_info[element].move_pattern == MV_VERTICAL)
1543 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
1544 else if (element_info[element].move_pattern & MV_ANY_DIRECTION)
1545 MovDir[x][y] = element_info[element].move_pattern;
1546 else if (element_info[element].move_pattern == MV_ALONG_LEFT_SIDE ||
1547 element_info[element].move_pattern == MV_ALONG_RIGHT_SIDE)
1551 int x1 = x + xy[i][0];
1552 int y1 = y + xy[i][1];
1554 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
1556 if (element_info[element].move_pattern == MV_ALONG_RIGHT_SIDE)
1557 MovDir[x][y] = direction[0][i];
1559 MovDir[x][y] = direction[1][i];
1568 MovDir[x][y] = 1 << RND(4);
1570 if (element != EL_BUG &&
1571 element != EL_SPACESHIP &&
1572 element != EL_BD_BUTTERFLY &&
1573 element != EL_BD_FIREFLY)
1578 int x1 = x + xy[i][0];
1579 int y1 = y + xy[i][1];
1581 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
1583 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
1585 MovDir[x][y] = direction[0][i];
1588 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
1589 element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
1591 MovDir[x][y] = direction[1][i];
1601 void InitAmoebaNr(int x, int y)
1604 int group_nr = AmoebeNachbarNr(x, y);
1608 for (i=1; i<MAX_NUM_AMOEBA; i++)
1610 if (AmoebaCnt[i] == 0)
1618 AmoebaNr[x][y] = group_nr;
1619 AmoebaCnt[group_nr]++;
1620 AmoebaCnt2[group_nr]++;
1626 boolean raise_level = FALSE;
1628 if (local_player->MovPos)
1632 if (tape.auto_play) /* tape might already be stopped here */
1633 tape.auto_play_level_solved = TRUE;
1635 if (tape.playing && tape.auto_play)
1636 tape.auto_play_level_solved = TRUE;
1639 local_player->LevelSolved = FALSE;
1641 PlaySoundStereo(SND_GAME_WINNING, SOUND_MIDDLE);
1645 if (!tape.playing && setup.sound_loops)
1646 PlaySoundExt(SND_GAME_LEVELTIME_BONUS, SOUND_MAX_VOLUME, SOUND_MIDDLE,
1647 SND_CTRL_PLAY_LOOP);
1649 while (TimeLeft > 0)
1651 if (!tape.playing && !setup.sound_loops)
1652 PlaySoundStereo(SND_GAME_LEVELTIME_BONUS, SOUND_MIDDLE);
1653 if (TimeLeft > 0 && !(TimeLeft % 10))
1654 RaiseScore(level.score[SC_TIME_BONUS]);
1655 if (TimeLeft > 100 && !(TimeLeft % 10))
1659 DrawText(DX_TIME, DY_TIME, int2str(TimeLeft, 3), FONT_TEXT_2);
1666 if (!tape.playing && setup.sound_loops)
1667 StopSound(SND_GAME_LEVELTIME_BONUS);
1669 else if (level.time == 0) /* level without time limit */
1671 if (!tape.playing && setup.sound_loops)
1672 PlaySoundExt(SND_GAME_LEVELTIME_BONUS, SOUND_MAX_VOLUME, SOUND_MIDDLE,
1673 SND_CTRL_PLAY_LOOP);
1675 while (TimePlayed < 999)
1677 if (!tape.playing && !setup.sound_loops)
1678 PlaySoundStereo(SND_GAME_LEVELTIME_BONUS, SOUND_MIDDLE);
1679 if (TimePlayed < 999 && !(TimePlayed % 10))
1680 RaiseScore(level.score[SC_TIME_BONUS]);
1681 if (TimePlayed < 900 && !(TimePlayed % 10))
1685 DrawText(DX_TIME, DY_TIME, int2str(TimePlayed, 3), FONT_TEXT_2);
1692 if (!tape.playing && setup.sound_loops)
1693 StopSound(SND_GAME_LEVELTIME_BONUS);
1696 /* close exit door after last player */
1697 if ((Feld[ExitX][ExitY] == EL_EXIT_OPEN ||
1698 Feld[ExitX][ExitY] == EL_SP_EXIT_OPEN) && AllPlayersGone)
1700 int element = Feld[ExitX][ExitY];
1702 Feld[ExitX][ExitY] = (element == EL_EXIT_OPEN ? EL_EXIT_CLOSING :
1703 EL_SP_EXIT_CLOSING);
1705 PlaySoundLevelElementAction(ExitX, ExitY, element, ACTION_CLOSING);
1708 /* Hero disappears */
1709 DrawLevelField(ExitX, ExitY);
1715 CloseDoor(DOOR_CLOSE_1);
1720 SaveTape(tape.level_nr); /* Ask to save tape */
1723 if (level_nr == leveldir_current->handicap_level)
1725 leveldir_current->handicap_level++;
1726 SaveLevelSetup_SeriesInfo();
1729 if (level_editor_test_game)
1730 local_player->score = -1; /* no highscore when playing from editor */
1731 else if (level_nr < leveldir_current->last_level)
1732 raise_level = TRUE; /* advance to next level */
1734 if ((hi_pos = NewHiScore()) >= 0)
1736 game_status = GAME_MODE_SCORES;
1737 DrawHallOfFame(hi_pos);
1746 game_status = GAME_MODE_MAIN;
1763 LoadScore(level_nr);
1765 if (strcmp(setup.player_name, EMPTY_PLAYER_NAME) == 0 ||
1766 local_player->score < highscore[MAX_SCORE_ENTRIES - 1].Score)
1769 for (k=0; k<MAX_SCORE_ENTRIES; k++)
1771 if (local_player->score > highscore[k].Score)
1773 /* player has made it to the hall of fame */
1775 if (k < MAX_SCORE_ENTRIES - 1)
1777 int m = MAX_SCORE_ENTRIES - 1;
1780 for (l=k; l<MAX_SCORE_ENTRIES; l++)
1781 if (!strcmp(setup.player_name, highscore[l].Name))
1783 if (m == k) /* player's new highscore overwrites his old one */
1789 strcpy(highscore[l].Name, highscore[l - 1].Name);
1790 highscore[l].Score = highscore[l - 1].Score;
1797 strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
1798 highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
1799 highscore[k].Score = local_player->score;
1805 else if (!strncmp(setup.player_name, highscore[k].Name,
1806 MAX_PLAYER_NAME_LEN))
1807 break; /* player already there with a higher score */
1813 SaveScore(level_nr);
1818 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
1820 if (player->GfxAction != action || player->GfxDir != dir)
1823 printf("Player frame reset! (%d => %d, %d => %d)\n",
1824 player->GfxAction, action, player->GfxDir, dir);
1827 player->GfxAction = action;
1828 player->GfxDir = dir;
1830 player->StepFrame = 0;
1834 static void ResetRandomAnimationValue(int x, int y)
1836 GfxRandom[x][y] = INIT_GFX_RANDOM();
1839 static void ResetGfxAnimation(int x, int y)
1842 GfxAction[x][y] = ACTION_DEFAULT;
1845 void InitMovingField(int x, int y, int direction)
1847 int element = Feld[x][y];
1848 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
1849 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
1853 if (!WasJustMoving[x][y] || direction != MovDir[x][y])
1854 ResetGfxAnimation(x, y);
1856 MovDir[newx][newy] = MovDir[x][y] = direction;
1858 if (Feld[newx][newy] == EL_EMPTY)
1859 Feld[newx][newy] = EL_BLOCKED;
1861 if (direction == MV_DOWN && CAN_FALL(element))
1862 GfxAction[x][y] = ACTION_FALLING;
1864 GfxAction[x][y] = ACTION_MOVING;
1866 GfxFrame[newx][newy] = GfxFrame[x][y];
1867 GfxAction[newx][newy] = GfxAction[x][y];
1868 GfxRandom[newx][newy] = GfxRandom[x][y];
1871 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
1873 int direction = MovDir[x][y];
1874 int newx = x + (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
1875 int newy = y + (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
1881 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
1883 int oldx = x, oldy = y;
1884 int direction = MovDir[x][y];
1886 if (direction == MV_LEFT)
1888 else if (direction == MV_RIGHT)
1890 else if (direction == MV_UP)
1892 else if (direction == MV_DOWN)
1895 *comes_from_x = oldx;
1896 *comes_from_y = oldy;
1899 int MovingOrBlocked2Element(int x, int y)
1901 int element = Feld[x][y];
1903 if (element == EL_BLOCKED)
1907 Blocked2Moving(x, y, &oldx, &oldy);
1908 return Feld[oldx][oldy];
1914 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
1916 /* like MovingOrBlocked2Element(), but if element is moving
1917 and (x,y) is the field the moving element is just leaving,
1918 return EL_BLOCKED instead of the element value */
1919 int element = Feld[x][y];
1921 if (IS_MOVING(x, y))
1923 if (element == EL_BLOCKED)
1927 Blocked2Moving(x, y, &oldx, &oldy);
1928 return Feld[oldx][oldy];
1937 static void RemoveField(int x, int y)
1939 Feld[x][y] = EL_EMPTY;
1946 ChangeDelay[x][y] = 0;
1947 ChangePage[x][y] = -1;
1948 Pushed[x][y] = FALSE;
1950 GfxElement[x][y] = EL_UNDEFINED;
1951 GfxAction[x][y] = ACTION_DEFAULT;
1954 void RemoveMovingField(int x, int y)
1956 int oldx = x, oldy = y, newx = x, newy = y;
1957 int element = Feld[x][y];
1958 int next_element = EL_UNDEFINED;
1960 if (element != EL_BLOCKED && !IS_MOVING(x, y))
1963 if (IS_MOVING(x, y))
1965 Moving2Blocked(x, y, &newx, &newy);
1966 if (Feld[newx][newy] != EL_BLOCKED)
1969 else if (element == EL_BLOCKED)
1971 Blocked2Moving(x, y, &oldx, &oldy);
1972 if (!IS_MOVING(oldx, oldy))
1976 if (element == EL_BLOCKED &&
1977 (Feld[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
1978 Feld[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
1979 Feld[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
1980 Feld[oldx][oldy] == EL_AMOEBA_DROPPING))
1981 next_element = get_next_element(Feld[oldx][oldy]);
1983 RemoveField(oldx, oldy);
1984 RemoveField(newx, newy);
1986 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
1988 if (next_element != EL_UNDEFINED)
1989 Feld[oldx][oldy] = next_element;
1991 DrawLevelField(oldx, oldy);
1992 DrawLevelField(newx, newy);
1995 void DrawDynamite(int x, int y)
1997 int sx = SCREENX(x), sy = SCREENY(y);
1998 int graphic = el2img(Feld[x][y]);
2001 if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
2004 if (IS_WALKABLE_INSIDE(Back[x][y]))
2008 DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
2009 else if (Store[x][y])
2010 DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
2012 frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
2015 if (Back[x][y] || Store[x][y])
2016 DrawGraphicThruMask(sx, sy, graphic, frame);
2018 DrawGraphic(sx, sy, graphic, frame);
2020 if (game.emulation == EMU_SUPAPLEX)
2021 DrawGraphic(sx, sy, IMG_SP_DISK_RED, frame);
2022 else if (Store[x][y])
2023 DrawGraphicThruMask(sx, sy, graphic, frame);
2025 DrawGraphic(sx, sy, graphic, frame);
2029 void CheckDynamite(int x, int y)
2031 if (MovDelay[x][y] != 0) /* dynamite is still waiting to explode */
2035 if (MovDelay[x][y] != 0)
2038 PlaySoundLevelActionIfLoop(x, y, ACTION_ACTIVE);
2045 StopSoundLevelActionIfLoop(x, y, ACTION_ACTIVE);
2047 if (Feld[x][y] == EL_DYNAMITE_ACTIVE ||
2048 Feld[x][y] == EL_SP_DISK_RED_ACTIVE)
2049 StopSound(SND_DYNAMITE_ACTIVE);
2051 StopSound(SND_DYNABOMB_ACTIVE);
2057 void RelocatePlayer(int x, int y, int element)
2059 struct PlayerInfo *player = &stored_player[element - EL_PLAYER_1];
2062 RemoveField(x, y); /* temporarily remove newly placed player */
2063 DrawLevelField(x, y);
2066 if (player->present)
2068 while (player->MovPos)
2070 ScrollPlayer(player, SCROLL_GO_ON);
2071 ScrollScreen(NULL, SCROLL_GO_ON);
2077 Delay(GAME_FRAME_DELAY);
2080 DrawPlayer(player); /* needed here only to cleanup last field */
2081 DrawLevelField(player->jx, player->jy); /* remove player graphic */
2083 player->is_moving = FALSE;
2086 Feld[x][y] = element;
2087 InitPlayerField(x, y, element, TRUE);
2089 if (player == local_player)
2091 int scroll_xx = -999, scroll_yy = -999;
2093 while (scroll_xx != scroll_x || scroll_yy != scroll_y)
2096 int fx = FX, fy = FY;
2098 scroll_xx = (local_player->jx < SBX_Left + MIDPOSX ? SBX_Left :
2099 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
2100 local_player->jx - MIDPOSX);
2102 scroll_yy = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
2103 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
2104 local_player->jy - MIDPOSY);
2106 dx = (scroll_xx < scroll_x ? +1 : scroll_xx > scroll_x ? -1 : 0);
2107 dy = (scroll_yy < scroll_y ? +1 : scroll_yy > scroll_y ? -1 : 0);
2112 fx += dx * TILEX / 2;
2113 fy += dy * TILEY / 2;
2115 ScrollLevel(dx, dy);
2118 /* scroll in two steps of half tile size to make things smoother */
2119 BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
2121 Delay(GAME_FRAME_DELAY);
2123 /* scroll second step to align at full tile size */
2125 Delay(GAME_FRAME_DELAY);
2130 void Explode(int ex, int ey, int phase, int mode)
2134 int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
2135 int last_phase = num_phase * delay;
2136 int half_phase = (num_phase / 2) * delay;
2137 int first_phase_after_start = EX_PHASE_START + 1;
2139 if (game.explosions_delayed)
2141 ExplodeField[ex][ey] = mode;
2145 if (phase == EX_PHASE_START) /* initialize 'Store[][]' field */
2147 int center_element = Feld[ex][ey];
2150 /* --- This is only really needed (and now handled) in "Impact()". --- */
2151 /* do not explode moving elements that left the explode field in time */
2152 if (game.engine_version >= RELEASE_IDENT(2,2,0,7) &&
2153 center_element == EL_EMPTY && (mode == EX_NORMAL || mode == EX_CENTER))
2157 if (mode == EX_NORMAL || mode == EX_CENTER)
2158 PlaySoundLevelAction(ex, ey, ACTION_EXPLODING);
2160 /* remove things displayed in background while burning dynamite */
2161 if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
2164 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
2166 /* put moving element to center field (and let it explode there) */
2167 center_element = MovingOrBlocked2Element(ex, ey);
2168 RemoveMovingField(ex, ey);
2169 Feld[ex][ey] = center_element;
2172 for (y = ey - 1; y <= ey + 1; y++) for(x = ex - 1; x <= ex + 1; x++)
2174 int xx = x - ex + 1;
2175 int yy = y - ey + 1;
2178 if (!IN_LEV_FIELD(x, y) ||
2179 ((mode != EX_NORMAL || center_element == EL_AMOEBA_TO_DIAMOND) &&
2180 (x != ex || y != ey)))
2183 element = Feld[x][y];
2185 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
2187 element = MovingOrBlocked2Element(x, y);
2189 if (!IS_EXPLOSION_PROOF(element))
2190 RemoveMovingField(x, y);
2196 if (IS_EXPLOSION_PROOF(element))
2199 /* indestructible elements can only explode in center (but not flames) */
2200 if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey)) ||
2201 element == EL_FLAMES)
2206 if ((IS_INDESTRUCTIBLE(element) &&
2207 (game.engine_version < VERSION_IDENT(2,2,0) ||
2208 (!IS_WALKABLE_OVER(element) && !IS_WALKABLE_UNDER(element)))) ||
2209 element == EL_FLAMES)
2213 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
2215 if (IS_ACTIVE_BOMB(element))
2217 /* re-activate things under the bomb like gate or penguin */
2218 Feld[x][y] = (Store[x][y] ? Store[x][y] : EL_EMPTY);
2225 /* save walkable background elements while explosion on same tile */
2227 if (IS_INDESTRUCTIBLE(element))
2228 Back[x][y] = element;
2230 if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element))
2231 Back[x][y] = element;
2234 /* ignite explodable elements reached by other explosion */
2235 if (element == EL_EXPLOSION)
2236 element = Store2[x][y];
2239 if (AmoebaNr[x][y] &&
2240 (element == EL_AMOEBA_FULL ||
2241 element == EL_BD_AMOEBA ||
2242 element == EL_AMOEBA_GROWING))
2244 AmoebaCnt[AmoebaNr[x][y]]--;
2245 AmoebaCnt2[AmoebaNr[x][y]]--;
2251 if (IS_PLAYER(ex, ey) && !PLAYER_PROTECTED(ex, ey))
2253 switch(StorePlayer[ex][ey])
2256 Store[x][y] = EL_EMERALD_RED;
2259 Store[x][y] = EL_EMERALD;
2262 Store[x][y] = EL_EMERALD_PURPLE;
2266 Store[x][y] = EL_EMERALD_YELLOW;
2270 if (game.emulation == EMU_SUPAPLEX)
2271 Store[x][y] = EL_EMPTY;
2273 else if (center_element == EL_MOLE)
2274 Store[x][y] = EL_EMERALD_RED;
2275 else if (center_element == EL_PENGUIN)
2276 Store[x][y] = EL_EMERALD_PURPLE;
2277 else if (center_element == EL_BUG)
2278 Store[x][y] = ((x == ex && y == ey) ? EL_DIAMOND : EL_EMERALD);
2279 else if (center_element == EL_BD_BUTTERFLY)
2280 Store[x][y] = EL_BD_DIAMOND;
2281 else if (center_element == EL_SP_ELECTRON)
2282 Store[x][y] = EL_SP_INFOTRON;
2283 else if (center_element == EL_AMOEBA_TO_DIAMOND)
2284 Store[x][y] = level.amoeba_content;
2285 else if (center_element == EL_YAMYAM)
2286 Store[x][y] = level.yamyam_content[game.yamyam_content_nr][xx][yy];
2287 else if (IS_CUSTOM_ELEMENT(center_element) &&
2288 element_info[center_element].content[xx][yy] != EL_EMPTY)
2289 Store[x][y] = element_info[center_element].content[xx][yy];
2290 else if (element == EL_WALL_EMERALD)
2291 Store[x][y] = EL_EMERALD;
2292 else if (element == EL_WALL_DIAMOND)
2293 Store[x][y] = EL_DIAMOND;
2294 else if (element == EL_WALL_BD_DIAMOND)
2295 Store[x][y] = EL_BD_DIAMOND;
2296 else if (element == EL_WALL_EMERALD_YELLOW)
2297 Store[x][y] = EL_EMERALD_YELLOW;
2298 else if (element == EL_WALL_EMERALD_RED)
2299 Store[x][y] = EL_EMERALD_RED;
2300 else if (element == EL_WALL_EMERALD_PURPLE)
2301 Store[x][y] = EL_EMERALD_PURPLE;
2302 else if (element == EL_WALL_PEARL)
2303 Store[x][y] = EL_PEARL;
2304 else if (element == EL_WALL_CRYSTAL)
2305 Store[x][y] = EL_CRYSTAL;
2306 else if (IS_CUSTOM_ELEMENT(element) && !CAN_EXPLODE(element))
2307 Store[x][y] = element_info[element].content[1][1];
2309 Store[x][y] = EL_EMPTY;
2311 if (x != ex || y != ey ||
2312 center_element == EL_AMOEBA_TO_DIAMOND || mode == EX_BORDER)
2313 Store2[x][y] = element;
2316 if (AmoebaNr[x][y] &&
2317 (element == EL_AMOEBA_FULL ||
2318 element == EL_BD_AMOEBA ||
2319 element == EL_AMOEBA_GROWING))
2321 AmoebaCnt[AmoebaNr[x][y]]--;
2322 AmoebaCnt2[AmoebaNr[x][y]]--;
2328 MovDir[x][y] = MovPos[x][y] = 0;
2333 Feld[x][y] = EL_EXPLOSION;
2335 GfxElement[x][y] = center_element;
2337 GfxElement[x][y] = EL_UNDEFINED;
2340 ExplodePhase[x][y] = 1;
2344 if (center_element == EL_YAMYAM)
2345 game.yamyam_content_nr =
2346 (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
2357 ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
2361 /* activate this even in non-DEBUG version until cause for crash in
2362 getGraphicAnimationFrame() (see below) is found and eliminated */
2366 if (GfxElement[x][y] == EL_UNDEFINED)
2369 printf("Explode(): x = %d, y = %d: GfxElement == EL_UNDEFINED\n", x, y);
2370 printf("Explode(): This should never happen!\n");
2373 GfxElement[x][y] = EL_EMPTY;
2377 if (phase == first_phase_after_start)
2379 int element = Store2[x][y];
2381 if (element == EL_BLACK_ORB)
2383 Feld[x][y] = Store2[x][y];
2388 else if (phase == half_phase)
2390 int element = Store2[x][y];
2392 if (IS_PLAYER(x, y))
2393 KillHeroUnlessProtected(x, y);
2394 else if (CAN_EXPLODE_BY_FIRE(element))
2396 Feld[x][y] = Store2[x][y];
2400 else if (element == EL_AMOEBA_TO_DIAMOND)
2401 AmoebeUmwandeln(x, y);
2404 if (phase == last_phase)
2408 element = Feld[x][y] = Store[x][y];
2409 Store[x][y] = Store2[x][y] = 0;
2410 GfxElement[x][y] = EL_UNDEFINED;
2412 if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
2413 element = Feld[x][y] = Back[x][y];
2416 MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
2417 ChangeDelay[x][y] = 0;
2418 ChangePage[x][y] = -1;
2420 InitField(x, y, FALSE);
2421 if (CAN_MOVE(element))
2423 DrawLevelField(x, y);
2425 TestIfElementTouchesCustomElement(x, y);
2427 if (GFX_CRUMBLED(element))
2428 DrawLevelFieldCrumbledSandNeighbours(x, y);
2430 if (IS_PLAYER(x, y) && !PLAYERINFO(x,y)->present)
2431 StorePlayer[x][y] = 0;
2433 if (ELEM_IS_PLAYER(element))
2434 RelocatePlayer(x, y, element);
2436 else if (phase >= delay && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
2439 int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
2441 int stored = Store[x][y];
2442 int graphic = (game.emulation != EMU_SUPAPLEX ? IMG_EXPLOSION :
2443 stored == EL_SP_INFOTRON ? IMG_SP_EXPLOSION_INFOTRON :
2446 int frame = getGraphicAnimationFrame(graphic, phase - delay);
2449 DrawLevelFieldCrumbledSand(x, y);
2451 if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
2453 DrawLevelElement(x, y, Back[x][y]);
2454 DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
2456 else if (IS_WALKABLE_UNDER(Back[x][y]))
2458 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
2459 DrawLevelElementThruMask(x, y, Back[x][y]);
2461 else if (!IS_WALKABLE_INSIDE(Back[x][y]))
2462 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
2466 void DynaExplode(int ex, int ey)
2469 int dynabomb_size = 1;
2470 boolean dynabomb_xl = FALSE;
2471 struct PlayerInfo *player;
2472 static int xy[4][2] =
2480 if (IS_ACTIVE_BOMB(Feld[ex][ey]))
2482 player = &stored_player[Feld[ex][ey] - EL_DYNABOMB_PLAYER_1_ACTIVE];
2483 dynabomb_size = player->dynabomb_size;
2484 dynabomb_xl = player->dynabomb_xl;
2485 player->dynabombs_left++;
2488 Explode(ex, ey, EX_PHASE_START, EX_CENTER);
2492 for (j=1; j<=dynabomb_size; j++)
2494 int x = ex + j * xy[i % 4][0];
2495 int y = ey + j * xy[i % 4][1];
2498 if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
2501 element = Feld[x][y];
2503 /* do not restart explosions of fields with active bombs */
2504 if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
2507 Explode(x, y, EX_PHASE_START, EX_BORDER);
2509 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
2510 if (element != EL_EMPTY &&
2511 element != EL_SAND &&
2512 element != EL_EXPLOSION &&
2519 void Bang(int x, int y)
2522 int element = MovingOrBlocked2Element(x, y);
2524 int element = Feld[x][y];
2528 if (IS_PLAYER(x, y) && !PLAYER_PROTECTED(x, y))
2530 if (IS_PLAYER(x, y))
2533 struct PlayerInfo *player = PLAYERINFO(x, y);
2535 element = Feld[x][y] = (player->use_murphy_graphic ? EL_SP_MURPHY :
2536 player->element_nr);
2541 PlaySoundLevelAction(x, y, ACTION_EXPLODING);
2543 if (game.emulation == EMU_SUPAPLEX)
2544 PlaySoundLevel(x, y, SND_SP_ELEMENT_EXPLODING);
2546 PlaySoundLevel(x, y, SND_ELEMENT_EXPLODING);
2551 if (IS_PLAYER(x, y)) /* remove objects that might cause smaller explosion */
2559 case EL_BD_BUTTERFLY:
2562 case EL_DARK_YAMYAM:
2566 RaiseScoreElement(element);
2567 Explode(x, y, EX_PHASE_START, EX_NORMAL);
2569 case EL_DYNABOMB_PLAYER_1_ACTIVE:
2570 case EL_DYNABOMB_PLAYER_2_ACTIVE:
2571 case EL_DYNABOMB_PLAYER_3_ACTIVE:
2572 case EL_DYNABOMB_PLAYER_4_ACTIVE:
2573 case EL_DYNABOMB_INCREASE_NUMBER:
2574 case EL_DYNABOMB_INCREASE_SIZE:
2575 case EL_DYNABOMB_INCREASE_POWER:
2580 case EL_LAMP_ACTIVE:
2581 if (IS_PLAYER(x, y))
2582 Explode(x, y, EX_PHASE_START, EX_NORMAL);
2584 Explode(x, y, EX_PHASE_START, EX_CENTER);
2587 if (CAN_EXPLODE_1X1(element))
2588 Explode(x, y, EX_PHASE_START, EX_CENTER);
2590 Explode(x, y, EX_PHASE_START, EX_NORMAL);
2594 CheckTriggeredElementChange(x, y, element, CE_OTHER_IS_EXPLODING);
2597 void SplashAcid(int x, int y)
2599 int element = Feld[x][y];
2601 if (element != EL_ACID_SPLASH_LEFT &&
2602 element != EL_ACID_SPLASH_RIGHT)
2604 PlaySoundLevel(x, y, SND_ACID_SPLASHING);
2606 if (IN_LEV_FIELD(x-1, y) && IS_FREE(x-1, y) &&
2607 (!IN_LEV_FIELD(x-1, y-1) ||
2608 !CAN_FALL(MovingOrBlocked2Element(x-1, y-1))))
2609 Feld[x-1][y] = EL_ACID_SPLASH_LEFT;
2611 if (IN_LEV_FIELD(x+1, y) && IS_FREE(x+1, y) &&
2612 (!IN_LEV_FIELD(x+1, y-1) ||
2613 !CAN_FALL(MovingOrBlocked2Element(x+1, y-1))))
2614 Feld[x+1][y] = EL_ACID_SPLASH_RIGHT;
2618 static void InitBeltMovement()
2620 static int belt_base_element[4] =
2622 EL_CONVEYOR_BELT_1_LEFT,
2623 EL_CONVEYOR_BELT_2_LEFT,
2624 EL_CONVEYOR_BELT_3_LEFT,
2625 EL_CONVEYOR_BELT_4_LEFT
2627 static int belt_base_active_element[4] =
2629 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
2630 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
2631 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
2632 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
2637 /* set frame order for belt animation graphic according to belt direction */
2644 int element = belt_base_active_element[belt_nr] + j;
2645 int graphic = el2img(element);
2647 if (game.belt_dir[i] == MV_LEFT)
2648 graphic_info[graphic].anim_mode &= ~ANIM_REVERSE;
2650 graphic_info[graphic].anim_mode |= ANIM_REVERSE;
2654 for(y=0; y<lev_fieldy; y++)
2656 for(x=0; x<lev_fieldx; x++)
2658 int element = Feld[x][y];
2662 if (IS_BELT(element) && game.belt_dir[i] != MV_NO_MOVING)
2664 int e_belt_nr = getBeltNrFromBeltElement(element);
2667 if (e_belt_nr == belt_nr)
2669 int belt_part = Feld[x][y] - belt_base_element[belt_nr];
2671 Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
2679 static void ToggleBeltSwitch(int x, int y)
2681 static int belt_base_element[4] =
2683 EL_CONVEYOR_BELT_1_LEFT,
2684 EL_CONVEYOR_BELT_2_LEFT,
2685 EL_CONVEYOR_BELT_3_LEFT,
2686 EL_CONVEYOR_BELT_4_LEFT
2688 static int belt_base_active_element[4] =
2690 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
2691 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
2692 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
2693 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
2695 static int belt_base_switch_element[4] =
2697 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
2698 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
2699 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
2700 EL_CONVEYOR_BELT_4_SWITCH_LEFT
2702 static int belt_move_dir[4] =
2710 int element = Feld[x][y];
2711 int belt_nr = getBeltNrFromBeltSwitchElement(element);
2712 int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
2713 int belt_dir = belt_move_dir[belt_dir_nr];
2716 if (!IS_BELT_SWITCH(element))
2719 game.belt_dir_nr[belt_nr] = belt_dir_nr;
2720 game.belt_dir[belt_nr] = belt_dir;
2722 if (belt_dir_nr == 3)
2725 /* set frame order for belt animation graphic according to belt direction */
2728 int element = belt_base_active_element[belt_nr] + i;
2729 int graphic = el2img(element);
2731 if (belt_dir == MV_LEFT)
2732 graphic_info[graphic].anim_mode &= ~ANIM_REVERSE;
2734 graphic_info[graphic].anim_mode |= ANIM_REVERSE;
2737 for (yy=0; yy<lev_fieldy; yy++)
2739 for (xx=0; xx<lev_fieldx; xx++)
2741 int element = Feld[xx][yy];
2743 if (IS_BELT_SWITCH(element))
2745 int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
2747 if (e_belt_nr == belt_nr)
2749 Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
2750 DrawLevelField(xx, yy);
2753 else if (IS_BELT(element) && belt_dir != MV_NO_MOVING)
2755 int e_belt_nr = getBeltNrFromBeltElement(element);
2757 if (e_belt_nr == belt_nr)
2759 int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
2761 Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
2762 DrawLevelField(xx, yy);
2765 else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NO_MOVING)
2767 int e_belt_nr = getBeltNrFromBeltActiveElement(element);
2769 if (e_belt_nr == belt_nr)
2771 int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
2773 Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
2774 DrawLevelField(xx, yy);
2781 static void ToggleSwitchgateSwitch(int x, int y)
2785 game.switchgate_pos = !game.switchgate_pos;
2787 for (yy=0; yy<lev_fieldy; yy++)
2789 for (xx=0; xx<lev_fieldx; xx++)
2791 int element = Feld[xx][yy];
2793 if (element == EL_SWITCHGATE_SWITCH_UP ||
2794 element == EL_SWITCHGATE_SWITCH_DOWN)
2796 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
2797 DrawLevelField(xx, yy);
2799 else if (element == EL_SWITCHGATE_OPEN ||
2800 element == EL_SWITCHGATE_OPENING)
2802 Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
2804 PlaySoundLevelAction(xx, yy, ACTION_CLOSING);
2806 PlaySoundLevel(xx, yy, SND_SWITCHGATE_CLOSING);
2809 else if (element == EL_SWITCHGATE_CLOSED ||
2810 element == EL_SWITCHGATE_CLOSING)
2812 Feld[xx][yy] = EL_SWITCHGATE_OPENING;
2814 PlaySoundLevelAction(xx, yy, ACTION_OPENING);
2816 PlaySoundLevel(xx, yy, SND_SWITCHGATE_OPENING);
2823 static int getInvisibleActiveFromInvisibleElement(int element)
2825 return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
2826 element == EL_INVISIBLE_WALL ? EL_INVISIBLE_WALL_ACTIVE :
2827 element == EL_INVISIBLE_SAND ? EL_INVISIBLE_SAND_ACTIVE :
2831 static int getInvisibleFromInvisibleActiveElement(int element)
2833 return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
2834 element == EL_INVISIBLE_WALL_ACTIVE ? EL_INVISIBLE_WALL :
2835 element == EL_INVISIBLE_SAND_ACTIVE ? EL_INVISIBLE_SAND :
2839 static void RedrawAllLightSwitchesAndInvisibleElements()
2843 for (y=0; y<lev_fieldy; y++)
2845 for (x=0; x<lev_fieldx; x++)
2847 int element = Feld[x][y];
2849 if (element == EL_LIGHT_SWITCH &&
2850 game.light_time_left > 0)
2852 Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
2853 DrawLevelField(x, y);
2855 else if (element == EL_LIGHT_SWITCH_ACTIVE &&
2856 game.light_time_left == 0)
2858 Feld[x][y] = EL_LIGHT_SWITCH;
2859 DrawLevelField(x, y);
2861 else if (element == EL_INVISIBLE_STEELWALL ||
2862 element == EL_INVISIBLE_WALL ||
2863 element == EL_INVISIBLE_SAND)
2865 if (game.light_time_left > 0)
2866 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
2868 DrawLevelField(x, y);
2870 else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
2871 element == EL_INVISIBLE_WALL_ACTIVE ||
2872 element == EL_INVISIBLE_SAND_ACTIVE)
2874 if (game.light_time_left == 0)
2875 Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
2877 DrawLevelField(x, y);
2883 static void ToggleLightSwitch(int x, int y)
2885 int element = Feld[x][y];
2887 game.light_time_left =
2888 (element == EL_LIGHT_SWITCH ?
2889 level.time_light * FRAMES_PER_SECOND : 0);
2891 RedrawAllLightSwitchesAndInvisibleElements();
2894 static void ActivateTimegateSwitch(int x, int y)
2898 game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
2900 for (yy=0; yy<lev_fieldy; yy++)
2902 for (xx=0; xx<lev_fieldx; xx++)
2904 int element = Feld[xx][yy];
2906 if (element == EL_TIMEGATE_CLOSED ||
2907 element == EL_TIMEGATE_CLOSING)
2909 Feld[xx][yy] = EL_TIMEGATE_OPENING;
2910 PlaySoundLevel(xx, yy, SND_TIMEGATE_OPENING);
2914 else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
2916 Feld[xx][yy] = EL_TIMEGATE_SWITCH;
2917 DrawLevelField(xx, yy);
2924 Feld[x][y] = EL_TIMEGATE_SWITCH_ACTIVE;
2927 inline static int getElementMoveStepsize(int x, int y)
2929 int element = Feld[x][y];
2930 int direction = MovDir[x][y];
2931 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
2932 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
2933 int horiz_move = (dx != 0);
2934 int sign = (horiz_move ? dx : dy);
2935 int step = sign * element_info[element].move_stepsize;
2937 /* special values for move stepsize for spring and things on conveyor belt */
2940 if (CAN_FALL(element) &&
2941 y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
2942 step = sign * MOVE_STEPSIZE_NORMAL / 2;
2943 else if (element == EL_SPRING)
2944 step = sign * MOVE_STEPSIZE_NORMAL * 2;
2950 void Impact(int x, int y)
2952 boolean lastline = (y == lev_fieldy-1);
2953 boolean object_hit = FALSE;
2954 boolean impact = (lastline || object_hit);
2955 int element = Feld[x][y];
2956 int smashed = EL_UNDEFINED;
2958 if (!lastline) /* check if element below was hit */
2960 if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
2963 object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
2964 MovDir[x][y + 1] != MV_DOWN ||
2965 MovPos[x][y + 1] <= TILEY / 2));
2967 /* do not smash moving elements that left the smashed field in time */
2968 if (game.engine_version >= RELEASE_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
2969 ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
2973 smashed = MovingOrBlocked2Element(x, y + 1);
2975 impact = (lastline || object_hit);
2978 if (!lastline && smashed == EL_ACID) /* element falls into acid */
2984 /* only reset graphic animation if graphic really changes after impact */
2986 el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
2988 ResetGfxAnimation(x, y);
2989 DrawLevelField(x, y);
2992 if (impact && CAN_EXPLODE_IMPACT(element))
2997 else if (impact && element == EL_PEARL)
2999 Feld[x][y] = EL_PEARL_BREAKING;
3000 PlaySoundLevel(x, y, SND_PEARL_BREAKING);
3003 else if (impact && CheckElementChange(x, y, element, CE_IMPACT))
3005 PlaySoundLevelElementAction(x, y, element, ACTION_IMPACT);
3010 if (impact && element == EL_AMOEBA_DROP)
3012 if (object_hit && IS_PLAYER(x, y + 1))
3013 KillHeroUnlessProtected(x, y + 1);
3014 else if (object_hit && smashed == EL_PENGUIN)
3018 Feld[x][y] = EL_AMOEBA_GROWING;
3019 Store[x][y] = EL_AMOEBA_WET;
3021 ResetRandomAnimationValue(x, y);
3026 if (object_hit) /* check which object was hit */
3028 if (CAN_PASS_MAGIC_WALL(element) &&
3029 (smashed == EL_MAGIC_WALL ||
3030 smashed == EL_BD_MAGIC_WALL))
3033 int activated_magic_wall =
3034 (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
3035 EL_BD_MAGIC_WALL_ACTIVE);
3037 /* activate magic wall / mill */
3038 for (yy=0; yy<lev_fieldy; yy++)
3039 for (xx=0; xx<lev_fieldx; xx++)
3040 if (Feld[xx][yy] == smashed)
3041 Feld[xx][yy] = activated_magic_wall;
3043 game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
3044 game.magic_wall_active = TRUE;
3046 PlaySoundLevel(x, y, (smashed == EL_MAGIC_WALL ?
3047 SND_MAGIC_WALL_ACTIVATING :
3048 SND_BD_MAGIC_WALL_ACTIVATING));
3051 if (IS_PLAYER(x, y + 1))
3053 if (CAN_SMASH_PLAYER(element))
3055 KillHeroUnlessProtected(x, y + 1);
3059 else if (smashed == EL_PENGUIN)
3061 if (CAN_SMASH_PLAYER(element))
3067 else if (element == EL_BD_DIAMOND)
3069 if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
3075 else if ((element == EL_SP_INFOTRON ||
3076 element == EL_SP_ZONK) &&
3077 (smashed == EL_SP_SNIKSNAK ||
3078 smashed == EL_SP_ELECTRON ||
3079 smashed == EL_SP_DISK_ORANGE))
3085 else if (CAN_SMASH_ENEMIES(element) && IS_CLASSIC_ENEMY(smashed))
3091 else if (CAN_SMASH_EVERYTHING(element))
3093 if (IS_CLASSIC_ENEMY(smashed) ||
3094 CAN_EXPLODE_SMASHED(smashed))
3099 else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
3101 if (smashed == EL_LAMP ||
3102 smashed == EL_LAMP_ACTIVE)
3107 else if (smashed == EL_NUT)
3109 Feld[x][y + 1] = EL_NUT_BREAKING;
3110 PlaySoundLevel(x, y, SND_NUT_BREAKING);
3111 RaiseScoreElement(EL_NUT);
3114 else if (smashed == EL_PEARL)
3116 Feld[x][y + 1] = EL_PEARL_BREAKING;
3117 PlaySoundLevel(x, y, SND_PEARL_BREAKING);
3120 else if (smashed == EL_DIAMOND)
3122 Feld[x][y + 1] = EL_DIAMOND_BREAKING;
3123 PlaySoundLevel(x, y, SND_DIAMOND_BREAKING);
3126 else if (IS_BELT_SWITCH(smashed))
3128 ToggleBeltSwitch(x, y + 1);
3130 else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
3131 smashed == EL_SWITCHGATE_SWITCH_DOWN)
3133 ToggleSwitchgateSwitch(x, y + 1);
3135 else if (smashed == EL_LIGHT_SWITCH ||
3136 smashed == EL_LIGHT_SWITCH_ACTIVE)
3138 ToggleLightSwitch(x, y + 1);
3142 CheckElementChange(x, y + 1, smashed, CE_SMASHED);
3144 CheckTriggeredElementSideChange(x, y + 1, smashed, CH_SIDE_TOP,
3145 CE_OTHER_IS_SWITCHING);
3146 CheckElementSideChange(x, y + 1, smashed, CH_SIDE_TOP,
3152 CheckElementChange(x, y + 1, smashed, CE_SMASHED);
3157 /* play sound of magic wall / mill */
3159 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
3160 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE))
3162 if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
3163 PlaySoundLevel(x, y, SND_MAGIC_WALL_FILLING);
3164 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
3165 PlaySoundLevel(x, y, SND_BD_MAGIC_WALL_FILLING);
3170 /* play sound of object that hits the ground */
3171 if (lastline || object_hit)
3172 PlaySoundLevelElementAction(x, y, element, ACTION_IMPACT);
3175 void TurnRound(int x, int y)
3187 { 0, 0 }, { 0, 0 }, { 0, 0 },
3192 int left, right, back;
3196 { MV_DOWN, MV_UP, MV_RIGHT },
3197 { MV_UP, MV_DOWN, MV_LEFT },
3199 { MV_LEFT, MV_RIGHT, MV_DOWN },
3203 { MV_RIGHT, MV_LEFT, MV_UP }
3206 int element = Feld[x][y];
3207 int old_move_dir = MovDir[x][y];
3208 int left_dir = turn[old_move_dir].left;
3209 int right_dir = turn[old_move_dir].right;
3210 int back_dir = turn[old_move_dir].back;
3212 int left_dx = move_xy[left_dir].x, left_dy = move_xy[left_dir].y;
3213 int right_dx = move_xy[right_dir].x, right_dy = move_xy[right_dir].y;
3214 int move_dx = move_xy[old_move_dir].x, move_dy = move_xy[old_move_dir].y;
3215 int back_dx = move_xy[back_dir].x, back_dy = move_xy[back_dir].y;
3217 int left_x = x + left_dx, left_y = y + left_dy;
3218 int right_x = x + right_dx, right_y = y + right_dy;
3219 int move_x = x + move_dx, move_y = y + move_dy;
3223 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
3225 TestIfBadThingTouchesOtherBadThing(x, y);
3227 if (ENEMY_CAN_ENTER_FIELD(right_x, right_y))
3228 MovDir[x][y] = right_dir;
3229 else if (!ENEMY_CAN_ENTER_FIELD(move_x, move_y))
3230 MovDir[x][y] = left_dir;
3232 if (element == EL_BUG && MovDir[x][y] != old_move_dir)
3234 else if (element == EL_BD_BUTTERFLY) /* && MovDir[x][y] == left_dir) */
3237 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
3238 element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
3240 TestIfBadThingTouchesOtherBadThing(x, y);
3242 if (ENEMY_CAN_ENTER_FIELD(left_x, left_y))
3243 MovDir[x][y] = left_dir;
3244 else if (!ENEMY_CAN_ENTER_FIELD(move_x, move_y))
3245 MovDir[x][y] = right_dir;
3247 if ((element == EL_SPACESHIP ||
3248 element == EL_SP_SNIKSNAK ||
3249 element == EL_SP_ELECTRON)
3250 && MovDir[x][y] != old_move_dir)
3252 else if (element == EL_BD_FIREFLY) /* && MovDir[x][y] == right_dir) */
3255 else if (element == EL_YAMYAM)
3257 boolean can_turn_left = YAMYAM_CAN_ENTER_FIELD(left_x, left_y);
3258 boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(right_x, right_y);
3260 if (can_turn_left && can_turn_right)
3261 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
3262 else if (can_turn_left)
3263 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
3264 else if (can_turn_right)
3265 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
3267 MovDir[x][y] = back_dir;
3269 MovDelay[x][y] = 16 + 16 * RND(3);
3271 else if (element == EL_DARK_YAMYAM)
3273 boolean can_turn_left = DARK_YAMYAM_CAN_ENTER_FIELD(left_x, left_y);
3274 boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(right_x, right_y);
3276 if (can_turn_left && can_turn_right)
3277 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
3278 else if (can_turn_left)
3279 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
3280 else if (can_turn_right)
3281 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
3283 MovDir[x][y] = back_dir;
3285 MovDelay[x][y] = 16 + 16 * RND(3);
3287 else if (element == EL_PACMAN)
3289 boolean can_turn_left = PACMAN_CAN_ENTER_FIELD(left_x, left_y);
3290 boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(right_x, right_y);
3292 if (can_turn_left && can_turn_right)
3293 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
3294 else if (can_turn_left)
3295 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
3296 else if (can_turn_right)
3297 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
3299 MovDir[x][y] = back_dir;
3301 MovDelay[x][y] = 6 + RND(40);
3303 else if (element == EL_PIG)
3305 boolean can_turn_left = PIG_CAN_ENTER_FIELD(left_x, left_y);
3306 boolean can_turn_right = PIG_CAN_ENTER_FIELD(right_x, right_y);
3307 boolean can_move_on = PIG_CAN_ENTER_FIELD(move_x, move_y);
3308 boolean should_turn_left, should_turn_right, should_move_on;
3310 int rnd = RND(rnd_value);
3312 should_turn_left = (can_turn_left &&
3314 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
3315 y + back_dy + left_dy)));
3316 should_turn_right = (can_turn_right &&
3318 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
3319 y + back_dy + right_dy)));
3320 should_move_on = (can_move_on &&
3323 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
3324 y + move_dy + left_dy) ||
3325 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
3326 y + move_dy + right_dy)));
3328 if (should_turn_left || should_turn_right || should_move_on)
3330 if (should_turn_left && should_turn_right && should_move_on)
3331 MovDir[x][y] = (rnd < rnd_value / 3 ? left_dir :
3332 rnd < 2 * rnd_value / 3 ? right_dir :
3334 else if (should_turn_left && should_turn_right)
3335 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
3336 else if (should_turn_left && should_move_on)
3337 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
3338 else if (should_turn_right && should_move_on)
3339 MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
3340 else if (should_turn_left)
3341 MovDir[x][y] = left_dir;
3342 else if (should_turn_right)
3343 MovDir[x][y] = right_dir;
3344 else if (should_move_on)
3345 MovDir[x][y] = old_move_dir;
3347 else if (can_move_on && rnd > rnd_value / 8)
3348 MovDir[x][y] = old_move_dir;
3349 else if (can_turn_left && can_turn_right)
3350 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
3351 else if (can_turn_left && rnd > rnd_value / 8)
3352 MovDir[x][y] = left_dir;
3353 else if (can_turn_right && rnd > rnd_value/8)
3354 MovDir[x][y] = right_dir;
3356 MovDir[x][y] = back_dir;
3358 xx = x + move_xy[MovDir[x][y]].x;
3359 yy = y + move_xy[MovDir[x][y]].y;
3361 if (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy]))
3362 MovDir[x][y] = old_move_dir;
3366 else if (element == EL_DRAGON)
3368 boolean can_turn_left = IN_LEV_FIELD_AND_IS_FREE(left_x, left_y);
3369 boolean can_turn_right = IN_LEV_FIELD_AND_IS_FREE(right_x, right_y);
3370 boolean can_move_on = IN_LEV_FIELD_AND_IS_FREE(move_x, move_y);
3372 int rnd = RND(rnd_value);
3374 if (can_move_on && rnd > rnd_value / 8)
3375 MovDir[x][y] = old_move_dir;
3376 else if (can_turn_left && can_turn_right)
3377 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
3378 else if (can_turn_left && rnd > rnd_value / 8)
3379 MovDir[x][y] = left_dir;
3380 else if (can_turn_right && rnd > rnd_value / 8)
3381 MovDir[x][y] = right_dir;
3383 MovDir[x][y] = back_dir;
3385 xx = x + move_xy[MovDir[x][y]].x;
3386 yy = y + move_xy[MovDir[x][y]].y;
3388 if (!IS_FREE(xx, yy))
3389 MovDir[x][y] = old_move_dir;
3393 else if (element == EL_MOLE)
3395 boolean can_move_on =
3396 (MOLE_CAN_ENTER_FIELD(move_x, move_y,
3397 IS_AMOEBOID(Feld[move_x][move_y]) ||
3398 Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
3401 boolean can_turn_left =
3402 (MOLE_CAN_ENTER_FIELD(left_x, left_y,
3403 IS_AMOEBOID(Feld[left_x][left_y])));
3405 boolean can_turn_right =
3406 (MOLE_CAN_ENTER_FIELD(right_x, right_y,
3407 IS_AMOEBOID(Feld[right_x][right_y])));
3409 if (can_turn_left && can_turn_right)
3410 MovDir[x][y] = (RND(2) ? left_dir : right_dir);
3411 else if (can_turn_left)
3412 MovDir[x][y] = left_dir;
3414 MovDir[x][y] = right_dir;
3417 if (MovDir[x][y] != old_move_dir)
3420 else if (element == EL_BALLOON)
3422 MovDir[x][y] = game.balloon_dir;
3425 else if (element == EL_SPRING)
3427 if (MovDir[x][y] & MV_HORIZONTAL &&
3428 (!IN_LEV_FIELD_AND_IS_FREE(move_x, move_y) ||
3429 IN_LEV_FIELD_AND_IS_FREE(x, y + 1)))
3430 MovDir[x][y] = MV_NO_MOVING;
3434 else if (element == EL_ROBOT ||
3435 element == EL_SATELLITE ||
3436 element == EL_PENGUIN)
3438 int attr_x = -1, attr_y = -1;
3449 for (i=0; i<MAX_PLAYERS; i++)
3451 struct PlayerInfo *player = &stored_player[i];
3452 int jx = player->jx, jy = player->jy;
3454 if (!player->active)
3458 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
3466 if (element == EL_ROBOT && ZX >= 0 && ZY >= 0)
3472 if (element == EL_PENGUIN)
3475 static int xy[4][2] =
3485 int ex = x + xy[i % 4][0];
3486 int ey = y + xy[i % 4][1];
3488 if (IN_LEV_FIELD(ex, ey) && Feld[ex][ey] == EL_EXIT_OPEN)
3497 MovDir[x][y] = MV_NO_MOVING;
3499 MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
3500 else if (attr_x > x)
3501 MovDir[x][y] |= (AllPlayersGone ? MV_LEFT : MV_RIGHT);
3503 MovDir[x][y] |= (AllPlayersGone ? MV_DOWN : MV_UP);
3504 else if (attr_y > y)
3505 MovDir[x][y] |= (AllPlayersGone ? MV_UP : MV_DOWN);
3507 if (element == EL_ROBOT)
3511 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
3512 MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
3513 Moving2Blocked(x, y, &newx, &newy);
3515 if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
3516 MovDelay[x][y] = 8 + 8 * !RND(3);
3518 MovDelay[x][y] = 16;
3520 else if (element == EL_PENGUIN)
3526 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
3528 boolean first_horiz = RND(2);
3529 int new_move_dir = MovDir[x][y];
3532 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
3533 Moving2Blocked(x, y, &newx, &newy);
3535 if (PENGUIN_CAN_ENTER_FIELD(newx, newy))
3539 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
3540 Moving2Blocked(x, y, &newx, &newy);
3542 if (PENGUIN_CAN_ENTER_FIELD(newx, newy))
3545 MovDir[x][y] = old_move_dir;
3549 else /* (element == EL_SATELLITE) */
3555 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
3557 boolean first_horiz = RND(2);
3558 int new_move_dir = MovDir[x][y];
3561 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
3562 Moving2Blocked(x, y, &newx, &newy);
3564 if (ELEMENT_CAN_ENTER_FIELD_OR_ACID_2(newx, newy))
3568 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
3569 Moving2Blocked(x, y, &newx, &newy);
3571 if (ELEMENT_CAN_ENTER_FIELD_OR_ACID_2(newx, newy))
3574 MovDir[x][y] = old_move_dir;
3579 else if (element_info[element].move_pattern == MV_ALL_DIRECTIONS ||
3580 element_info[element].move_pattern == MV_TURNING_LEFT ||
3581 element_info[element].move_pattern == MV_TURNING_RIGHT)
3583 boolean can_turn_left = ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
3584 boolean can_turn_right = ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
3586 if (element_info[element].move_pattern == MV_TURNING_LEFT)
3587 MovDir[x][y] = left_dir;
3588 else if (element_info[element].move_pattern == MV_TURNING_RIGHT)
3589 MovDir[x][y] = right_dir;
3590 else if (can_turn_left && can_turn_right)
3591 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
3592 else if (can_turn_left)
3593 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
3594 else if (can_turn_right)
3595 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
3597 MovDir[x][y] = back_dir;
3599 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
3601 else if (element_info[element].move_pattern == MV_HORIZONTAL ||
3602 element_info[element].move_pattern == MV_VERTICAL)
3604 if (element_info[element].move_pattern & old_move_dir)
3605 MovDir[x][y] = back_dir;
3606 else if (element_info[element].move_pattern == MV_HORIZONTAL)
3607 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
3608 else if (element_info[element].move_pattern == MV_VERTICAL)
3609 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
3611 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
3613 else if (element_info[element].move_pattern & MV_ANY_DIRECTION)
3615 MovDir[x][y] = element_info[element].move_pattern;
3616 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
3618 else if (element_info[element].move_pattern == MV_ALONG_LEFT_SIDE)
3620 if (ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
3621 MovDir[x][y] = left_dir;
3622 else if (!ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
3623 MovDir[x][y] = right_dir;
3625 if (MovDir[x][y] != old_move_dir)
3626 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
3628 else if (element_info[element].move_pattern == MV_ALONG_RIGHT_SIDE)
3630 if (ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
3631 MovDir[x][y] = right_dir;
3632 else if (!ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
3633 MovDir[x][y] = left_dir;
3635 if (MovDir[x][y] != old_move_dir)
3636 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
3638 else if (element_info[element].move_pattern == MV_TOWARDS_PLAYER ||
3639 element_info[element].move_pattern == MV_AWAY_FROM_PLAYER)
3641 int attr_x = -1, attr_y = -1;
3644 (element_info[element].move_pattern == MV_AWAY_FROM_PLAYER);
3655 for (i=0; i<MAX_PLAYERS; i++)
3657 struct PlayerInfo *player = &stored_player[i];
3658 int jx = player->jx, jy = player->jy;
3660 if (!player->active)
3664 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
3672 MovDir[x][y] = MV_NO_MOVING;
3674 MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
3675 else if (attr_x > x)
3676 MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
3678 MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
3679 else if (attr_y > y)
3680 MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
3682 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
3684 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
3686 boolean first_horiz = RND(2);
3687 int new_move_dir = MovDir[x][y];
3690 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
3691 Moving2Blocked(x, y, &newx, &newy);
3693 if (ELEMENT_CAN_ENTER_FIELD_OR_ACID(element, newx, newy))
3697 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
3698 Moving2Blocked(x, y, &newx, &newy);
3700 if (ELEMENT_CAN_ENTER_FIELD_OR_ACID(element, newx, newy))
3703 MovDir[x][y] = old_move_dir;
3706 else if (element_info[element].move_pattern == MV_WHEN_PUSHED)
3708 if (!IN_LEV_FIELD_AND_IS_FREE(move_x, move_y))
3709 MovDir[x][y] = MV_NO_MOVING;
3715 static boolean JustBeingPushed(int x, int y)
3719 for (i=0; i<MAX_PLAYERS; i++)
3721 struct PlayerInfo *player = &stored_player[i];
3723 if (player->active && player->is_pushing && player->MovPos)
3725 int next_jx = player->jx + (player->jx - player->last_jx);
3726 int next_jy = player->jy + (player->jy - player->last_jy);
3728 if (x == next_jx && y == next_jy)
3736 void StartMoving(int x, int y)
3738 boolean use_spring_bug = (game.engine_version < VERSION_IDENT(2,2,0));
3739 boolean started_moving = FALSE; /* some elements can fall _and_ move */
3740 int element = Feld[x][y];
3745 /* !!! this should be handled more generic (not only for mole) !!! */
3746 if (element != EL_MOLE && GfxAction[x][y] != ACTION_DIGGING)
3747 GfxAction[x][y] = ACTION_DEFAULT;
3749 if (CAN_FALL(element) && y < lev_fieldy - 1)
3751 if ((x > 0 && IS_PLAYER(x - 1, y)) ||
3752 (x < lev_fieldx-1 && IS_PLAYER(x + 1, y)))
3753 if (JustBeingPushed(x, y))
3756 if (element == EL_QUICKSAND_FULL)
3758 if (IS_FREE(x, y + 1))
3760 InitMovingField(x, y, MV_DOWN);
3761 started_moving = TRUE;
3763 Feld[x][y] = EL_QUICKSAND_EMPTYING;
3764 Store[x][y] = EL_ROCK;
3766 PlaySoundLevelAction(x, y, ACTION_EMPTYING);
3768 PlaySoundLevel(x, y, SND_QUICKSAND_EMPTYING);
3771 else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
3773 if (!MovDelay[x][y])
3774 MovDelay[x][y] = TILEY + 1;
3783 Feld[x][y] = EL_QUICKSAND_EMPTY;
3784 Feld[x][y + 1] = EL_QUICKSAND_FULL;
3785 Store[x][y + 1] = Store[x][y];
3788 PlaySoundLevelAction(x, y, ACTION_FILLING);
3790 PlaySoundLevel(x, y, SND_QUICKSAND_FILLING);
3794 else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
3795 Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
3797 InitMovingField(x, y, MV_DOWN);
3798 started_moving = TRUE;
3800 Feld[x][y] = EL_QUICKSAND_FILLING;
3801 Store[x][y] = element;
3803 PlaySoundLevelAction(x, y, ACTION_FILLING);
3805 PlaySoundLevel(x, y, SND_QUICKSAND_FILLING);
3808 else if (element == EL_MAGIC_WALL_FULL)
3810 if (IS_FREE(x, y + 1))
3812 InitMovingField(x, y, MV_DOWN);
3813 started_moving = TRUE;
3815 Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
3816 Store[x][y] = EL_CHANGED(Store[x][y]);
3818 else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
3820 if (!MovDelay[x][y])
3821 MovDelay[x][y] = TILEY/4 + 1;
3830 Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
3831 Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
3832 Store[x][y + 1] = EL_CHANGED(Store[x][y]);
3836 else if (element == EL_BD_MAGIC_WALL_FULL)
3838 if (IS_FREE(x, y + 1))
3840 InitMovingField(x, y, MV_DOWN);
3841 started_moving = TRUE;
3843 Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
3844 Store[x][y] = EL_CHANGED2(Store[x][y]);
3846 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
3848 if (!MovDelay[x][y])
3849 MovDelay[x][y] = TILEY/4 + 1;
3858 Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
3859 Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
3860 Store[x][y + 1] = EL_CHANGED2(Store[x][y]);
3864 else if (CAN_PASS_MAGIC_WALL(element) &&
3865 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
3866 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE))
3868 InitMovingField(x, y, MV_DOWN);
3869 started_moving = TRUE;
3872 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
3873 EL_BD_MAGIC_WALL_FILLING);
3874 Store[x][y] = element;
3877 else if (CAN_SMASH(element) && Feld[x][y + 1] == EL_ACID)
3879 else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
3884 InitMovingField(x, y, MV_DOWN);
3885 started_moving = TRUE;
3887 Store[x][y] = EL_ACID;
3889 /* !!! TEST !!! better use "_FALLING" etc. !!! */
3890 GfxAction[x][y + 1] = ACTION_ACTIVE;
3894 else if ((game.engine_version < RELEASE_IDENT(2,2,0,7) &&
3895 CAN_SMASH(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
3896 (Feld[x][y + 1] == EL_BLOCKED)) ||
3897 (game.engine_version >= VERSION_IDENT(3,0,7) &&
3898 CAN_SMASH(element) && WasJustFalling[x][y] &&
3899 (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))))
3903 else if (game.engine_version < RELEASE_IDENT(2,2,0,7) &&
3904 CAN_SMASH(element) && Feld[x][y + 1] == EL_BLOCKED &&
3905 WasJustMoving[x][y] && !Pushed[x][y + 1])
3907 else if (CAN_SMASH(element) && Feld[x][y + 1] == EL_BLOCKED &&
3908 WasJustMoving[x][y])
3913 /* this is needed for a special case not covered by calling "Impact()"
3914 from "ContinueMoving()": if an element moves to a tile directly below
3915 another element which was just falling on that tile (which was empty
3916 in the previous frame), the falling element above would just stop
3917 instead of smashing the element below (in previous version, the above
3918 element was just checked for "moving" instead of "falling", resulting
3919 in incorrect smashes caused by horizontal movement of the above
3920 element; also, the case of the player being the element to smash was
3921 simply not covered here... :-/ ) */
3925 else if (IS_FREE(x, y + 1) && element == EL_SPRING && use_spring_bug)
3927 if (MovDir[x][y] == MV_NO_MOVING)
3929 InitMovingField(x, y, MV_DOWN);
3930 started_moving = TRUE;
3933 else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
3935 if (WasJustFalling[x][y]) /* prevent animation from being restarted */
3936 MovDir[x][y] = MV_DOWN;
3938 InitMovingField(x, y, MV_DOWN);
3939 started_moving = TRUE;
3941 else if (element == EL_AMOEBA_DROP)
3943 Feld[x][y] = EL_AMOEBA_GROWING;
3944 Store[x][y] = EL_AMOEBA_WET;
3946 /* Store[x][y + 1] must be zero, because:
3947 (EL_QUICKSAND_FULL -> EL_ROCK): Store[x][y + 1] == EL_QUICKSAND_EMPTY
3950 #if OLD_GAME_BEHAVIOUR
3951 else if (IS_SLIPPERY(Feld[x][y + 1]) && !Store[x][y + 1])
3953 else if (IS_SLIPPERY(Feld[x][y + 1]) && !Store[x][y + 1] &&
3954 !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
3955 element != EL_DX_SUPABOMB)
3958 else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
3959 (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
3960 !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
3961 element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
3964 boolean can_fall_left = (x > 0 && IS_FREE(x - 1, y) &&
3965 (IS_FREE(x - 1, y + 1) ||
3966 Feld[x - 1][y + 1] == EL_ACID));
3967 boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
3968 (IS_FREE(x + 1, y + 1) ||
3969 Feld[x + 1][y + 1] == EL_ACID));
3970 boolean can_fall_any = (can_fall_left || can_fall_right);
3971 boolean can_fall_both = (can_fall_left && can_fall_right);
3973 if (can_fall_any && IS_CUSTOM_ELEMENT(Feld[x][y + 1]))
3975 int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
3977 if (slippery_type == SLIPPERY_ONLY_LEFT)
3978 can_fall_right = FALSE;
3979 else if (slippery_type == SLIPPERY_ONLY_RIGHT)
3980 can_fall_left = FALSE;
3981 else if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
3982 can_fall_right = FALSE;
3983 else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
3984 can_fall_left = FALSE;
3986 can_fall_any = (can_fall_left || can_fall_right);
3987 can_fall_both = (can_fall_left && can_fall_right);
3992 if (can_fall_both &&
3993 (game.emulation != EMU_BOULDERDASH &&
3994 element != EL_BD_ROCK && element != EL_BD_DIAMOND))
3995 can_fall_left = !(can_fall_right = RND(2));
3997 InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
3998 started_moving = TRUE;
4001 else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
4003 boolean left_is_free = (x > 0 && IS_FREE(x - 1, y));
4004 boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
4005 int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
4006 int belt_dir = game.belt_dir[belt_nr];
4008 if ((belt_dir == MV_LEFT && left_is_free) ||
4009 (belt_dir == MV_RIGHT && right_is_free))
4011 InitMovingField(x, y, belt_dir);
4012 started_moving = TRUE;
4014 GfxAction[x][y] = ACTION_DEFAULT;
4019 /* not "else if" because of elements that can fall and move (EL_SPRING) */
4020 if (CAN_MOVE(element) && !started_moving)
4025 if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
4028 if ((element == EL_SATELLITE ||
4029 element == EL_BALLOON ||
4030 element == EL_SPRING)
4031 && JustBeingPushed(x, y))
4037 if (element == EL_SPRING && MovDir[x][y] == MV_DOWN)
4038 Feld[x][y + 1] = EL_EMPTY; /* was set to EL_BLOCKED above */
4040 if (element == EL_SPRING && MovDir[x][y] != MV_NO_MOVING)
4042 Moving2Blocked(x, y, &newx, &newy);
4043 if (Feld[newx][newy] == EL_BLOCKED)
4044 Feld[newx][newy] = EL_EMPTY; /* was set to EL_BLOCKED above */
4049 if (!MovDelay[x][y]) /* start new movement phase */
4051 /* all objects that can change their move direction after each step
4052 (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall */
4054 if (element != EL_YAMYAM &&
4055 element != EL_DARK_YAMYAM &&
4056 element != EL_PACMAN &&
4057 !(element_info[element].move_pattern & MV_ANY_DIRECTION) &&
4058 element_info[element].move_pattern != MV_TURNING_LEFT &&
4059 element_info[element].move_pattern != MV_TURNING_RIGHT)
4063 if (MovDelay[x][y] && (element == EL_BUG ||
4064 element == EL_SPACESHIP ||
4065 element == EL_SP_SNIKSNAK ||
4066 element == EL_SP_ELECTRON ||
4067 element == EL_MOLE))
4068 DrawLevelField(x, y);
4072 if (MovDelay[x][y]) /* wait some time before next movement */
4077 if (element == EL_YAMYAM)
4080 el_act_dir2img(EL_YAMYAM, ACTION_WAITING, MV_LEFT));
4081 DrawLevelElementAnimation(x, y, element);
4085 if (MovDelay[x][y]) /* element still has to wait some time */
4088 /* !!! PLACE THIS SOMEWHERE AFTER "TurnRound()" !!! */
4089 ResetGfxAnimation(x, y);
4091 GfxAction[x][y] = ACTION_WAITING;
4094 if (element == EL_ROBOT ||
4096 element == EL_PACMAN ||
4098 element == EL_YAMYAM ||
4099 element == EL_DARK_YAMYAM)
4102 DrawLevelElementAnimation(x, y, element);
4104 DrawLevelElementAnimationIfNeeded(x, y, element);
4106 PlaySoundLevelAction(x, y, ACTION_WAITING);
4108 else if (element == EL_SP_ELECTRON)
4109 DrawLevelElementAnimationIfNeeded(x, y, element);
4110 else if (element == EL_DRAGON)
4113 int dir = MovDir[x][y];
4114 int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
4115 int dy = (dir == MV_UP ? -1 : dir == MV_DOWN ? +1 : 0);
4116 int graphic = (dir == MV_LEFT ? IMG_FLAMES_1_LEFT :
4117 dir == MV_RIGHT ? IMG_FLAMES_1_RIGHT :
4118 dir == MV_UP ? IMG_FLAMES_1_UP :
4119 dir == MV_DOWN ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
4120 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
4123 printf("::: %d, %d\n", GfxAction[x][y], GfxFrame[x][y]);
4126 GfxAction[x][y] = ACTION_ATTACKING;
4128 if (IS_PLAYER(x, y))
4129 DrawPlayerField(x, y);
4131 DrawLevelField(x, y);
4133 PlaySoundLevelActionIfLoop(x, y, ACTION_ATTACKING);
4135 for (i=1; i <= 3; i++)
4137 int xx = x + i * dx;
4138 int yy = y + i * dy;
4139 int sx = SCREENX(xx);
4140 int sy = SCREENY(yy);
4141 int flame_graphic = graphic + (i - 1);
4143 if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
4148 int flamed = MovingOrBlocked2Element(xx, yy);
4150 if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_FIRE(flamed))
4153 RemoveMovingField(xx, yy);
4155 Feld[xx][yy] = EL_FLAMES;
4156 if (IN_SCR_FIELD(sx, sy))
4158 DrawLevelFieldCrumbledSand(xx, yy);
4159 DrawGraphic(sx, sy, flame_graphic, frame);
4164 if (Feld[xx][yy] == EL_FLAMES)
4165 Feld[xx][yy] = EL_EMPTY;
4166 DrawLevelField(xx, yy);
4171 if (MovDelay[x][y]) /* element still has to wait some time */
4173 PlaySoundLevelAction(x, y, ACTION_WAITING);
4178 /* special case of "moving" animation of waiting elements (FIX THIS !!!);
4179 for all other elements GfxAction will be set by InitMovingField() */
4180 if (element == EL_BD_BUTTERFLY || element == EL_BD_FIREFLY)
4181 GfxAction[x][y] = ACTION_MOVING;
4184 /* now make next step */
4186 Moving2Blocked(x, y, &newx, &newy); /* get next screen position */
4188 if (DONT_COLLIDE_WITH(element) &&
4189 IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
4190 !PLAYER_PROTECTED(newx, newy))
4193 TestIfBadThingRunsIntoHero(x, y, MovDir[x][y]);
4196 /* player killed by element which is deadly when colliding with */
4198 KillHero(PLAYERINFO(newx, newy));
4203 else if ((element == EL_PENGUIN ||
4204 element == EL_ROBOT ||
4205 element == EL_SATELLITE ||
4206 element == EL_BALLOON ||
4207 IS_CUSTOM_ELEMENT(element)) &&
4208 IN_LEV_FIELD(newx, newy) &&
4209 MovDir[x][y] == MV_DOWN && Feld[newx][newy] == EL_ACID)
4212 Store[x][y] = EL_ACID;
4214 else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
4216 if (Feld[newx][newy] == EL_EXIT_OPEN)
4218 Feld[x][y] = EL_EMPTY;
4219 DrawLevelField(x, y);
4221 PlaySoundLevel(newx, newy, SND_PENGUIN_PASSING);
4222 if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
4223 DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
4225 local_player->friends_still_needed--;
4226 if (!local_player->friends_still_needed &&
4227 !local_player->GameOver && AllPlayersGone)
4228 local_player->LevelSolved = local_player->GameOver = TRUE;
4232 else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
4234 if (DigField(local_player, newx, newy, 0, 0, DF_DIG) == MF_MOVING)
4235 DrawLevelField(newx, newy);
4237 MovDir[x][y] = MV_NO_MOVING;
4239 else if (!IS_FREE(newx, newy))
4241 GfxAction[x][y] = ACTION_WAITING;
4243 if (IS_PLAYER(x, y))
4244 DrawPlayerField(x, y);
4246 DrawLevelField(x, y);
4250 else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
4252 if (IS_FOOD_PIG(Feld[newx][newy]))
4254 if (IS_MOVING(newx, newy))
4255 RemoveMovingField(newx, newy);
4258 Feld[newx][newy] = EL_EMPTY;
4259 DrawLevelField(newx, newy);
4262 PlaySoundLevel(x, y, SND_PIG_DIGGING);
4264 else if (!IS_FREE(newx, newy))
4266 if (IS_PLAYER(x, y))
4267 DrawPlayerField(x, y);
4269 DrawLevelField(x, y);
4273 else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
4275 if (!IS_FREE(newx, newy))
4277 if (IS_PLAYER(x, y))
4278 DrawPlayerField(x, y);
4280 DrawLevelField(x, y);
4286 boolean wanna_flame = !RND(10);
4287 int dx = newx - x, dy = newy - y;
4288 int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
4289 int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
4290 int element1 = (IN_LEV_FIELD(newx1, newy1) ?
4291 MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
4292 int element2 = (IN_LEV_FIELD(newx2, newy2) ?
4293 MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
4296 IS_CLASSIC_ENEMY(element1) ||
4297 IS_CLASSIC_ENEMY(element2)) &&
4298 element1 != EL_DRAGON && element2 != EL_DRAGON &&
4299 element1 != EL_FLAMES && element2 != EL_FLAMES)
4302 ResetGfxAnimation(x, y);
4303 GfxAction[x][y] = ACTION_ATTACKING;
4306 if (IS_PLAYER(x, y))
4307 DrawPlayerField(x, y);
4309 DrawLevelField(x, y);
4311 PlaySoundLevel(x, y, SND_DRAGON_ATTACKING);
4313 MovDelay[x][y] = 50;
4315 Feld[newx][newy] = EL_FLAMES;
4316 if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
4317 Feld[newx1][newy1] = EL_FLAMES;
4318 if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
4319 Feld[newx2][newy2] = EL_FLAMES;
4325 else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
4326 Feld[newx][newy] == EL_DIAMOND)
4328 if (IS_MOVING(newx, newy))
4329 RemoveMovingField(newx, newy);
4332 Feld[newx][newy] = EL_EMPTY;
4333 DrawLevelField(newx, newy);
4336 PlaySoundLevel(x, y, SND_YAMYAM_DIGGING);
4338 else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
4339 IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
4341 if (AmoebaNr[newx][newy])
4343 AmoebaCnt2[AmoebaNr[newx][newy]]--;
4344 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
4345 Feld[newx][newy] == EL_BD_AMOEBA)
4346 AmoebaCnt[AmoebaNr[newx][newy]]--;
4349 if (IS_MOVING(newx, newy))
4350 RemoveMovingField(newx, newy);
4353 Feld[newx][newy] = EL_EMPTY;
4354 DrawLevelField(newx, newy);
4357 PlaySoundLevel(x, y, SND_DARK_YAMYAM_DIGGING);
4359 else if ((element == EL_PACMAN || element == EL_MOLE)
4360 && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
4362 if (AmoebaNr[newx][newy])
4364 AmoebaCnt2[AmoebaNr[newx][newy]]--;
4365 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
4366 Feld[newx][newy] == EL_BD_AMOEBA)
4367 AmoebaCnt[AmoebaNr[newx][newy]]--;
4370 if (element == EL_MOLE)
4372 Feld[newx][newy] = EL_AMOEBA_SHRINKING;
4373 PlaySoundLevel(x, y, SND_MOLE_DIGGING);
4375 ResetGfxAnimation(x, y);
4376 GfxAction[x][y] = ACTION_DIGGING;
4377 DrawLevelField(x, y);
4379 MovDelay[newx][newy] = 0; /* start amoeba shrinking delay */
4380 return; /* wait for shrinking amoeba */
4382 else /* element == EL_PACMAN */
4384 Feld[newx][newy] = EL_EMPTY;
4385 DrawLevelField(newx, newy);
4386 PlaySoundLevel(x, y, SND_PACMAN_DIGGING);
4389 else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
4390 (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
4391 (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
4393 /* wait for shrinking amoeba to completely disappear */
4396 else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
4398 /* object was running against a wall */
4403 if (GFX_ELEMENT(element) != EL_SAND) /* !!! FIX THIS (crumble) !!! */
4404 DrawLevelElementAnimation(x, y, element);
4406 if (element == EL_BUG ||
4407 element == EL_SPACESHIP ||
4408 element == EL_SP_SNIKSNAK)
4409 DrawLevelField(x, y);
4410 else if (element == EL_MOLE)
4411 DrawLevelField(x, y);
4412 else if (element == EL_BD_BUTTERFLY ||
4413 element == EL_BD_FIREFLY)
4414 DrawLevelElementAnimationIfNeeded(x, y, element);
4415 else if (element == EL_SATELLITE)
4416 DrawLevelElementAnimationIfNeeded(x, y, element);
4417 else if (element == EL_SP_ELECTRON)
4418 DrawLevelElementAnimationIfNeeded(x, y, element);
4421 if (DONT_TOUCH(element))
4422 TestIfBadThingTouchesHero(x, y);
4425 PlaySoundLevelAction(x, y, ACTION_WAITING);
4431 InitMovingField(x, y, MovDir[x][y]);
4433 PlaySoundLevelAction(x, y, ACTION_MOVING);
4437 ContinueMoving(x, y);
4440 void ContinueMoving(int x, int y)
4442 int element = Feld[x][y];
4443 int direction = MovDir[x][y];
4444 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
4445 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
4446 int newx = x + dx, newy = y + dy;
4447 int nextx = newx + dx, nexty = newy + dy;
4448 boolean pushed = Pushed[x][y];
4450 MovPos[x][y] += getElementMoveStepsize(x, y);
4452 if (pushed) /* special case: moving object pushed by player */
4453 MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
4455 if (ABS(MovPos[x][y]) < TILEX)
4457 DrawLevelField(x, y);
4459 return; /* element is still moving */
4462 /* element reached destination field */
4464 Feld[x][y] = EL_EMPTY;
4465 Feld[newx][newy] = element;
4466 MovPos[x][y] = 0; /* force "not moving" for "crumbled sand" */
4468 if (element == EL_MOLE)
4470 Feld[x][y] = EL_SAND;
4472 DrawLevelFieldCrumbledSandNeighbours(x, y);
4474 else if (element == EL_QUICKSAND_FILLING)
4476 element = Feld[newx][newy] = get_next_element(element);
4477 Store[newx][newy] = Store[x][y];
4479 else if (element == EL_QUICKSAND_EMPTYING)
4481 Feld[x][y] = get_next_element(element);
4482 element = Feld[newx][newy] = Store[x][y];
4484 else if (element == EL_MAGIC_WALL_FILLING)
4486 element = Feld[newx][newy] = get_next_element(element);
4487 if (!game.magic_wall_active)
4488 element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
4489 Store[newx][newy] = Store[x][y];
4491 else if (element == EL_MAGIC_WALL_EMPTYING)
4493 Feld[x][y] = get_next_element(element);
4494 if (!game.magic_wall_active)
4495 Feld[x][y] = EL_MAGIC_WALL_DEAD;
4496 element = Feld[newx][newy] = Store[x][y];
4498 else if (element == EL_BD_MAGIC_WALL_FILLING)
4500 element = Feld[newx][newy] = get_next_element(element);
4501 if (!game.magic_wall_active)
4502 element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
4503 Store[newx][newy] = Store[x][y];
4505 else if (element == EL_BD_MAGIC_WALL_EMPTYING)
4507 Feld[x][y] = get_next_element(element);
4508 if (!game.magic_wall_active)
4509 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
4510 element = Feld[newx][newy] = Store[x][y];
4512 else if (element == EL_AMOEBA_DROPPING)
4514 Feld[x][y] = get_next_element(element);
4515 element = Feld[newx][newy] = Store[x][y];
4517 else if (element == EL_SOKOBAN_OBJECT)
4520 Feld[x][y] = Back[x][y];
4522 if (Back[newx][newy])
4523 Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
4525 Back[x][y] = Back[newx][newy] = 0;
4527 else if (Store[x][y] == EL_ACID)
4529 element = Feld[newx][newy] = EL_ACID;
4533 MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
4534 MovDelay[newx][newy] = 0;
4536 /* copy element change control values to new field */
4537 ChangeDelay[newx][newy] = ChangeDelay[x][y];
4538 ChangePage[newx][newy] = ChangePage[x][y];
4539 Changed[newx][newy] = Changed[x][y];
4540 ChangeEvent[newx][newy] = ChangeEvent[x][y];
4542 ChangeDelay[x][y] = 0;
4543 ChangePage[x][y] = -1;
4544 Changed[x][y] = CE_BITMASK_DEFAULT;
4545 ChangeEvent[x][y] = CE_BITMASK_DEFAULT;
4547 /* copy animation control values to new field */
4548 GfxFrame[newx][newy] = GfxFrame[x][y];
4549 GfxAction[newx][newy] = GfxAction[x][y]; /* keep action one frame */
4550 GfxRandom[newx][newy] = GfxRandom[x][y]; /* keep same random value */
4552 Pushed[x][y] = Pushed[newx][newy] = FALSE;
4554 ResetGfxAnimation(x, y); /* reset animation values for old field */
4557 /* 2.1.1 (does not work correctly for spring) */
4558 if (!CAN_MOVE(element))
4559 MovDir[newx][newy] = 0;
4563 /* (does not work for falling objects that slide horizontally) */
4564 if (CAN_FALL(element) && MovDir[newx][newy] == MV_DOWN)
4565 MovDir[newx][newy] = 0;
4568 if (!CAN_MOVE(element) ||
4569 (element == EL_SPRING && MovDir[newx][newy] == MV_DOWN))
4570 MovDir[newx][newy] = 0;
4573 if (!CAN_MOVE(element) ||
4574 (CAN_FALL(element) && direction == MV_DOWN))
4575 MovDir[newx][newy] = 0;
4580 DrawLevelField(x, y);
4581 DrawLevelField(newx, newy);
4583 Stop[newx][newy] = TRUE; /* ignore this element until the next frame */
4585 /* prevent pushed element from moving on in pushed direction */
4586 if (pushed && CAN_MOVE(element) &&
4587 element_info[element].move_pattern & MV_ANY_DIRECTION &&
4588 !(element_info[element].move_pattern & direction))
4589 TurnRound(newx, newy);
4591 if (!pushed) /* special case: moving object pushed by player */
4593 WasJustMoving[newx][newy] = 3;
4595 if (CAN_FALL(element) && direction == MV_DOWN)
4596 WasJustFalling[newx][newy] = 3;
4599 if (DONT_TOUCH(element)) /* object may be nasty to player or others */
4601 TestIfBadThingTouchesHero(newx, newy);
4602 TestIfBadThingTouchesFriend(newx, newy);
4603 TestIfBadThingTouchesOtherBadThing(newx, newy);
4605 else if (element == EL_PENGUIN)
4606 TestIfFriendTouchesBadThing(newx, newy);
4608 if (CAN_FALL(element) && direction == MV_DOWN &&
4609 (newy == lev_fieldy - 1 || !IS_FREE(x, newy + 1)))
4613 TestIfElementTouchesCustomElement(x, y); /* for empty space */
4617 if (ChangePage[newx][newy] != -1) /* delayed change */
4618 ChangeElement(newx, newy, ChangePage[newx][newy]);
4621 if (!IN_LEV_FIELD(nextx, nexty) || !IS_FREE(nextx, nexty))
4622 CheckElementSideChange(newx, newy, Feld[newx][newy], direction,
4625 TestIfPlayerTouchesCustomElement(newx, newy);
4626 TestIfElementTouchesCustomElement(newx, newy);
4629 int AmoebeNachbarNr(int ax, int ay)
4632 int element = Feld[ax][ay];
4634 static int xy[4][2] =
4644 int x = ax + xy[i][0];
4645 int y = ay + xy[i][1];
4647 if (!IN_LEV_FIELD(x, y))
4650 if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
4651 group_nr = AmoebaNr[x][y];
4657 void AmoebenVereinigen(int ax, int ay)
4659 int i, x, y, xx, yy;
4660 int new_group_nr = AmoebaNr[ax][ay];
4661 static int xy[4][2] =
4669 if (new_group_nr == 0)
4677 if (!IN_LEV_FIELD(x, y))
4680 if ((Feld[x][y] == EL_AMOEBA_FULL ||
4681 Feld[x][y] == EL_BD_AMOEBA ||
4682 Feld[x][y] == EL_AMOEBA_DEAD) &&
4683 AmoebaNr[x][y] != new_group_nr)
4685 int old_group_nr = AmoebaNr[x][y];
4687 if (old_group_nr == 0)
4690 AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
4691 AmoebaCnt[old_group_nr] = 0;
4692 AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
4693 AmoebaCnt2[old_group_nr] = 0;
4695 for (yy=0; yy<lev_fieldy; yy++)
4697 for (xx=0; xx<lev_fieldx; xx++)
4699 if (AmoebaNr[xx][yy] == old_group_nr)
4700 AmoebaNr[xx][yy] = new_group_nr;
4707 void AmoebeUmwandeln(int ax, int ay)
4711 if (Feld[ax][ay] == EL_AMOEBA_DEAD)
4713 int group_nr = AmoebaNr[ax][ay];
4718 printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
4719 printf("AmoebeUmwandeln(): This should never happen!\n");
4724 for (y=0; y<lev_fieldy; y++)
4726 for (x=0; x<lev_fieldx; x++)
4728 if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
4731 Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
4735 PlaySoundLevel(ax, ay, (IS_GEM(level.amoeba_content) ?
4736 SND_AMOEBA_TURNING_TO_GEM :
4737 SND_AMOEBA_TURNING_TO_ROCK));
4742 static int xy[4][2] =
4755 if (!IN_LEV_FIELD(x, y))
4758 if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
4760 PlaySoundLevel(x, y, (IS_GEM(level.amoeba_content) ?
4761 SND_AMOEBA_TURNING_TO_GEM :
4762 SND_AMOEBA_TURNING_TO_ROCK));
4769 void AmoebeUmwandelnBD(int ax, int ay, int new_element)
4772 int group_nr = AmoebaNr[ax][ay];
4773 boolean done = FALSE;
4778 printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
4779 printf("AmoebeUmwandelnBD(): This should never happen!\n");
4784 for (y=0; y<lev_fieldy; y++)
4786 for (x=0; x<lev_fieldx; x++)
4788 if (AmoebaNr[x][y] == group_nr &&
4789 (Feld[x][y] == EL_AMOEBA_DEAD ||
4790 Feld[x][y] == EL_BD_AMOEBA ||
4791 Feld[x][y] == EL_AMOEBA_GROWING))
4794 Feld[x][y] = new_element;
4795 InitField(x, y, FALSE);
4796 DrawLevelField(x, y);
4803 PlaySoundLevel(ax, ay, (new_element == EL_BD_ROCK ?
4804 SND_BD_AMOEBA_TURNING_TO_ROCK :
4805 SND_BD_AMOEBA_TURNING_TO_GEM));
4808 void AmoebeWaechst(int x, int y)
4810 static unsigned long sound_delay = 0;
4811 static unsigned long sound_delay_value = 0;
4813 if (!MovDelay[x][y]) /* start new growing cycle */
4817 if (DelayReached(&sound_delay, sound_delay_value))
4820 PlaySoundLevelElementAction(x, y, Store[x][y], ACTION_GROWING);
4822 if (Store[x][y] == EL_BD_AMOEBA)
4823 PlaySoundLevel(x, y, SND_BD_AMOEBA_GROWING);
4825 PlaySoundLevel(x, y, SND_AMOEBA_GROWING);
4827 sound_delay_value = 30;
4831 if (MovDelay[x][y]) /* wait some time before growing bigger */
4834 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
4836 int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
4837 6 - MovDelay[x][y]);
4839 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
4842 if (!MovDelay[x][y])
4844 Feld[x][y] = Store[x][y];
4846 DrawLevelField(x, y);
4851 void AmoebaDisappearing(int x, int y)
4853 static unsigned long sound_delay = 0;
4854 static unsigned long sound_delay_value = 0;
4856 if (!MovDelay[x][y]) /* start new shrinking cycle */
4860 if (DelayReached(&sound_delay, sound_delay_value))
4861 sound_delay_value = 30;
4864 if (MovDelay[x][y]) /* wait some time before shrinking */
4867 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
4869 int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
4870 6 - MovDelay[x][y]);
4872 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
4875 if (!MovDelay[x][y])
4877 Feld[x][y] = EL_EMPTY;
4878 DrawLevelField(x, y);
4880 /* don't let mole enter this field in this cycle;
4881 (give priority to objects falling to this field from above) */
4887 void AmoebeAbleger(int ax, int ay)
4890 int element = Feld[ax][ay];
4891 int graphic = el2img(element);
4892 int newax = ax, neway = ay;
4893 static int xy[4][2] =
4901 if (!level.amoeba_speed)
4903 Feld[ax][ay] = EL_AMOEBA_DEAD;
4904 DrawLevelField(ax, ay);
4908 if (IS_ANIMATED(graphic))
4909 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
4911 if (!MovDelay[ax][ay]) /* start making new amoeba field */
4912 MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
4914 if (MovDelay[ax][ay]) /* wait some time before making new amoeba */
4917 if (MovDelay[ax][ay])
4921 if (element == EL_AMOEBA_WET) /* object is an acid / amoeba drop */
4924 int x = ax + xy[start][0];
4925 int y = ay + xy[start][1];
4927 if (!IN_LEV_FIELD(x, y))
4930 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
4931 if (IS_FREE(x, y) ||
4932 Feld[x][y] == EL_SAND || Feld[x][y] == EL_QUICKSAND_EMPTY)
4938 if (newax == ax && neway == ay)
4941 else /* normal or "filled" (BD style) amoeba */
4944 boolean waiting_for_player = FALSE;
4948 int j = (start + i) % 4;
4949 int x = ax + xy[j][0];
4950 int y = ay + xy[j][1];
4952 if (!IN_LEV_FIELD(x, y))
4955 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
4956 if (IS_FREE(x, y) ||
4957 Feld[x][y] == EL_SAND || Feld[x][y] == EL_QUICKSAND_EMPTY)
4963 else if (IS_PLAYER(x, y))
4964 waiting_for_player = TRUE;
4967 if (newax == ax && neway == ay) /* amoeba cannot grow */
4969 if (i == 4 && (!waiting_for_player || game.emulation == EMU_BOULDERDASH))
4971 Feld[ax][ay] = EL_AMOEBA_DEAD;
4972 DrawLevelField(ax, ay);
4973 AmoebaCnt[AmoebaNr[ax][ay]]--;
4975 if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0) /* amoeba is completely dead */
4977 if (element == EL_AMOEBA_FULL)
4978 AmoebeUmwandeln(ax, ay);
4979 else if (element == EL_BD_AMOEBA)
4980 AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
4985 else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
4987 /* amoeba gets larger by growing in some direction */
4989 int new_group_nr = AmoebaNr[ax][ay];
4992 if (new_group_nr == 0)
4994 printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
4995 printf("AmoebeAbleger(): This should never happen!\n");
5000 AmoebaNr[newax][neway] = new_group_nr;
5001 AmoebaCnt[new_group_nr]++;
5002 AmoebaCnt2[new_group_nr]++;
5004 /* if amoeba touches other amoeba(s) after growing, unify them */
5005 AmoebenVereinigen(newax, neway);
5007 if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
5009 AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
5015 if (element != EL_AMOEBA_WET || neway < ay || !IS_FREE(newax, neway) ||
5016 (neway == lev_fieldy - 1 && newax != ax))
5018 Feld[newax][neway] = EL_AMOEBA_GROWING; /* creation of new amoeba */
5019 Store[newax][neway] = element;
5021 else if (neway == ay)
5023 Feld[newax][neway] = EL_AMOEBA_DROP; /* drop left/right of amoeba */
5025 PlaySoundLevelAction(newax, neway, ACTION_GROWING);
5027 PlaySoundLevel(newax, neway, SND_AMOEBA_GROWING);
5032 InitMovingField(ax, ay, MV_DOWN); /* drop dripping from amoeba */
5033 Feld[ax][ay] = EL_AMOEBA_DROPPING;
5034 Store[ax][ay] = EL_AMOEBA_DROP;
5035 ContinueMoving(ax, ay);
5039 DrawLevelField(newax, neway);
5042 void Life(int ax, int ay)
5045 static int life[4] = { 2, 3, 3, 3 }; /* parameters for "game of life" */
5047 int element = Feld[ax][ay];
5048 int graphic = el2img(element);
5049 boolean changed = FALSE;
5051 if (IS_ANIMATED(graphic))
5052 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
5057 if (!MovDelay[ax][ay]) /* start new "game of life" cycle */
5058 MovDelay[ax][ay] = life_time;
5060 if (MovDelay[ax][ay]) /* wait some time before next cycle */
5063 if (MovDelay[ax][ay])
5067 for (y1=-1; y1<2; y1++) for(x1=-1; x1<2; x1++)
5069 int xx = ax+x1, yy = ay+y1;
5072 if (!IN_LEV_FIELD(xx, yy))
5075 for (y2=-1; y2<2; y2++) for (x2=-1; x2<2; x2++)
5077 int x = xx+x2, y = yy+y2;
5079 if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
5082 if (((Feld[x][y] == element ||
5083 (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y))) &&
5085 (IS_FREE(x, y) && Stop[x][y]))
5089 if (xx == ax && yy == ay) /* field in the middle */
5091 if (nachbarn < life[0] || nachbarn > life[1])
5093 Feld[xx][yy] = EL_EMPTY;
5095 DrawLevelField(xx, yy);
5096 Stop[xx][yy] = TRUE;
5100 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
5101 else if (IS_FREE(xx, yy) || Feld[xx][yy] == EL_SAND)
5102 { /* free border field */
5103 if (nachbarn >= life[2] && nachbarn <= life[3])
5105 Feld[xx][yy] = element;
5106 MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
5108 DrawLevelField(xx, yy);
5109 Stop[xx][yy] = TRUE;
5116 PlaySoundLevel(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
5117 SND_GAME_OF_LIFE_GROWING);
5120 static void InitRobotWheel(int x, int y)
5122 ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
5125 static void RunRobotWheel(int x, int y)
5127 PlaySoundLevel(x, y, SND_ROBOT_WHEEL_ACTIVE);
5130 static void StopRobotWheel(int x, int y)
5132 if (ZX == x && ZY == y)
5136 static void InitTimegateWheel(int x, int y)
5138 ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
5141 static void RunTimegateWheel(int x, int y)
5143 PlaySoundLevel(x, y, SND_TIMEGATE_SWITCH_ACTIVE);
5146 void CheckExit(int x, int y)
5148 if (local_player->gems_still_needed > 0 ||
5149 local_player->sokobanfields_still_needed > 0 ||
5150 local_player->lights_still_needed > 0)
5152 int element = Feld[x][y];
5153 int graphic = el2img(element);
5155 if (IS_ANIMATED(graphic))
5156 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
5161 if (AllPlayersGone) /* do not re-open exit door closed after last player */
5164 Feld[x][y] = EL_EXIT_OPENING;
5166 PlaySoundLevelNearest(x, y, SND_CLASS_EXIT_OPENING);
5169 void CheckExitSP(int x, int y)
5171 if (local_player->gems_still_needed > 0)
5173 int element = Feld[x][y];
5174 int graphic = el2img(element);
5176 if (IS_ANIMATED(graphic))
5177 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
5182 if (AllPlayersGone) /* do not re-open exit door closed after last player */
5185 Feld[x][y] = EL_SP_EXIT_OPENING;
5187 PlaySoundLevelNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
5190 static void CloseAllOpenTimegates()
5194 for (y=0; y<lev_fieldy; y++)
5196 for (x=0; x<lev_fieldx; x++)
5198 int element = Feld[x][y];
5200 if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
5202 Feld[x][y] = EL_TIMEGATE_CLOSING;
5204 PlaySoundLevelAction(x, y, ACTION_CLOSING);
5206 PlaySoundLevel(x, y, SND_TIMEGATE_CLOSING);
5213 void EdelsteinFunkeln(int x, int y)
5215 if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
5218 if (Feld[x][y] == EL_BD_DIAMOND)
5221 if (MovDelay[x][y] == 0) /* next animation frame */
5222 MovDelay[x][y] = 11 * !SimpleRND(500);
5224 if (MovDelay[x][y] != 0) /* wait some time before next frame */
5228 if (setup.direct_draw && MovDelay[x][y])
5229 SetDrawtoField(DRAW_BUFFERED);
5231 DrawLevelElementAnimation(x, y, Feld[x][y]);
5233 if (MovDelay[x][y] != 0)
5235 int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
5236 10 - MovDelay[x][y]);
5238 DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
5240 if (setup.direct_draw)
5244 dest_x = FX + SCREENX(x) * TILEX;
5245 dest_y = FY + SCREENY(y) * TILEY;
5247 BlitBitmap(drawto_field, window,
5248 dest_x, dest_y, TILEX, TILEY, dest_x, dest_y);
5249 SetDrawtoField(DRAW_DIRECT);
5255 void MauerWaechst(int x, int y)
5259 if (!MovDelay[x][y]) /* next animation frame */
5260 MovDelay[x][y] = 3 * delay;
5262 if (MovDelay[x][y]) /* wait some time before next frame */
5266 if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
5268 int graphic = el_dir2img(Feld[x][y], MovDir[x][y]);
5269 int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
5271 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5274 if (!MovDelay[x][y])
5276 if (MovDir[x][y] == MV_LEFT)
5278 if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
5279 DrawLevelField(x - 1, y);
5281 else if (MovDir[x][y] == MV_RIGHT)
5283 if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
5284 DrawLevelField(x + 1, y);
5286 else if (MovDir[x][y] == MV_UP)
5288 if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
5289 DrawLevelField(x, y - 1);
5293 if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
5294 DrawLevelField(x, y + 1);
5297 Feld[x][y] = Store[x][y];
5299 MovDir[x][y] = MV_NO_MOVING;
5300 DrawLevelField(x, y);
5305 void MauerAbleger(int ax, int ay)
5307 int element = Feld[ax][ay];
5308 int graphic = el2img(element);
5309 boolean oben_frei = FALSE, unten_frei = FALSE;
5310 boolean links_frei = FALSE, rechts_frei = FALSE;
5311 boolean oben_massiv = FALSE, unten_massiv = FALSE;
5312 boolean links_massiv = FALSE, rechts_massiv = FALSE;
5313 boolean new_wall = FALSE;
5315 if (IS_ANIMATED(graphic))
5316 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
5318 if (!MovDelay[ax][ay]) /* start building new wall */
5319 MovDelay[ax][ay] = 6;
5321 if (MovDelay[ax][ay]) /* wait some time before building new wall */
5324 if (MovDelay[ax][ay])
5328 if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
5330 if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
5332 if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
5334 if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
5337 if (element == EL_EXPANDABLE_WALL_VERTICAL ||
5338 element == EL_EXPANDABLE_WALL_ANY)
5342 Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
5343 Store[ax][ay-1] = element;
5344 MovDir[ax][ay-1] = MV_UP;
5345 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
5346 DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
5347 IMG_EXPANDABLE_WALL_GROWING_UP, 0);
5352 Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
5353 Store[ax][ay+1] = element;
5354 MovDir[ax][ay+1] = MV_DOWN;
5355 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
5356 DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
5357 IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
5362 if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
5363 element == EL_EXPANDABLE_WALL_ANY ||
5364 element == EL_EXPANDABLE_WALL)
5368 Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
5369 Store[ax-1][ay] = element;
5370 MovDir[ax-1][ay] = MV_LEFT;
5371 if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
5372 DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
5373 IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
5379 Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
5380 Store[ax+1][ay] = element;
5381 MovDir[ax+1][ay] = MV_RIGHT;
5382 if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
5383 DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
5384 IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
5389 if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
5390 DrawLevelField(ax, ay);
5392 if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
5394 if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
5395 unten_massiv = TRUE;
5396 if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
5397 links_massiv = TRUE;
5398 if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
5399 rechts_massiv = TRUE;
5401 if (((oben_massiv && unten_massiv) ||
5402 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
5403 element == EL_EXPANDABLE_WALL) &&
5404 ((links_massiv && rechts_massiv) ||
5405 element == EL_EXPANDABLE_WALL_VERTICAL))
5406 Feld[ax][ay] = EL_WALL;
5410 PlaySoundLevelAction(ax, ay, ACTION_GROWING);
5412 PlaySoundLevel(ax, ay, SND_EXPANDABLE_WALL_GROWING);
5416 void CheckForDragon(int x, int y)
5419 boolean dragon_found = FALSE;
5420 static int xy[4][2] =
5432 int xx = x + j*xy[i][0], yy = y + j*xy[i][1];
5434 if (IN_LEV_FIELD(xx, yy) &&
5435 (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
5437 if (Feld[xx][yy] == EL_DRAGON)
5438 dragon_found = TRUE;
5451 int xx = x + j*xy[i][0], yy = y + j*xy[i][1];
5453 if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
5455 Feld[xx][yy] = EL_EMPTY;
5456 DrawLevelField(xx, yy);
5465 static void InitBuggyBase(int x, int y)
5467 int element = Feld[x][y];
5468 int activating_delay = FRAMES_PER_SECOND / 4;
5471 (element == EL_SP_BUGGY_BASE ?
5472 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
5473 element == EL_SP_BUGGY_BASE_ACTIVATING ?
5475 element == EL_SP_BUGGY_BASE_ACTIVE ?
5476 1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
5479 static void WarnBuggyBase(int x, int y)
5482 static int xy[4][2] =
5492 int xx = x + xy[i][0], yy = y + xy[i][1];
5494 if (IS_PLAYER(xx, yy))
5496 PlaySoundLevel(x, y, SND_SP_BUGGY_BASE_ACTIVE);
5503 static void InitTrap(int x, int y)
5505 ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
5508 static void ActivateTrap(int x, int y)
5510 PlaySoundLevel(x, y, SND_TRAP_ACTIVATING);
5513 static void ChangeActiveTrap(int x, int y)
5515 int graphic = IMG_TRAP_ACTIVE;
5517 /* if new animation frame was drawn, correct crumbled sand border */
5518 if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
5519 DrawLevelFieldCrumbledSand(x, y);
5522 static void ChangeElementNowExt(int x, int y, int target_element)
5524 /* check if element under player changes from accessible to unaccessible
5525 (needed for special case of dropping element which then changes) */
5526 if (IS_PLAYER(x, y) && !PLAYER_PROTECTED(x, y) &&
5527 IS_ACCESSIBLE(Feld[x][y]) && !IS_ACCESSIBLE(target_element))
5534 Feld[x][y] = target_element;
5536 Changed[x][y] |= ChangeEvent[x][y]; /* ignore same changes in this frame */
5538 ResetGfxAnimation(x, y);
5539 ResetRandomAnimationValue(x, y);
5541 InitField(x, y, FALSE);
5542 if (CAN_MOVE(Feld[x][y]))
5545 DrawLevelField(x, y);
5547 if (GFX_CRUMBLED(Feld[x][y]))
5548 DrawLevelFieldCrumbledSandNeighbours(x, y);
5550 TestIfBadThingTouchesHero(x, y);
5551 TestIfPlayerTouchesCustomElement(x, y);
5552 TestIfElementTouchesCustomElement(x, y);
5554 if (ELEM_IS_PLAYER(target_element))
5555 RelocatePlayer(x, y, target_element);
5558 static boolean ChangeElementNow(int x, int y, int element, int page)
5560 struct ElementChangeInfo *change = &element_info[element].change_page[page];
5562 /* always use default change event to prevent running into a loop */
5563 if (ChangeEvent[x][y] == CE_BITMASK_DEFAULT)
5564 ChangeEvent[x][y] = CH_EVENT_BIT(CE_DELAY);
5566 /* do not change already changed elements with same change event */
5568 if (Changed[x][y] & ChangeEvent[x][y])
5575 Changed[x][y] |= ChangeEvent[x][y]; /* ignore same changes in this frame */
5577 CheckTriggeredElementChange(x, y, Feld[x][y], CE_OTHER_IS_CHANGING);
5579 if (change->explode)
5586 if (change->use_content)
5588 boolean complete_change = TRUE;
5589 boolean can_change[3][3];
5592 for (yy = 0; yy < 3; yy++) for(xx = 0; xx < 3 ; xx++)
5594 boolean half_destructible;
5595 int ex = x + xx - 1;
5596 int ey = y + yy - 1;
5599 can_change[xx][yy] = TRUE;
5601 if (ex == x && ey == y) /* do not check changing element itself */
5604 if (change->content[xx][yy] == EL_EMPTY_SPACE)
5606 can_change[xx][yy] = FALSE; /* do not change empty borders */
5611 if (!IN_LEV_FIELD(ex, ey))
5613 can_change[xx][yy] = FALSE;
5614 complete_change = FALSE;
5621 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
5622 e = MovingOrBlocked2Element(ex, ey);
5624 half_destructible = (IS_FREE(ex, ey) || IS_DIGGABLE(e));
5626 if ((change->power <= CP_NON_DESTRUCTIVE && !IS_FREE(ex, ey)) ||
5627 (change->power <= CP_HALF_DESTRUCTIVE && !half_destructible) ||
5628 (change->power <= CP_FULL_DESTRUCTIVE && IS_INDESTRUCTIBLE(e)))
5630 can_change[xx][yy] = FALSE;
5631 complete_change = FALSE;
5635 if (!change->only_complete || complete_change)
5637 boolean something_has_changed = FALSE;
5639 if (change->only_complete && change->use_random_change &&
5640 RND(100) < change->random)
5643 for (yy = 0; yy < 3; yy++) for(xx = 0; xx < 3 ; xx++)
5645 int ex = x + xx - 1;
5646 int ey = y + yy - 1;
5648 if (can_change[xx][yy] && (!change->use_random_change ||
5649 RND(100) < change->random))
5651 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
5652 RemoveMovingField(ex, ey);
5654 ChangeEvent[ex][ey] = ChangeEvent[x][y];
5656 ChangeElementNowExt(ex, ey, change->content[xx][yy]);
5658 something_has_changed = TRUE;
5660 /* for symmetry reasons, freeze newly created border elements */
5661 if (ex != x || ey != y)
5662 Stop[ex][ey] = TRUE; /* no more moving in this frame */
5666 if (something_has_changed)
5667 PlaySoundLevelElementAction(x, y, element, ACTION_CHANGING);
5672 ChangeElementNowExt(x, y, change->target_element);
5674 PlaySoundLevelElementAction(x, y, element, ACTION_CHANGING);
5680 static void ChangeElement(int x, int y, int page)
5682 int element = MovingOrBlocked2Element(x, y);
5683 struct ElementInfo *ei = &element_info[element];
5684 struct ElementChangeInfo *change = &ei->change_page[page];
5688 if (!CAN_CHANGE(element))
5691 printf("ChangeElement(): %d,%d: element = %d ('%s')\n",
5692 x, y, element, element_info[element].token_name);
5693 printf("ChangeElement(): This should never happen!\n");
5699 if (ChangeDelay[x][y] == 0) /* initialize element change */
5701 ChangeDelay[x][y] = ( change->delay_fixed * change->delay_frames +
5702 RND(change->delay_random * change->delay_frames)) + 1;
5704 ResetGfxAnimation(x, y);
5705 ResetRandomAnimationValue(x, y);
5707 if (change->pre_change_function)
5708 change->pre_change_function(x, y);
5711 ChangeDelay[x][y]--;
5713 if (ChangeDelay[x][y] != 0) /* continue element change */
5715 int graphic = el_act_dir2img(element, GfxAction[x][y], MovDir[x][y]);
5717 if (IS_ANIMATED(graphic))
5718 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
5720 if (change->change_function)
5721 change->change_function(x, y);
5723 else /* finish element change */
5725 if (ChangePage[x][y] != -1) /* remember page from delayed change */
5727 page = ChangePage[x][y];
5728 ChangePage[x][y] = -1;
5731 if (IS_MOVING(x, y)) /* never change a running system ;-) */
5733 ChangeDelay[x][y] = 1; /* try change after next move step */
5734 ChangePage[x][y] = page; /* remember page to use for change */
5739 if (ChangeElementNow(x, y, element, page))
5741 if (change->post_change_function)
5742 change->post_change_function(x, y);
5747 static boolean CheckTriggeredElementSideChange(int lx, int ly,
5748 int trigger_element,
5754 if (!(trigger_events[trigger_element] & CH_EVENT_BIT(trigger_event)))
5757 for (i=0; i < NUM_CUSTOM_ELEMENTS; i++)
5759 int element = EL_CUSTOM_START + i;
5761 boolean change_element = FALSE;
5764 if (!CAN_CHANGE(element) || !HAS_ANY_CHANGE_EVENT(element, trigger_event))
5767 for (j=0; j < element_info[element].num_change_pages; j++)
5769 struct ElementChangeInfo *change = &element_info[element].change_page[j];
5771 if (change->can_change &&
5772 change->sides & trigger_side &&
5773 change->trigger_element == trigger_element)
5775 change_element = TRUE;
5782 if (!change_element)
5785 for (y=0; y<lev_fieldy; y++) for (x=0; x<lev_fieldx; x++)
5788 if (x == lx && y == ly) /* do not change trigger element itself */
5792 if (Feld[x][y] == element)
5794 ChangeDelay[x][y] = 1;
5795 ChangeEvent[x][y] = CH_EVENT_BIT(trigger_event);
5796 ChangeElement(x, y, page);
5804 static boolean CheckTriggeredElementChange(int lx, int ly, int trigger_element,
5807 return CheckTriggeredElementSideChange(lx, ly, trigger_element, CH_SIDE_ANY,
5811 static boolean CheckElementSideChange(int x, int y, int element, int side,
5812 int trigger_event, int page)
5814 if (!CAN_CHANGE(element) || !HAS_ANY_CHANGE_EVENT(element, trigger_event))
5817 if (Feld[x][y] == EL_BLOCKED)
5819 Blocked2Moving(x, y, &x, &y);
5820 element = Feld[x][y];
5824 page = element_info[element].event_page_nr[trigger_event];
5826 if (!(element_info[element].change_page[page].sides & side))
5829 ChangeDelay[x][y] = 1;
5830 ChangeEvent[x][y] = CH_EVENT_BIT(trigger_event);
5831 ChangeElement(x, y, page);
5836 static boolean CheckElementChange(int x, int y, int element, int trigger_event)
5838 return CheckElementSideChange(x, y, element, CH_SIDE_ANY, trigger_event, -1);
5841 static void PlayerActions(struct PlayerInfo *player, byte player_action)
5843 static byte stored_player_action[MAX_PLAYERS];
5844 static int num_stored_actions = 0;
5845 boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
5846 int left = player_action & JOY_LEFT;
5847 int right = player_action & JOY_RIGHT;
5848 int up = player_action & JOY_UP;
5849 int down = player_action & JOY_DOWN;
5850 int button1 = player_action & JOY_BUTTON_1;
5851 int button2 = player_action & JOY_BUTTON_2;
5852 int dx = (left ? -1 : right ? 1 : 0);
5853 int dy = (up ? -1 : down ? 1 : 0);
5855 stored_player_action[player->index_nr] = 0;
5856 num_stored_actions++;
5858 if (!player->active || tape.pausing)
5864 snapped = SnapField(player, dx, dy);
5868 dropped = DropElement(player);
5870 moved = MovePlayer(player, dx, dy);
5873 if (tape.single_step && tape.recording && !tape.pausing)
5875 if (button1 || (dropped && !moved))
5877 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
5878 SnapField(player, 0, 0); /* stop snapping */
5882 stored_player_action[player->index_nr] = player_action;
5886 /* no actions for this player (no input at player's configured device) */
5888 DigField(player, 0, 0, 0, 0, DF_NO_PUSH);
5889 SnapField(player, 0, 0);
5890 CheckGravityMovement(player);
5892 if (player->MovPos == 0)
5893 InitPlayerGfxAnimation(player, ACTION_DEFAULT, player->MovDir);
5895 if (player->MovPos == 0) /* needed for tape.playing */
5896 player->is_moving = FALSE;
5899 if (tape.recording && num_stored_actions >= MAX_PLAYERS)
5901 TapeRecordAction(stored_player_action);
5902 num_stored_actions = 0;
5908 static unsigned long action_delay = 0;
5909 unsigned long action_delay_value;
5910 int magic_wall_x = 0, magic_wall_y = 0;
5911 int i, x, y, element, graphic;
5912 byte *recorded_player_action;
5913 byte summarized_player_action = 0;
5915 if (game_status != GAME_MODE_PLAYING)
5918 action_delay_value =
5919 (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
5921 if (tape.playing && tape.index_search && !tape.pausing)
5922 action_delay_value = 0;
5924 /* ---------- main game synchronization point ---------- */
5926 WaitUntilDelayReached(&action_delay, action_delay_value);
5928 if (network_playing && !network_player_action_received)
5932 printf("DEBUG: try to get network player actions in time\n");
5936 #if defined(PLATFORM_UNIX)
5937 /* last chance to get network player actions without main loop delay */
5941 if (game_status != GAME_MODE_PLAYING)
5944 if (!network_player_action_received)
5948 printf("DEBUG: failed to get network player actions in time\n");
5958 recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
5960 for (i=0; i<MAX_PLAYERS; i++)
5962 summarized_player_action |= stored_player[i].action;
5964 if (!network_playing)
5965 stored_player[i].effective_action = stored_player[i].action;
5968 #if defined(PLATFORM_UNIX)
5969 if (network_playing)
5970 SendToServer_MovePlayer(summarized_player_action);
5973 if (!options.network && !setup.team_mode)
5974 local_player->effective_action = summarized_player_action;
5976 for (i=0; i<MAX_PLAYERS; i++)
5978 int actual_player_action = stored_player[i].effective_action;
5980 if (stored_player[i].programmed_action)
5981 actual_player_action = stored_player[i].programmed_action;
5983 if (recorded_player_action)
5984 actual_player_action = recorded_player_action[i];
5986 PlayerActions(&stored_player[i], actual_player_action);
5987 ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
5990 network_player_action_received = FALSE;
5992 ScrollScreen(NULL, SCROLL_GO_ON);
5998 for (i=0; i<MAX_PLAYERS; i++)
5999 stored_player[i].Frame++;
6003 if (game.engine_version < RELEASE_IDENT(2,2,0,7))
6005 for (i=0; i<MAX_PLAYERS; i++)
6007 struct PlayerInfo *player = &stored_player[i];
6011 if (player->active && player->is_pushing && player->is_moving &&
6014 ContinueMoving(x, y);
6016 /* continue moving after pushing (this is actually a bug) */
6017 if (!IS_MOVING(x, y))
6026 for (y=0; y<lev_fieldy; y++) for (x=0; x<lev_fieldx; x++)
6028 Changed[x][y] = CE_BITMASK_DEFAULT;
6029 ChangeEvent[x][y] = CE_BITMASK_DEFAULT;
6032 if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
6034 printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
6035 printf("GameActions(): This should never happen!\n");
6037 ChangePage[x][y] = -1;
6042 if (WasJustMoving[x][y] > 0)
6043 WasJustMoving[x][y]--;
6044 if (WasJustFalling[x][y] > 0)
6045 WasJustFalling[x][y]--;
6050 /* reset finished pushing action (not done in ContinueMoving() to allow
6051 continous pushing animation for elements with zero push delay) */
6052 if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
6054 ResetGfxAnimation(x, y);
6055 DrawLevelField(x, y);
6060 if (IS_BLOCKED(x, y))
6064 Blocked2Moving(x, y, &oldx, &oldy);
6065 if (!IS_MOVING(oldx, oldy))
6067 printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
6068 printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
6069 printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
6070 printf("GameActions(): This should never happen!\n");
6076 for (y=0; y<lev_fieldy; y++) for (x=0; x<lev_fieldx; x++)
6078 element = Feld[x][y];
6080 graphic = el_act_dir2img(element, GfxAction[x][y], MovDir[x][y]);
6082 graphic = el2img(element);
6088 printf("::: %d,%d: %d [%d]\n", x, y, element, FrameCounter);
6090 element = graphic = 0;
6094 if (graphic_info[graphic].anim_global_sync)
6095 GfxFrame[x][y] = FrameCounter;
6097 if (ANIM_MODE(graphic) == ANIM_RANDOM &&
6098 IS_NEXT_FRAME(GfxFrame[x][y], graphic))
6099 ResetRandomAnimationValue(x, y);
6101 SetRandomAnimationValue(x, y);
6104 PlaySoundLevelActionIfLoop(x, y, GfxAction[x][y]);
6107 if (IS_INACTIVE(element))
6109 if (IS_ANIMATED(graphic))
6110 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
6116 /* this may take place after moving, so 'element' may have changed */
6117 if (IS_CHANGING(x, y))
6120 ChangeElement(x, y, ChangePage[x][y] != -1 ? ChangePage[x][y] :
6121 element_info[element].event_page_nr[CE_DELAY]);
6123 ChangeElement(x, y, element_info[element].event_page_nr[CE_DELAY]);
6126 element = Feld[x][y];
6127 graphic = el_act_dir2img(element, GfxAction[x][y], MovDir[x][y]);
6131 if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
6136 graphic = el_act_dir2img(element, GfxAction[x][y], MovDir[x][y]);
6138 if (element == EL_MOLE)
6139 printf("::: %d, %d, %d [%d]\n",
6140 IS_ANIMATED(graphic), IS_MOVING(x, y), Stop[x][y],
6144 if (element == EL_YAMYAM)
6145 printf("::: %d, %d, %d\n",
6146 IS_ANIMATED(graphic), IS_MOVING(x, y), Stop[x][y]);
6150 if (IS_ANIMATED(graphic) &&
6154 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
6157 if (element == EL_MOLE)
6158 printf("::: %d, %d\n", graphic, GfxFrame[x][y]);
6162 if (IS_GEM(element) || element == EL_SP_INFOTRON)
6163 EdelsteinFunkeln(x, y);
6165 else if ((element == EL_ACID ||
6166 element == EL_EXIT_OPEN ||
6167 element == EL_SP_EXIT_OPEN ||
6168 element == EL_SP_TERMINAL ||
6169 element == EL_SP_TERMINAL_ACTIVE ||
6170 element == EL_EXTRA_TIME ||
6171 element == EL_SHIELD_NORMAL ||
6172 element == EL_SHIELD_DEADLY) &&
6173 IS_ANIMATED(graphic))
6174 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
6175 else if (IS_MOVING(x, y))
6176 ContinueMoving(x, y);
6177 else if (IS_ACTIVE_BOMB(element))
6178 CheckDynamite(x, y);
6180 else if (element == EL_EXPLOSION && !game.explosions_delayed)
6181 Explode(x, y, ExplodePhase[x][y], EX_NORMAL);
6183 else if (element == EL_AMOEBA_GROWING)
6184 AmoebeWaechst(x, y);
6185 else if (element == EL_AMOEBA_SHRINKING)
6186 AmoebaDisappearing(x, y);
6188 #if !USE_NEW_AMOEBA_CODE
6189 else if (IS_AMOEBALIVE(element))
6190 AmoebeAbleger(x, y);
6193 else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
6195 else if (element == EL_EXIT_CLOSED)
6197 else if (element == EL_SP_EXIT_CLOSED)
6199 else if (element == EL_EXPANDABLE_WALL_GROWING)
6201 else if (element == EL_EXPANDABLE_WALL ||
6202 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
6203 element == EL_EXPANDABLE_WALL_VERTICAL ||
6204 element == EL_EXPANDABLE_WALL_ANY)
6206 else if (element == EL_FLAMES)
6207 CheckForDragon(x, y);
6209 else if (IS_AUTO_CHANGING(element))
6210 ChangeElement(x, y);
6212 else if (element == EL_EXPLOSION)
6213 ; /* drawing of correct explosion animation is handled separately */
6214 else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
6215 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
6218 /* this may take place after moving, so 'element' may have changed */
6219 if (IS_AUTO_CHANGING(Feld[x][y]))
6220 ChangeElement(x, y);
6223 if (IS_BELT_ACTIVE(element))
6224 PlaySoundLevelAction(x, y, ACTION_ACTIVE);
6226 if (game.magic_wall_active)
6228 int jx = local_player->jx, jy = local_player->jy;
6230 /* play the element sound at the position nearest to the player */
6231 if ((element == EL_MAGIC_WALL_FULL ||
6232 element == EL_MAGIC_WALL_ACTIVE ||
6233 element == EL_MAGIC_WALL_EMPTYING ||
6234 element == EL_BD_MAGIC_WALL_FULL ||
6235 element == EL_BD_MAGIC_WALL_ACTIVE ||
6236 element == EL_BD_MAGIC_WALL_EMPTYING) &&
6237 ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
6245 #if USE_NEW_AMOEBA_CODE
6246 /* new experimental amoeba growth stuff */
6248 if (!(FrameCounter % 8))
6251 static unsigned long random = 1684108901;
6253 for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
6256 x = (random >> 10) % lev_fieldx;
6257 y = (random >> 20) % lev_fieldy;
6259 x = RND(lev_fieldx);
6260 y = RND(lev_fieldy);
6262 element = Feld[x][y];
6264 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
6265 if (!IS_PLAYER(x,y) &&
6266 (element == EL_EMPTY ||
6267 element == EL_SAND ||
6268 element == EL_QUICKSAND_EMPTY ||
6269 element == EL_ACID_SPLASH_LEFT ||
6270 element == EL_ACID_SPLASH_RIGHT))
6272 if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
6273 (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
6274 (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
6275 (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
6276 Feld[x][y] = EL_AMOEBA_DROP;
6279 random = random * 129 + 1;
6285 if (game.explosions_delayed)
6288 game.explosions_delayed = FALSE;
6290 for (y=0; y<lev_fieldy; y++) for (x=0; x<lev_fieldx; x++)
6292 element = Feld[x][y];
6294 if (ExplodeField[x][y])
6295 Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
6296 else if (element == EL_EXPLOSION)
6297 Explode(x, y, ExplodePhase[x][y], EX_NORMAL);
6299 ExplodeField[x][y] = EX_NO_EXPLOSION;
6302 game.explosions_delayed = TRUE;
6305 if (game.magic_wall_active)
6307 if (!(game.magic_wall_time_left % 4))
6309 int element = Feld[magic_wall_x][magic_wall_y];
6311 if (element == EL_BD_MAGIC_WALL_FULL ||
6312 element == EL_BD_MAGIC_WALL_ACTIVE ||
6313 element == EL_BD_MAGIC_WALL_EMPTYING)
6314 PlaySoundLevel(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
6316 PlaySoundLevel(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
6319 if (game.magic_wall_time_left > 0)
6321 game.magic_wall_time_left--;
6322 if (!game.magic_wall_time_left)
6324 for (y=0; y<lev_fieldy; y++) for (x=0; x<lev_fieldx; x++)
6326 element = Feld[x][y];
6328 if (element == EL_MAGIC_WALL_ACTIVE ||
6329 element == EL_MAGIC_WALL_FULL)
6331 Feld[x][y] = EL_MAGIC_WALL_DEAD;
6332 DrawLevelField(x, y);
6334 else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
6335 element == EL_BD_MAGIC_WALL_FULL)
6337 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
6338 DrawLevelField(x, y);
6342 game.magic_wall_active = FALSE;
6347 if (game.light_time_left > 0)
6349 game.light_time_left--;
6351 if (game.light_time_left == 0)
6352 RedrawAllLightSwitchesAndInvisibleElements();
6355 if (game.timegate_time_left > 0)
6357 game.timegate_time_left--;
6359 if (game.timegate_time_left == 0)
6360 CloseAllOpenTimegates();
6363 for (i=0; i<MAX_PLAYERS; i++)
6365 struct PlayerInfo *player = &stored_player[i];
6367 if (SHIELD_ON(player))
6369 if (player->shield_deadly_time_left)
6370 PlaySoundLevel(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
6371 else if (player->shield_normal_time_left)
6372 PlaySoundLevel(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
6376 if (TimeFrames >= FRAMES_PER_SECOND)
6381 for (i=0; i<MAX_PLAYERS; i++)
6383 struct PlayerInfo *player = &stored_player[i];
6385 if (SHIELD_ON(player))
6387 player->shield_normal_time_left--;
6389 if (player->shield_deadly_time_left > 0)
6390 player->shield_deadly_time_left--;
6394 if (tape.recording || tape.playing)
6395 DrawVideoDisplay(VIDEO_STATE_TIME_ON, TimePlayed);
6401 if (TimeLeft <= 10 && setup.time_limit)
6402 PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, SOUND_MIDDLE);
6404 DrawText(DX_TIME, DY_TIME, int2str(TimeLeft, 3), FONT_TEXT_2);
6406 if (!TimeLeft && setup.time_limit)
6407 for (i=0; i<MAX_PLAYERS; i++)
6408 KillHero(&stored_player[i]);
6410 else if (level.time == 0 && !AllPlayersGone) /* level without time limit */
6411 DrawText(DX_TIME, DY_TIME, int2str(TimePlayed, 3), FONT_TEXT_2);
6416 if (options.debug) /* calculate frames per second */
6418 static unsigned long fps_counter = 0;
6419 static int fps_frames = 0;
6420 unsigned long fps_delay_ms = Counter() - fps_counter;
6424 if (fps_delay_ms >= 500) /* calculate fps every 0.5 seconds */
6426 global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
6429 fps_counter = Counter();
6432 redraw_mask |= REDRAW_FPS;
6436 if (stored_player[0].jx != stored_player[0].last_jx ||
6437 stored_player[0].jy != stored_player[0].last_jy)
6438 printf("::: %d, %d, %d, %d, %d\n",
6439 stored_player[0].MovDir,
6440 stored_player[0].MovPos,
6441 stored_player[0].GfxPos,
6442 stored_player[0].Frame,
6443 stored_player[0].StepFrame);
6450 for (i=0; i<MAX_PLAYERS; i++)
6453 MOVE_DELAY_NORMAL_SPEED / stored_player[i].move_delay_value;
6455 stored_player[i].Frame += move_frames;
6457 if (stored_player[i].MovPos != 0)
6458 stored_player[i].StepFrame += move_frames;
6463 if (local_player->show_envelope != 0 && local_player->MovPos == 0)
6465 ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
6467 local_player->show_envelope = 0;
6472 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
6474 int min_x = x, min_y = y, max_x = x, max_y = y;
6477 for (i=0; i<MAX_PLAYERS; i++)
6479 int jx = stored_player[i].jx, jy = stored_player[i].jy;
6481 if (!stored_player[i].active || &stored_player[i] == player)
6484 min_x = MIN(min_x, jx);
6485 min_y = MIN(min_y, jy);
6486 max_x = MAX(max_x, jx);
6487 max_y = MAX(max_y, jy);
6490 return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
6493 static boolean AllPlayersInVisibleScreen()
6497 for (i=0; i<MAX_PLAYERS; i++)
6499 int jx = stored_player[i].jx, jy = stored_player[i].jy;
6501 if (!stored_player[i].active)
6504 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
6511 void ScrollLevel(int dx, int dy)
6513 int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
6516 BlitBitmap(drawto_field, drawto_field,
6517 FX + TILEX * (dx == -1) - softscroll_offset,
6518 FY + TILEY * (dy == -1) - softscroll_offset,
6519 SXSIZE - TILEX * (dx!=0) + 2 * softscroll_offset,
6520 SYSIZE - TILEY * (dy!=0) + 2 * softscroll_offset,
6521 FX + TILEX * (dx == 1) - softscroll_offset,
6522 FY + TILEY * (dy == 1) - softscroll_offset);
6526 x = (dx == 1 ? BX1 : BX2);
6527 for (y=BY1; y <= BY2; y++)
6528 DrawScreenField(x, y);
6533 y = (dy == 1 ? BY1 : BY2);
6534 for (x=BX1; x <= BX2; x++)
6535 DrawScreenField(x, y);
6538 redraw_mask |= REDRAW_FIELD;
6541 static void CheckGravityMovement(struct PlayerInfo *player)
6543 if (game.gravity && !player->programmed_action)
6545 int move_dir_vertical = player->action & (MV_UP | MV_DOWN);
6546 int move_dir_horizontal = player->action & (MV_LEFT | MV_RIGHT);
6548 (player->last_move_dir & (MV_LEFT | MV_RIGHT) ?
6549 (move_dir_vertical ? move_dir_vertical : move_dir_horizontal) :
6550 (move_dir_horizontal ? move_dir_horizontal : move_dir_vertical));
6551 int jx = player->jx, jy = player->jy;
6552 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
6553 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
6554 int new_jx = jx + dx, new_jy = jy + dy;
6555 boolean field_under_player_is_free =
6556 (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
6557 boolean player_is_moving_to_valid_field =
6558 (IN_LEV_FIELD(new_jx, new_jy) &&
6559 (Feld[new_jx][new_jy] == EL_SP_BASE ||
6560 Feld[new_jx][new_jy] == EL_SAND));
6561 /* !!! extend EL_SAND to anything diggable !!! */
6563 if (field_under_player_is_free &&
6564 !player_is_moving_to_valid_field &&
6565 !IS_WALKABLE_INSIDE(Feld[jx][jy]))
6566 player->programmed_action = MV_DOWN;
6572 -----------------------------------------------------------------------------
6573 dx, dy: direction (non-diagonal) to try to move the player to
6574 real_dx, real_dy: direction as read from input device (can be diagonal)
6577 boolean MovePlayerOneStep(struct PlayerInfo *player,
6578 int dx, int dy, int real_dx, int real_dy)
6581 static int change_sides[4][2] =
6583 /* enter side leave side */
6584 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* moving left */
6585 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* moving right */
6586 { CH_SIDE_BOTTOM, CH_SIDE_TOP }, /* moving up */
6587 { CH_SIDE_TOP, CH_SIDE_BOTTOM } /* moving down */
6589 int move_direction = (dx == -1 ? MV_LEFT :
6590 dx == +1 ? MV_RIGHT :
6592 dy == +1 ? MV_DOWN : MV_NO_MOVING);
6593 int enter_side = change_sides[MV_DIR_BIT(move_direction)][0];
6594 int leave_side = change_sides[MV_DIR_BIT(move_direction)][1];
6596 int jx = player->jx, jy = player->jy;
6597 int new_jx = jx + dx, new_jy = jy + dy;
6601 if (!player->active || (!dx && !dy))
6602 return MF_NO_ACTION;
6604 player->MovDir = (dx < 0 ? MV_LEFT :
6607 dy > 0 ? MV_DOWN : MV_NO_MOVING);
6609 if (!IN_LEV_FIELD(new_jx, new_jy))
6610 return MF_NO_ACTION;
6612 if (!options.network && !AllPlayersInSight(player, new_jx, new_jy))
6613 return MF_NO_ACTION;
6616 element = MovingOrBlocked2Element(new_jx, new_jy);
6618 element = MovingOrBlocked2ElementIfNotLeaving(new_jx, new_jy);
6621 if (DONT_RUN_INTO(element))
6623 if (element == EL_ACID && dx == 0 && dy == 1)
6626 Feld[jx][jy] = EL_PLAYER_1;
6627 InitMovingField(jx, jy, MV_DOWN);
6628 Store[jx][jy] = EL_ACID;
6629 ContinueMoving(jx, jy);
6633 TestIfHeroRunsIntoBadThing(jx, jy, player->MovDir);
6638 can_move = DigField(player, new_jx, new_jy, real_dx, real_dy, DF_DIG);
6639 if (can_move != MF_MOVING)
6642 /* check if DigField() has caused relocation of the player */
6643 if (player->jx != jx || player->jy != jy)
6644 return MF_NO_ACTION;
6646 StorePlayer[jx][jy] = 0;
6647 player->last_jx = jx;
6648 player->last_jy = jy;
6649 player->jx = new_jx;
6650 player->jy = new_jy;
6651 StorePlayer[new_jx][new_jy] = player->element_nr;
6654 (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
6656 ScrollPlayer(player, SCROLL_INIT);
6659 if (IS_CUSTOM_ELEMENT(Feld[jx][jy]))
6661 CheckTriggeredElementSideChange(jx, jy, Feld[jx][jy], leave_side,
6662 CE_OTHER_GETS_LEFT);
6663 CheckElementSideChange(jx, jy, Feld[jx][jy], leave_side,
6664 CE_LEFT_BY_PLAYER, -1);
6667 if (IS_CUSTOM_ELEMENT(Feld[new_jx][new_jy]))
6669 CheckTriggeredElementSideChange(new_jx, new_jy, Feld[new_jx][new_jy],
6670 enter_side, CE_OTHER_GETS_ENTERED);
6671 CheckElementSideChange(new_jx, new_jy, Feld[new_jx][new_jy], enter_side,
6672 CE_ENTERED_BY_PLAYER, -1);
6679 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
6681 int jx = player->jx, jy = player->jy;
6682 int old_jx = jx, old_jy = jy;
6683 int moved = MF_NO_ACTION;
6685 if (!player->active || (!dx && !dy))
6689 if (!FrameReached(&player->move_delay, player->move_delay_value) &&
6693 if (!FrameReached(&player->move_delay, player->move_delay_value) &&
6694 !(tape.playing && tape.file_version < FILE_VERSION_2_0))
6698 /* remove the last programmed player action */
6699 player->programmed_action = 0;
6703 /* should only happen if pre-1.2 tape recordings are played */
6704 /* this is only for backward compatibility */
6706 int original_move_delay_value = player->move_delay_value;
6709 printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%ld]\n",
6713 /* scroll remaining steps with finest movement resolution */
6714 player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
6716 while (player->MovPos)
6718 ScrollPlayer(player, SCROLL_GO_ON);
6719 ScrollScreen(NULL, SCROLL_GO_ON);
6725 player->move_delay_value = original_move_delay_value;
6728 if (player->last_move_dir & (MV_LEFT | MV_RIGHT))
6730 if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
6731 moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
6735 if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
6736 moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
6742 if (moved & MF_MOVING && !ScreenMovPos &&
6743 (player == local_player || !options.network))
6745 int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
6746 int offset = (setup.scroll_delay ? 3 : 0);
6748 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
6750 /* actual player has left the screen -- scroll in that direction */
6751 if (jx != old_jx) /* player has moved horizontally */
6752 scroll_x += (jx - old_jx);
6753 else /* player has moved vertically */
6754 scroll_y += (jy - old_jy);
6758 if (jx != old_jx) /* player has moved horizontally */
6760 if ((player->MovDir == MV_LEFT && scroll_x > jx - MIDPOSX + offset) ||
6761 (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
6762 scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
6764 /* don't scroll over playfield boundaries */
6765 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
6766 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
6768 /* don't scroll more than one field at a time */
6769 scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
6771 /* don't scroll against the player's moving direction */
6772 if ((player->MovDir == MV_LEFT && scroll_x > old_scroll_x) ||
6773 (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
6774 scroll_x = old_scroll_x;
6776 else /* player has moved vertically */
6778 if ((player->MovDir == MV_UP && scroll_y > jy - MIDPOSY + offset) ||
6779 (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
6780 scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
6782 /* don't scroll over playfield boundaries */
6783 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
6784 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
6786 /* don't scroll more than one field at a time */
6787 scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
6789 /* don't scroll against the player's moving direction */
6790 if ((player->MovDir == MV_UP && scroll_y > old_scroll_y) ||
6791 (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
6792 scroll_y = old_scroll_y;
6796 if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
6798 if (!options.network && !AllPlayersInVisibleScreen())
6800 scroll_x = old_scroll_x;
6801 scroll_y = old_scroll_y;
6805 ScrollScreen(player, SCROLL_INIT);
6806 ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
6813 InitPlayerGfxAnimation(player, ACTION_DEFAULT);
6815 if (!(moved & MF_MOVING) && !player->is_pushing)
6820 player->StepFrame = 0;
6822 if (moved & MF_MOVING)
6824 if (old_jx != jx && old_jy == jy)
6825 player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
6826 else if (old_jx == jx && old_jy != jy)
6827 player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
6829 DrawLevelField(jx, jy); /* for "crumbled sand" */
6831 player->last_move_dir = player->MovDir;
6832 player->is_moving = TRUE;
6834 player->is_snapping = FALSE;
6838 player->is_switching = FALSE;
6844 static int change_sides[4][2] =
6846 /* enter side leave side */
6847 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* moving left */
6848 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* moving right */
6849 { CH_SIDE_BOTTOM, CH_SIDE_TOP }, /* moving up */
6850 { CH_SIDE_TOP, CH_SIDE_BOTTOM } /* moving down */
6852 int move_direction = player->MovDir;
6853 int enter_side = change_sides[MV_DIR_BIT(move_direction)][0];
6854 int leave_side = change_sides[MV_DIR_BIT(move_direction)][1];
6857 if (IS_CUSTOM_ELEMENT(Feld[old_jx][old_jy]))
6859 CheckTriggeredElementSideChange(old_jx, old_jy, Feld[old_jx][old_jy],
6860 leave_side, CE_OTHER_GETS_LEFT);
6861 CheckElementSideChange(old_jx, old_jy, Feld[old_jx][old_jy],
6862 leave_side, CE_LEFT_BY_PLAYER, -1);
6865 if (IS_CUSTOM_ELEMENT(Feld[jx][jy]))
6867 CheckTriggeredElementSideChange(jx, jy, Feld[jx][jy],
6868 enter_side, CE_OTHER_GETS_ENTERED);
6869 CheckElementSideChange(jx, jy, Feld[jx][jy],
6870 enter_side, CE_ENTERED_BY_PLAYER, -1);
6881 CheckGravityMovement(player);
6884 player->last_move_dir = MV_NO_MOVING;
6886 player->is_moving = FALSE;
6889 if (game.engine_version < VERSION_IDENT(3,0,7))
6891 TestIfHeroTouchesBadThing(jx, jy);
6892 TestIfPlayerTouchesCustomElement(jx, jy);
6895 if (!player->active)
6901 void ScrollPlayer(struct PlayerInfo *player, int mode)
6903 int jx = player->jx, jy = player->jy;
6904 int last_jx = player->last_jx, last_jy = player->last_jy;
6905 int move_stepsize = TILEX / player->move_delay_value;
6907 if (!player->active || !player->MovPos)
6910 if (mode == SCROLL_INIT)
6912 player->actual_frame_counter = FrameCounter;
6913 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
6915 if (Feld[last_jx][last_jy] == EL_EMPTY)
6916 Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
6923 else if (!FrameReached(&player->actual_frame_counter, 1))
6926 player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
6927 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
6929 if (Feld[last_jx][last_jy] == EL_PLAYER_IS_LEAVING)
6930 Feld[last_jx][last_jy] = EL_EMPTY;
6932 /* before DrawPlayer() to draw correct player graphic for this case */
6933 if (player->MovPos == 0)
6934 CheckGravityMovement(player);
6937 DrawPlayer(player); /* needed here only to cleanup last field */
6940 if (player->MovPos == 0) /* player reached destination field */
6942 if (IS_PASSABLE(Feld[last_jx][last_jy]))
6944 /* continue with normal speed after quickly moving through gate */
6945 HALVE_PLAYER_SPEED(player);
6947 /* be able to make the next move without delay */
6948 player->move_delay = 0;
6951 player->last_jx = jx;
6952 player->last_jy = jy;
6954 if (Feld[jx][jy] == EL_EXIT_OPEN ||
6955 Feld[jx][jy] == EL_SP_EXIT_OPEN ||
6956 Feld[jx][jy] == EL_SP_EXIT_OPENING) /* <-- special case */
6958 DrawPlayer(player); /* needed here only to cleanup last field */
6961 if (local_player->friends_still_needed == 0 ||
6962 IS_SP_ELEMENT(Feld[jx][jy]))
6963 player->LevelSolved = player->GameOver = TRUE;
6966 if (game.engine_version >= VERSION_IDENT(3,0,7))
6968 TestIfHeroTouchesBadThing(jx, jy);
6969 TestIfPlayerTouchesCustomElement(jx, jy);
6971 TestIfElementTouchesCustomElement(jx, jy); /* for empty space */
6974 if (!player->active)
6978 if (tape.single_step && tape.recording && !tape.pausing &&
6979 !player->programmed_action)
6980 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
6984 void ScrollScreen(struct PlayerInfo *player, int mode)
6986 static unsigned long screen_frame_counter = 0;
6988 if (mode == SCROLL_INIT)
6990 /* set scrolling step size according to actual player's moving speed */
6991 ScrollStepSize = TILEX / player->move_delay_value;
6993 screen_frame_counter = FrameCounter;
6994 ScreenMovDir = player->MovDir;
6995 ScreenMovPos = player->MovPos;
6996 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
6999 else if (!FrameReached(&screen_frame_counter, 1))
7004 ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
7005 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
7006 redraw_mask |= REDRAW_FIELD;
7009 ScreenMovDir = MV_NO_MOVING;
7012 void TestIfPlayerTouchesCustomElement(int x, int y)
7014 static int xy[4][2] =
7021 static int change_sides[4][2] =
7023 /* center side border side */
7024 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
7025 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
7026 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
7027 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
7029 static int touch_dir[4] =
7036 int center_element = Feld[x][y]; /* should always be non-moving! */
7041 int xx = x + xy[i][0];
7042 int yy = y + xy[i][1];
7043 int center_side = change_sides[i][0];
7044 int border_side = change_sides[i][1];
7047 if (!IN_LEV_FIELD(xx, yy))
7050 if (IS_PLAYER(x, y))
7052 if (game.engine_version < VERSION_IDENT(3,0,7))
7053 border_element = Feld[xx][yy]; /* may be moving! */
7054 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
7055 border_element = Feld[xx][yy];
7056 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
7057 border_element = MovingOrBlocked2Element(xx, yy);
7059 continue; /* center and border element do not touch */
7061 CheckTriggeredElementSideChange(xx, yy, border_element, border_side,
7062 CE_OTHER_GETS_TOUCHED);
7063 CheckElementSideChange(xx, yy, border_element, border_side,
7064 CE_TOUCHED_BY_PLAYER, -1);
7066 else if (IS_PLAYER(xx, yy))
7068 if (game.engine_version >= VERSION_IDENT(3,0,7))
7070 struct PlayerInfo *player = PLAYERINFO(xx, yy);
7072 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
7073 continue; /* center and border element do not touch */
7076 CheckTriggeredElementSideChange(x, y, center_element, center_side,
7077 CE_OTHER_GETS_TOUCHED);
7078 CheckElementSideChange(x, y, center_element, center_side,
7079 CE_TOUCHED_BY_PLAYER, -1);
7086 void TestIfElementTouchesCustomElement(int x, int y)
7088 static int xy[4][2] =
7095 static int change_sides[4][2] =
7097 /* center side border side */
7098 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
7099 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
7100 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
7101 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
7103 static int touch_dir[4] =
7110 boolean change_center_element = FALSE;
7111 int center_element_change_page = 0;
7112 int center_element = Feld[x][y]; /* should always be non-moving! */
7117 int xx = x + xy[i][0];
7118 int yy = y + xy[i][1];
7119 int center_side = change_sides[i][0];
7120 int border_side = change_sides[i][1];
7123 if (!IN_LEV_FIELD(xx, yy))
7126 if (game.engine_version < VERSION_IDENT(3,0,7))
7127 border_element = Feld[xx][yy]; /* may be moving! */
7128 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
7129 border_element = Feld[xx][yy];
7130 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
7131 border_element = MovingOrBlocked2Element(xx, yy);
7133 continue; /* center and border element do not touch */
7135 /* check for change of center element (but change it only once) */
7136 if (IS_CUSTOM_ELEMENT(center_element) &&
7137 HAS_ANY_CHANGE_EVENT(center_element, CE_OTHER_IS_TOUCHING) &&
7138 !change_center_element)
7140 for (j=0; j < element_info[center_element].num_change_pages; j++)
7142 struct ElementChangeInfo *change =
7143 &element_info[center_element].change_page[j];
7145 if (change->can_change &&
7146 change->events & CH_EVENT_BIT(CE_OTHER_IS_TOUCHING) &&
7147 change->sides & border_side &&
7148 change->trigger_element == border_element)
7150 change_center_element = TRUE;
7151 center_element_change_page = j;
7158 /* check for change of border element */
7159 if (IS_CUSTOM_ELEMENT(border_element) &&
7160 HAS_ANY_CHANGE_EVENT(border_element, CE_OTHER_IS_TOUCHING))
7162 for (j=0; j < element_info[border_element].num_change_pages; j++)
7164 struct ElementChangeInfo *change =
7165 &element_info[border_element].change_page[j];
7167 if (change->can_change &&
7168 change->events & CH_EVENT_BIT(CE_OTHER_IS_TOUCHING) &&
7169 change->sides & center_side &&
7170 change->trigger_element == center_element)
7172 CheckElementSideChange(xx, yy, border_element, CH_SIDE_ANY,
7173 CE_OTHER_IS_TOUCHING, j);
7180 if (change_center_element)
7181 CheckElementSideChange(x, y, center_element, CH_SIDE_ANY,
7182 CE_OTHER_IS_TOUCHING, center_element_change_page);
7185 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
7187 int i, kill_x = -1, kill_y = -1;
7188 static int test_xy[4][2] =
7195 static int test_dir[4] =
7205 int test_x, test_y, test_move_dir, test_element;
7207 test_x = good_x + test_xy[i][0];
7208 test_y = good_y + test_xy[i][1];
7209 if (!IN_LEV_FIELD(test_x, test_y))
7213 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NO_MOVING);
7216 test_element = Feld[test_x][test_y];
7218 test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
7221 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
7222 2nd case: DONT_TOUCH style bad thing does not move away from good thing
7224 if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
7225 (DONT_TOUCH(test_element) && test_move_dir != test_dir[i]))
7233 if (kill_x != -1 || kill_y != -1)
7235 if (IS_PLAYER(good_x, good_y))
7237 struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
7239 if (player->shield_deadly_time_left > 0)
7240 Bang(kill_x, kill_y);
7241 else if (!PLAYER_PROTECTED(good_x, good_y))
7245 Bang(good_x, good_y);
7249 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
7251 int i, kill_x = -1, kill_y = -1;
7252 int bad_element = Feld[bad_x][bad_y];
7253 static int test_xy[4][2] =
7260 static int touch_dir[4] =
7267 static int test_dir[4] =
7275 if (bad_element == EL_EXPLOSION) /* skip just exploding bad things */
7280 int test_x, test_y, test_move_dir, test_element;
7282 test_x = bad_x + test_xy[i][0];
7283 test_y = bad_y + test_xy[i][1];
7284 if (!IN_LEV_FIELD(test_x, test_y))
7288 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NO_MOVING);
7290 test_element = Feld[test_x][test_y];
7292 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
7293 2nd case: DONT_TOUCH style bad thing does not move away from good thing
7295 if ((DONT_RUN_INTO(bad_element) && bad_move_dir == test_dir[i]) ||
7296 (DONT_TOUCH(bad_element) && test_move_dir != test_dir[i]))
7298 /* good thing is player or penguin that does not move away */
7299 if (IS_PLAYER(test_x, test_y))
7301 struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
7303 if (bad_element == EL_ROBOT && player->is_moving)
7304 continue; /* robot does not kill player if he is moving */
7306 if (game.engine_version >= VERSION_IDENT(3,0,7))
7308 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
7309 continue; /* center and border element do not touch */
7316 else if (test_element == EL_PENGUIN)
7325 if (kill_x != -1 || kill_y != -1)
7327 if (IS_PLAYER(kill_x, kill_y))
7329 struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
7331 if (player->shield_deadly_time_left > 0)
7333 else if (!PLAYER_PROTECTED(kill_x, kill_y))
7337 Bang(kill_x, kill_y);
7341 void TestIfHeroTouchesBadThing(int x, int y)
7343 TestIfGoodThingHitsBadThing(x, y, MV_NO_MOVING);
7346 void TestIfHeroRunsIntoBadThing(int x, int y, int move_dir)
7348 TestIfGoodThingHitsBadThing(x, y, move_dir);
7351 void TestIfBadThingTouchesHero(int x, int y)
7353 TestIfBadThingHitsGoodThing(x, y, MV_NO_MOVING);
7356 void TestIfBadThingRunsIntoHero(int x, int y, int move_dir)
7358 TestIfBadThingHitsGoodThing(x, y, move_dir);
7361 void TestIfFriendTouchesBadThing(int x, int y)
7363 TestIfGoodThingHitsBadThing(x, y, MV_NO_MOVING);
7366 void TestIfBadThingTouchesFriend(int x, int y)
7368 TestIfBadThingHitsGoodThing(x, y, MV_NO_MOVING);
7371 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
7373 int i, kill_x = bad_x, kill_y = bad_y;
7374 static int xy[4][2] =
7386 x = bad_x + xy[i][0];
7387 y = bad_y + xy[i][1];
7388 if (!IN_LEV_FIELD(x, y))
7391 element = Feld[x][y];
7392 if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
7393 element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
7401 if (kill_x != bad_x || kill_y != bad_y)
7405 void KillHero(struct PlayerInfo *player)
7407 int jx = player->jx, jy = player->jy;
7409 if (!player->active)
7412 /* remove accessible field at the player's position */
7413 Feld[jx][jy] = EL_EMPTY;
7415 /* deactivate shield (else Bang()/Explode() would not work right) */
7416 player->shield_normal_time_left = 0;
7417 player->shield_deadly_time_left = 0;
7423 static void KillHeroUnlessProtected(int x, int y)
7425 if (!PLAYER_PROTECTED(x, y))
7426 KillHero(PLAYERINFO(x, y));
7429 void BuryHero(struct PlayerInfo *player)
7431 int jx = player->jx, jy = player->jy;
7433 if (!player->active)
7437 PlaySoundLevelElementAction(jx, jy, player->element_nr, ACTION_DYING);
7439 PlaySoundLevel(jx, jy, SND_CLASS_PLAYER_DYING);
7441 PlaySoundLevel(jx, jy, SND_GAME_LOSING);
7443 player->GameOver = TRUE;
7447 void RemoveHero(struct PlayerInfo *player)
7449 int jx = player->jx, jy = player->jy;
7450 int i, found = FALSE;
7452 player->present = FALSE;
7453 player->active = FALSE;
7455 if (!ExplodeField[jx][jy])
7456 StorePlayer[jx][jy] = 0;
7458 for (i=0; i<MAX_PLAYERS; i++)
7459 if (stored_player[i].active)
7463 AllPlayersGone = TRUE;
7470 =============================================================================
7471 checkDiagonalPushing()
7472 -----------------------------------------------------------------------------
7473 check if diagonal input device direction results in pushing of object
7474 (by checking if the alternative direction is walkable, diggable, ...)
7475 =============================================================================
7478 static boolean checkDiagonalPushing(struct PlayerInfo *player,
7479 int x, int y, int real_dx, int real_dy)
7481 int jx, jy, dx, dy, xx, yy;
7483 if (real_dx == 0 || real_dy == 0) /* no diagonal direction => push */
7486 /* diagonal direction: check alternative direction */
7491 xx = jx + (dx == 0 ? real_dx : 0);
7492 yy = jy + (dy == 0 ? real_dy : 0);
7494 return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
7498 =============================================================================
7500 -----------------------------------------------------------------------------
7501 x, y: field next to player (non-diagonal) to try to dig to
7502 real_dx, real_dy: direction as read from input device (can be diagonal)
7503 =============================================================================
7506 int DigField(struct PlayerInfo *player,
7507 int x, int y, int real_dx, int real_dy, int mode)
7509 static int change_sides[4] =
7511 CH_SIDE_RIGHT, /* moving left */
7512 CH_SIDE_LEFT, /* moving right */
7513 CH_SIDE_BOTTOM, /* moving up */
7514 CH_SIDE_TOP, /* moving down */
7516 boolean use_spring_bug = (game.engine_version < VERSION_IDENT(2,2,0));
7517 int jx = player->jx, jy = player->jy;
7518 int dx = x - jx, dy = y - jy;
7519 int nextx = x + dx, nexty = y + dy;
7520 int move_direction = (dx == -1 ? MV_LEFT :
7521 dx == +1 ? MV_RIGHT :
7523 dy == +1 ? MV_DOWN : MV_NO_MOVING);
7524 int dig_side = change_sides[MV_DIR_BIT(move_direction)];
7527 if (player->MovPos == 0)
7529 player->is_digging = FALSE;
7530 player->is_collecting = FALSE;
7533 if (player->MovPos == 0) /* last pushing move finished */
7534 player->is_pushing = FALSE;
7536 if (mode == DF_NO_PUSH) /* player just stopped pushing */
7538 player->is_switching = FALSE;
7539 player->push_delay = 0;
7541 return MF_NO_ACTION;
7544 if (IS_MOVING(x, y) || IS_PLAYER(x, y))
7545 return MF_NO_ACTION;
7548 if (IS_TUBE(Feld[jx][jy]) || IS_TUBE(Back[jx][jy]))
7550 if (IS_TUBE(Feld[jx][jy]) ||
7551 (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0)))
7555 int tube_element = (IS_TUBE(Feld[jx][jy]) ? Feld[jx][jy] : Back[jx][jy]);
7556 int tube_leave_directions[][2] =
7558 { EL_TUBE_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
7559 { EL_TUBE_VERTICAL, MV_UP | MV_DOWN },
7560 { EL_TUBE_HORIZONTAL, MV_LEFT | MV_RIGHT },
7561 { EL_TUBE_VERTICAL_LEFT, MV_LEFT | MV_UP | MV_DOWN },
7562 { EL_TUBE_VERTICAL_RIGHT, MV_RIGHT | MV_UP | MV_DOWN },
7563 { EL_TUBE_HORIZONTAL_UP, MV_LEFT | MV_RIGHT | MV_UP },
7564 { EL_TUBE_HORIZONTAL_DOWN, MV_LEFT | MV_RIGHT | MV_DOWN },
7565 { EL_TUBE_LEFT_UP, MV_LEFT | MV_UP },
7566 { EL_TUBE_LEFT_DOWN, MV_LEFT | MV_DOWN },
7567 { EL_TUBE_RIGHT_UP, MV_RIGHT | MV_UP },
7568 { EL_TUBE_RIGHT_DOWN, MV_RIGHT | MV_DOWN },
7569 { -1, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN }
7572 while (tube_leave_directions[i][0] != tube_element)
7575 if (tube_leave_directions[i][0] == -1) /* should not happen */
7579 if (!(tube_leave_directions[i][1] & move_direction))
7580 return MF_NO_ACTION; /* tube has no opening in this direction */
7583 element = Feld[x][y];
7585 if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
7586 game.engine_version >= VERSION_IDENT(2,2,0))
7587 return MF_NO_ACTION;
7591 case EL_SP_PORT_LEFT:
7592 case EL_SP_PORT_RIGHT:
7594 case EL_SP_PORT_DOWN:
7595 case EL_SP_PORT_HORIZONTAL:
7596 case EL_SP_PORT_VERTICAL:
7597 case EL_SP_PORT_ANY:
7598 case EL_SP_GRAVITY_PORT_LEFT:
7599 case EL_SP_GRAVITY_PORT_RIGHT:
7600 case EL_SP_GRAVITY_PORT_UP:
7601 case EL_SP_GRAVITY_PORT_DOWN:
7603 element != EL_SP_PORT_LEFT &&
7604 element != EL_SP_GRAVITY_PORT_LEFT &&
7605 element != EL_SP_PORT_HORIZONTAL &&
7606 element != EL_SP_PORT_ANY) ||
7608 element != EL_SP_PORT_RIGHT &&
7609 element != EL_SP_GRAVITY_PORT_RIGHT &&
7610 element != EL_SP_PORT_HORIZONTAL &&
7611 element != EL_SP_PORT_ANY) ||
7613 element != EL_SP_PORT_UP &&
7614 element != EL_SP_GRAVITY_PORT_UP &&
7615 element != EL_SP_PORT_VERTICAL &&
7616 element != EL_SP_PORT_ANY) ||
7618 element != EL_SP_PORT_DOWN &&
7619 element != EL_SP_GRAVITY_PORT_DOWN &&
7620 element != EL_SP_PORT_VERTICAL &&
7621 element != EL_SP_PORT_ANY) ||
7622 !IN_LEV_FIELD(nextx, nexty) ||
7623 !IS_FREE(nextx, nexty))
7624 return MF_NO_ACTION;
7626 if (element == EL_SP_GRAVITY_PORT_LEFT ||
7627 element == EL_SP_GRAVITY_PORT_RIGHT ||
7628 element == EL_SP_GRAVITY_PORT_UP ||
7629 element == EL_SP_GRAVITY_PORT_DOWN)
7630 game.gravity = !game.gravity;
7632 /* automatically move to the next field with double speed */
7633 player->programmed_action = move_direction;
7634 DOUBLE_PLAYER_SPEED(player);
7636 PlaySoundLevel(x, y, SND_CLASS_SP_PORT_PASSING);
7640 case EL_TUBE_VERTICAL:
7641 case EL_TUBE_HORIZONTAL:
7642 case EL_TUBE_VERTICAL_LEFT:
7643 case EL_TUBE_VERTICAL_RIGHT:
7644 case EL_TUBE_HORIZONTAL_UP:
7645 case EL_TUBE_HORIZONTAL_DOWN:
7646 case EL_TUBE_LEFT_UP:
7647 case EL_TUBE_LEFT_DOWN:
7648 case EL_TUBE_RIGHT_UP:
7649 case EL_TUBE_RIGHT_DOWN:
7652 int tube_enter_directions[][2] =
7654 { EL_TUBE_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
7655 { EL_TUBE_VERTICAL, MV_UP | MV_DOWN },
7656 { EL_TUBE_HORIZONTAL, MV_LEFT | MV_RIGHT },
7657 { EL_TUBE_VERTICAL_LEFT, MV_RIGHT | MV_UP | MV_DOWN },
7658 { EL_TUBE_VERTICAL_RIGHT, MV_LEFT | MV_UP | MV_DOWN },
7659 { EL_TUBE_HORIZONTAL_UP, MV_LEFT | MV_RIGHT | MV_DOWN },
7660 { EL_TUBE_HORIZONTAL_DOWN, MV_LEFT | MV_RIGHT | MV_UP },
7661 { EL_TUBE_LEFT_UP, MV_RIGHT | MV_DOWN },
7662 { EL_TUBE_LEFT_DOWN, MV_RIGHT | MV_UP },
7663 { EL_TUBE_RIGHT_UP, MV_LEFT | MV_DOWN },
7664 { EL_TUBE_RIGHT_DOWN, MV_LEFT | MV_UP },
7665 { -1, MV_NO_MOVING }
7668 while (tube_enter_directions[i][0] != element)
7671 if (tube_enter_directions[i][0] == -1) /* should not happen */
7675 if (!(tube_enter_directions[i][1] & move_direction))
7676 return MF_NO_ACTION; /* tube has no opening in this direction */
7678 PlaySoundLevel(x, y, SND_CLASS_TUBE_WALKING);
7684 if (IS_WALKABLE(element))
7686 int sound_action = ACTION_WALKING;
7688 if (element >= EL_GATE_1 && element <= EL_GATE_4)
7690 if (!player->key[element - EL_GATE_1])
7691 return MF_NO_ACTION;
7693 else if (element >= EL_GATE_1_GRAY && element <= EL_GATE_4_GRAY)
7695 if (!player->key[element - EL_GATE_1_GRAY])
7696 return MF_NO_ACTION;
7698 else if (element == EL_EXIT_OPEN ||
7699 element == EL_SP_EXIT_OPEN ||
7700 element == EL_SP_EXIT_OPENING)
7702 sound_action = ACTION_PASSING; /* player is passing exit */
7704 else if (element == EL_EMPTY)
7706 sound_action = ACTION_MOVING; /* nothing to walk on */
7709 /* play sound from background or player, whatever is available */
7710 if (element_info[element].sound[sound_action] != SND_UNDEFINED)
7711 PlaySoundLevelElementAction(x, y, element, sound_action);
7713 PlaySoundLevelElementAction(x, y, player->element_nr, sound_action);
7717 else if (IS_PASSABLE(element))
7719 if (!IN_LEV_FIELD(nextx, nexty) || !IS_FREE(nextx, nexty))
7720 return MF_NO_ACTION;
7723 if (CAN_MOVE(element)) /* only fixed elements can be passed! */
7724 return MF_NO_ACTION;
7727 if (element >= EL_EM_GATE_1 && element <= EL_EM_GATE_4)
7729 if (!player->key[element - EL_EM_GATE_1])
7730 return MF_NO_ACTION;
7732 else if (element >= EL_EM_GATE_1_GRAY && element <= EL_EM_GATE_4_GRAY)
7734 if (!player->key[element - EL_EM_GATE_1_GRAY])
7735 return MF_NO_ACTION;
7738 /* automatically move to the next field with double speed */
7739 player->programmed_action = move_direction;
7740 DOUBLE_PLAYER_SPEED(player);
7742 PlaySoundLevelAction(x, y, ACTION_PASSING);
7746 else if (IS_DIGGABLE(element))
7750 if (mode != DF_SNAP)
7753 GfxElement[x][y] = GFX_ELEMENT(element);
7756 (GFX_CRUMBLED(element) ? EL_SAND : GFX_ELEMENT(element));
7758 player->is_digging = TRUE;
7761 PlaySoundLevelElementAction(x, y, element, ACTION_DIGGING);
7763 CheckTriggeredElementChange(x, y, element, CE_OTHER_GETS_DIGGED);
7766 if (mode == DF_SNAP)
7767 TestIfElementTouchesCustomElement(x, y); /* for empty space */
7772 else if (IS_COLLECTIBLE(element))
7776 if (mode != DF_SNAP)
7778 GfxElement[x][y] = element;
7779 player->is_collecting = TRUE;
7782 if (element == EL_SPEED_PILL)
7783 player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
7784 else if (element == EL_EXTRA_TIME && level.time > 0)
7787 DrawText(DX_TIME, DY_TIME, int2str(TimeLeft, 3), FONT_TEXT_2);
7789 else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
7791 player->shield_normal_time_left += 10;
7792 if (element == EL_SHIELD_DEADLY)
7793 player->shield_deadly_time_left += 10;
7795 else if (element == EL_DYNAMITE || element == EL_SP_DISK_RED)
7797 if (player->inventory_size < MAX_INVENTORY_SIZE)
7798 player->inventory_element[player->inventory_size++] = element;
7800 DrawText(DX_DYNAMITE, DY_DYNAMITE,
7801 int2str(local_player->inventory_size, 3), FONT_TEXT_2);
7803 else if (element == EL_DYNABOMB_INCREASE_NUMBER)
7805 player->dynabomb_count++;
7806 player->dynabombs_left++;
7808 else if (element == EL_DYNABOMB_INCREASE_SIZE)
7810 player->dynabomb_size++;
7812 else if (element == EL_DYNABOMB_INCREASE_POWER)
7814 player->dynabomb_xl = TRUE;
7816 else if ((element >= EL_KEY_1 && element <= EL_KEY_4) ||
7817 (element >= EL_EM_KEY_1 && element <= EL_EM_KEY_4))
7819 int key_nr = (element >= EL_KEY_1 && element <= EL_KEY_4 ?
7820 element - EL_KEY_1 : element - EL_EM_KEY_1);
7822 player->key[key_nr] = TRUE;
7824 DrawMiniGraphicExt(drawto, DX_KEYS + key_nr * MINI_TILEX, DY_KEYS,
7825 el2edimg(EL_KEY_1 + key_nr));
7826 redraw_mask |= REDRAW_DOOR_1;
7828 else if (IS_ENVELOPE(element))
7831 player->show_envelope = element;
7833 ShowEnvelope(element - EL_ENVELOPE_1);
7836 else if (IS_DROPPABLE(element)) /* can be collected and dropped */
7840 for (i=0; i < element_info[element].collect_count; i++)
7841 if (player->inventory_size < MAX_INVENTORY_SIZE)
7842 player->inventory_element[player->inventory_size++] = element;
7844 DrawText(DX_DYNAMITE, DY_DYNAMITE,
7845 int2str(local_player->inventory_size, 3), FONT_TEXT_2);
7847 else if (element_info[element].collect_count > 0)
7849 local_player->gems_still_needed -=
7850 element_info[element].collect_count;
7851 if (local_player->gems_still_needed < 0)
7852 local_player->gems_still_needed = 0;
7854 DrawText(DX_EMERALDS, DY_EMERALDS,
7855 int2str(local_player->gems_still_needed, 3), FONT_TEXT_2);
7858 RaiseScoreElement(element);
7859 PlaySoundLevelElementAction(x, y, element, ACTION_COLLECTING);
7861 CheckTriggeredElementChange(x, y, element, CE_OTHER_GETS_COLLECTED);
7864 if (mode == DF_SNAP)
7865 TestIfElementTouchesCustomElement(x, y); /* for empty space */
7870 else if (IS_PUSHABLE(element))
7872 if (mode == DF_SNAP && element != EL_BD_ROCK)
7873 return MF_NO_ACTION;
7875 if (CAN_FALL(element) && dy)
7876 return MF_NO_ACTION;
7878 if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
7879 !(element == EL_SPRING && use_spring_bug))
7880 return MF_NO_ACTION;
7883 /* do not push elements already moving away faster than player */
7884 if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
7885 ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
7886 return MF_NO_ACTION;
7888 if (element == EL_SPRING && MovDir[x][y] != MV_NO_MOVING)
7889 return MF_NO_ACTION;
7891 if (!player->is_pushing &&
7892 game.engine_version >= RELEASE_IDENT(2,2,0,7))
7893 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
7895 player->is_pushing = TRUE;
7897 if (!(IN_LEV_FIELD(nextx, nexty) &&
7898 (IS_FREE(nextx, nexty) ||
7899 (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY &&
7900 IS_SB_ELEMENT(element)))))
7901 return MF_NO_ACTION;
7903 if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
7904 return MF_NO_ACTION;
7906 if (player->push_delay == 0) /* new pushing; restart delay */
7907 player->push_delay = FrameCounter;
7909 if (!FrameReached(&player->push_delay, player->push_delay_value) &&
7910 !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
7911 element != EL_SPRING && element != EL_BALLOON)
7913 /* make sure that there is no move delay before next try to push */
7914 if (game.engine_version >= VERSION_IDENT(3,0,7))
7915 player->move_delay = INITIAL_MOVE_DELAY_OFF;
7917 return MF_NO_ACTION;
7920 if (IS_SB_ELEMENT(element))
7922 if (element == EL_SOKOBAN_FIELD_FULL)
7924 Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
7925 local_player->sokobanfields_still_needed++;
7928 if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
7930 Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
7931 local_player->sokobanfields_still_needed--;
7934 Feld[x][y] = EL_SOKOBAN_OBJECT;
7936 if (Back[x][y] == Back[nextx][nexty])
7937 PlaySoundLevelAction(x, y, ACTION_PUSHING);
7938 else if (Back[x][y] != 0)
7939 PlaySoundLevelElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
7942 PlaySoundLevelElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
7945 if (local_player->sokobanfields_still_needed == 0 &&
7946 game.emulation == EMU_SOKOBAN)
7948 player->LevelSolved = player->GameOver = TRUE;
7949 PlaySoundLevel(x, y, SND_GAME_SOKOBAN_SOLVING);
7953 PlaySoundLevelElementAction(x, y, element, ACTION_PUSHING);
7955 InitMovingField(x, y, move_direction);
7956 GfxAction[x][y] = ACTION_PUSHING;
7958 if (mode == DF_SNAP)
7959 ContinueMoving(x, y);
7961 MovPos[x][y] = (dx != 0 ? dx : dy);
7963 Pushed[x][y] = TRUE;
7964 Pushed[nextx][nexty] = TRUE;
7966 if (game.engine_version < RELEASE_IDENT(2,2,0,7))
7967 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
7969 CheckTriggeredElementSideChange(x, y, element, dig_side,
7970 CE_OTHER_GETS_PUSHED);
7971 CheckElementSideChange(x, y, element, dig_side,
7972 CE_PUSHED_BY_PLAYER, -1);
7976 else if (IS_SWITCHABLE(element))
7978 if (PLAYER_SWITCHING(player, x, y))
7981 player->is_switching = TRUE;
7982 player->switch_x = x;
7983 player->switch_y = y;
7985 PlaySoundLevelElementAction(x, y, element, ACTION_ACTIVATING);
7987 if (element == EL_ROBOT_WHEEL)
7989 Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
7993 DrawLevelField(x, y);
7995 else if (element == EL_SP_TERMINAL)
7999 for (yy=0; yy < lev_fieldy; yy++) for (xx=0; xx < lev_fieldx; xx++)
8001 if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
8003 else if (Feld[xx][yy] == EL_SP_TERMINAL)
8004 Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
8007 else if (IS_BELT_SWITCH(element))
8009 ToggleBeltSwitch(x, y);
8011 else if (element == EL_SWITCHGATE_SWITCH_UP ||
8012 element == EL_SWITCHGATE_SWITCH_DOWN)
8014 ToggleSwitchgateSwitch(x, y);
8016 else if (element == EL_LIGHT_SWITCH ||
8017 element == EL_LIGHT_SWITCH_ACTIVE)
8019 ToggleLightSwitch(x, y);
8022 PlaySoundLevel(x, y, element == EL_LIGHT_SWITCH ?
8023 SND_LIGHT_SWITCH_ACTIVATING :
8024 SND_LIGHT_SWITCH_DEACTIVATING);
8027 else if (element == EL_TIMEGATE_SWITCH)
8029 ActivateTimegateSwitch(x, y);
8031 else if (element == EL_BALLOON_SWITCH_LEFT ||
8032 element == EL_BALLOON_SWITCH_RIGHT ||
8033 element == EL_BALLOON_SWITCH_UP ||
8034 element == EL_BALLOON_SWITCH_DOWN ||
8035 element == EL_BALLOON_SWITCH_ANY)
8037 if (element == EL_BALLOON_SWITCH_ANY)
8038 game.balloon_dir = move_direction;
8040 game.balloon_dir = (element == EL_BALLOON_SWITCH_LEFT ? MV_LEFT :
8041 element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
8042 element == EL_BALLOON_SWITCH_UP ? MV_UP :
8043 element == EL_BALLOON_SWITCH_DOWN ? MV_DOWN :
8046 else if (element == EL_LAMP)
8048 Feld[x][y] = EL_LAMP_ACTIVE;
8049 local_player->lights_still_needed--;
8051 DrawLevelField(x, y);
8053 else if (element == EL_TIME_ORB_FULL)
8055 Feld[x][y] = EL_TIME_ORB_EMPTY;
8057 DrawText(DX_TIME, DY_TIME, int2str(TimeLeft, 3), FONT_TEXT_2);
8059 DrawLevelField(x, y);
8062 PlaySoundStereo(SND_TIME_ORB_FULL_COLLECTING, SOUND_MIDDLE);
8070 if (!PLAYER_SWITCHING(player, x, y))
8072 player->is_switching = TRUE;
8073 player->switch_x = x;
8074 player->switch_y = y;
8076 CheckTriggeredElementSideChange(x, y, element, dig_side,
8077 CE_OTHER_IS_SWITCHING);
8078 CheckElementSideChange(x, y, element, dig_side, CE_SWITCHED, -1);
8081 CheckTriggeredElementSideChange(x, y, element, dig_side,
8082 CE_OTHER_GETS_PRESSED);
8083 CheckElementSideChange(x, y, element, dig_side,
8084 CE_PRESSED_BY_PLAYER, -1);
8087 return MF_NO_ACTION;
8090 player->push_delay = 0;
8092 if (Feld[x][y] != element) /* really digged/collected something */
8093 player->is_collecting = !player->is_digging;
8098 boolean SnapField(struct PlayerInfo *player, int dx, int dy)
8100 int jx = player->jx, jy = player->jy;
8101 int x = jx + dx, y = jy + dy;
8102 int snap_direction = (dx == -1 ? MV_LEFT :
8103 dx == +1 ? MV_RIGHT :
8105 dy == +1 ? MV_DOWN : MV_NO_MOVING);
8107 if (player->MovPos && game.engine_version >= VERSION_IDENT(2,2,0))
8110 if (!player->active || !IN_LEV_FIELD(x, y))
8118 if (player->MovPos == 0)
8119 player->is_pushing = FALSE;
8121 player->is_snapping = FALSE;
8123 if (player->MovPos == 0)
8125 player->is_moving = FALSE;
8126 player->is_digging = FALSE;
8127 player->is_collecting = FALSE;
8133 if (player->is_snapping)
8136 player->MovDir = snap_direction;
8138 player->is_moving = FALSE;
8139 player->is_digging = FALSE;
8140 player->is_collecting = FALSE;
8142 if (DigField(player, x, y, 0, 0, DF_SNAP) == MF_NO_ACTION)
8145 player->is_snapping = TRUE;
8147 player->is_moving = FALSE;
8148 player->is_digging = FALSE;
8149 player->is_collecting = FALSE;
8151 DrawLevelField(x, y);
8157 boolean DropElement(struct PlayerInfo *player)
8159 int jx = player->jx, jy = player->jy;
8162 if (!player->active || player->MovPos)
8165 old_element = Feld[jx][jy];
8167 /* check if player has anything that can be dropped */
8168 if (player->inventory_size == 0 && player->dynabombs_left == 0)
8171 /* check if anything can be dropped at the current position */
8172 if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
8175 /* collected custom elements can only be dropped on empty fields */
8176 if (player->inventory_size > 0 &&
8177 IS_CUSTOM_ELEMENT(player->inventory_element[player->inventory_size - 1])
8178 && old_element != EL_EMPTY)
8181 if (old_element != EL_EMPTY)
8182 Back[jx][jy] = old_element; /* store old element on this field */
8184 MovDelay[jx][jy] = 96;
8186 ResetGfxAnimation(jx, jy);
8187 ResetRandomAnimationValue(jx, jy);
8189 if (player->inventory_size > 0)
8191 int new_element = player->inventory_element[--player->inventory_size];
8193 Feld[jx][jy] = (new_element == EL_DYNAMITE ? EL_DYNAMITE_ACTIVE :
8194 new_element == EL_SP_DISK_RED ? EL_SP_DISK_RED_ACTIVE :
8197 DrawText(DX_DYNAMITE, DY_DYNAMITE,
8198 int2str(local_player->inventory_size, 3), FONT_TEXT_2);
8200 if (IN_SCR_FIELD(SCREENX(jx), SCREENY(jy)))
8201 DrawGraphicThruMask(SCREENX(jx), SCREENY(jy), el2img(Feld[jx][jy]), 0);
8203 PlaySoundLevelAction(jx, jy, ACTION_DROPPING);
8205 CheckTriggeredElementChange(jx, jy, new_element, CE_OTHER_GETS_DROPPED);
8206 CheckElementChange(jx, jy, new_element, CE_DROPPED_BY_PLAYER);
8208 TestIfElementTouchesCustomElement(jx, jy);
8210 else /* player is dropping a dyna bomb */
8212 player->dynabombs_left--;
8215 EL_DYNABOMB_PLAYER_1_ACTIVE + (player->element_nr - EL_PLAYER_1);
8217 if (IN_SCR_FIELD(SCREENX(jx), SCREENY(jy)))
8218 DrawGraphicThruMask(SCREENX(jx), SCREENY(jy), el2img(Feld[jx][jy]), 0);
8220 PlaySoundLevelAction(jx, jy, ACTION_DROPPING);
8226 /* ------------------------------------------------------------------------- */
8227 /* game sound playing functions */
8228 /* ------------------------------------------------------------------------- */
8230 static int *loop_sound_frame = NULL;
8231 static int *loop_sound_volume = NULL;
8233 void InitPlaySoundLevel()
8235 int num_sounds = getSoundListSize();
8237 if (loop_sound_frame != NULL)
8238 free(loop_sound_frame);
8240 if (loop_sound_volume != NULL)
8241 free(loop_sound_volume);
8243 loop_sound_frame = checked_calloc(num_sounds * sizeof(int));
8244 loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
8247 static void PlaySoundLevel(int x, int y, int nr)
8249 int sx = SCREENX(x), sy = SCREENY(y);
8250 int volume, stereo_position;
8251 int max_distance = 8;
8252 int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
8254 if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
8255 (!setup.sound_loops && IS_LOOP_SOUND(nr)))
8258 if (!IN_LEV_FIELD(x, y) ||
8259 sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
8260 sy < -max_distance || sy >= SCR_FIELDY + max_distance)
8263 volume = SOUND_MAX_VOLUME;
8265 if (!IN_SCR_FIELD(sx, sy))
8267 int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
8268 int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
8270 volume -= volume * (dx > dy ? dx : dy) / max_distance;
8273 stereo_position = (SOUND_MAX_LEFT +
8274 (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
8275 (SCR_FIELDX + 2 * max_distance));
8277 if (IS_LOOP_SOUND(nr))
8279 /* This assures that quieter loop sounds do not overwrite louder ones,
8280 while restarting sound volume comparison with each new game frame. */
8282 if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
8285 loop_sound_volume[nr] = volume;
8286 loop_sound_frame[nr] = FrameCounter;
8289 PlaySoundExt(nr, volume, stereo_position, type);
8292 static void PlaySoundLevelNearest(int x, int y, int sound_action)
8294 PlaySoundLevel(x < LEVELX(BX1) ? LEVELX(BX1) :
8295 x > LEVELX(BX2) ? LEVELX(BX2) : x,
8296 y < LEVELY(BY1) ? LEVELY(BY1) :
8297 y > LEVELY(BY2) ? LEVELY(BY2) : y,
8301 static void PlaySoundLevelAction(int x, int y, int action)
8303 PlaySoundLevelElementAction(x, y, Feld[x][y], action);
8306 static void PlaySoundLevelElementAction(int x, int y, int element, int action)
8308 int sound_effect = element_info[element].sound[action];
8310 if (sound_effect != SND_UNDEFINED)
8311 PlaySoundLevel(x, y, sound_effect);
8314 static void PlaySoundLevelActionIfLoop(int x, int y, int action)
8316 int sound_effect = element_info[Feld[x][y]].sound[action];
8318 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
8319 PlaySoundLevel(x, y, sound_effect);
8322 static void StopSoundLevelActionIfLoop(int x, int y, int action)
8324 int sound_effect = element_info[Feld[x][y]].sound[action];
8326 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
8327 StopSoundExt(sound_effect, SND_CTRL_STOP_SOUND);
8330 void RaiseScore(int value)
8332 local_player->score += value;
8333 DrawText(DX_SCORE, DY_SCORE, int2str(local_player->score, 5), FONT_TEXT_2);
8336 void RaiseScoreElement(int element)
8342 case EL_EMERALD_YELLOW:
8343 case EL_EMERALD_RED:
8344 case EL_EMERALD_PURPLE:
8345 case EL_SP_INFOTRON:
8346 RaiseScore(level.score[SC_EMERALD]);
8349 RaiseScore(level.score[SC_DIAMOND]);
8352 RaiseScore(level.score[SC_CRYSTAL]);
8355 RaiseScore(level.score[SC_PEARL]);
8358 case EL_BD_BUTTERFLY:
8359 case EL_SP_ELECTRON:
8360 RaiseScore(level.score[SC_BUG]);
8364 case EL_SP_SNIKSNAK:
8365 RaiseScore(level.score[SC_SPACESHIP]);
8368 case EL_DARK_YAMYAM:
8369 RaiseScore(level.score[SC_YAMYAM]);
8372 RaiseScore(level.score[SC_ROBOT]);
8375 RaiseScore(level.score[SC_PACMAN]);
8378 RaiseScore(level.score[SC_NUT]);
8381 case EL_SP_DISK_RED:
8382 case EL_DYNABOMB_INCREASE_NUMBER:
8383 case EL_DYNABOMB_INCREASE_SIZE:
8384 case EL_DYNABOMB_INCREASE_POWER:
8385 RaiseScore(level.score[SC_DYNAMITE]);
8387 case EL_SHIELD_NORMAL:
8388 case EL_SHIELD_DEADLY:
8389 RaiseScore(level.score[SC_SHIELD]);
8392 RaiseScore(level.score[SC_TIME_BONUS]);
8398 RaiseScore(level.score[SC_KEY]);
8401 RaiseScore(element_info[element].collect_score);
8406 void RequestQuitGame(boolean ask_if_really_quit)
8408 if (AllPlayersGone ||
8409 !ask_if_really_quit ||
8410 level_editor_test_game ||
8411 Request("Do you really want to quit the game ?",
8412 REQ_ASK | REQ_STAY_CLOSED))
8414 #if defined(PLATFORM_UNIX)
8415 if (options.network)
8416 SendToServer_StopPlaying();
8420 game_status = GAME_MODE_MAIN;
8426 OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
8431 /* ---------- new game button stuff ---------------------------------------- */
8433 /* graphic position values for game buttons */
8434 #define GAME_BUTTON_XSIZE 30
8435 #define GAME_BUTTON_YSIZE 30
8436 #define GAME_BUTTON_XPOS 5
8437 #define GAME_BUTTON_YPOS 215
8438 #define SOUND_BUTTON_XPOS 5
8439 #define SOUND_BUTTON_YPOS (GAME_BUTTON_YPOS + GAME_BUTTON_YSIZE)
8441 #define GAME_BUTTON_STOP_XPOS (GAME_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
8442 #define GAME_BUTTON_PAUSE_XPOS (GAME_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
8443 #define GAME_BUTTON_PLAY_XPOS (GAME_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
8444 #define SOUND_BUTTON_MUSIC_XPOS (SOUND_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
8445 #define SOUND_BUTTON_LOOPS_XPOS (SOUND_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
8446 #define SOUND_BUTTON_SIMPLE_XPOS (SOUND_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
8453 } gamebutton_info[NUM_GAME_BUTTONS] =
8456 GAME_BUTTON_STOP_XPOS, GAME_BUTTON_YPOS,
8461 GAME_BUTTON_PAUSE_XPOS, GAME_BUTTON_YPOS,
8466 GAME_BUTTON_PLAY_XPOS, GAME_BUTTON_YPOS,
8471 SOUND_BUTTON_MUSIC_XPOS, SOUND_BUTTON_YPOS,
8472 SOUND_CTRL_ID_MUSIC,
8473 "background music on/off"
8476 SOUND_BUTTON_LOOPS_XPOS, SOUND_BUTTON_YPOS,
8477 SOUND_CTRL_ID_LOOPS,
8478 "sound loops on/off"
8481 SOUND_BUTTON_SIMPLE_XPOS, SOUND_BUTTON_YPOS,
8482 SOUND_CTRL_ID_SIMPLE,
8483 "normal sounds on/off"
8487 void CreateGameButtons()
8491 for (i=0; i<NUM_GAME_BUTTONS; i++)
8493 Bitmap *gd_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
8494 struct GadgetInfo *gi;
8497 unsigned long event_mask;
8498 int gd_xoffset, gd_yoffset;
8499 int gd_x1, gd_x2, gd_y1, gd_y2;
8502 gd_xoffset = gamebutton_info[i].x;
8503 gd_yoffset = gamebutton_info[i].y;
8504 gd_x1 = DOOR_GFX_PAGEX4 + gd_xoffset;
8505 gd_x2 = DOOR_GFX_PAGEX3 + gd_xoffset;
8507 if (id == GAME_CTRL_ID_STOP ||
8508 id == GAME_CTRL_ID_PAUSE ||
8509 id == GAME_CTRL_ID_PLAY)
8511 button_type = GD_TYPE_NORMAL_BUTTON;
8513 event_mask = GD_EVENT_RELEASED;
8514 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
8515 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
8519 button_type = GD_TYPE_CHECK_BUTTON;
8521 ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
8522 (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
8523 (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
8524 event_mask = GD_EVENT_PRESSED;
8525 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset;
8526 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
8529 gi = CreateGadget(GDI_CUSTOM_ID, id,
8530 GDI_INFO_TEXT, gamebutton_info[i].infotext,
8531 GDI_X, DX + gd_xoffset,
8532 GDI_Y, DY + gd_yoffset,
8533 GDI_WIDTH, GAME_BUTTON_XSIZE,
8534 GDI_HEIGHT, GAME_BUTTON_YSIZE,
8535 GDI_TYPE, button_type,
8536 GDI_STATE, GD_BUTTON_UNPRESSED,
8537 GDI_CHECKED, checked,
8538 GDI_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y1,
8539 GDI_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y1,
8540 GDI_ALT_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y2,
8541 GDI_ALT_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y2,
8542 GDI_EVENT_MASK, event_mask,
8543 GDI_CALLBACK_ACTION, HandleGameButtons,
8547 Error(ERR_EXIT, "cannot create gadget");
8549 game_gadget[id] = gi;
8553 void FreeGameButtons()
8557 for (i=0; i<NUM_GAME_BUTTONS; i++)
8558 FreeGadget(game_gadget[i]);
8561 static void MapGameButtons()
8565 for (i=0; i<NUM_GAME_BUTTONS; i++)
8566 MapGadget(game_gadget[i]);
8569 void UnmapGameButtons()
8573 for (i=0; i<NUM_GAME_BUTTONS; i++)
8574 UnmapGadget(game_gadget[i]);
8577 static void HandleGameButtons(struct GadgetInfo *gi)
8579 int id = gi->custom_id;
8581 if (game_status != GAME_MODE_PLAYING)
8586 case GAME_CTRL_ID_STOP:
8587 RequestQuitGame(TRUE);
8590 case GAME_CTRL_ID_PAUSE:
8591 if (options.network)
8593 #if defined(PLATFORM_UNIX)
8595 SendToServer_ContinuePlaying();
8597 SendToServer_PausePlaying();
8601 TapeTogglePause(TAPE_TOGGLE_MANUAL);
8604 case GAME_CTRL_ID_PLAY:
8607 #if defined(PLATFORM_UNIX)
8608 if (options.network)
8609 SendToServer_ContinuePlaying();
8613 tape.pausing = FALSE;
8614 DrawVideoDisplay(VIDEO_STATE_PAUSE_OFF,0);
8619 case SOUND_CTRL_ID_MUSIC:
8620 if (setup.sound_music)
8622 setup.sound_music = FALSE;
8625 else if (audio.music_available)
8627 setup.sound = setup.sound_music = TRUE;
8629 SetAudioMode(setup.sound);
8630 PlayMusic(level_nr);
8634 case SOUND_CTRL_ID_LOOPS:
8635 if (setup.sound_loops)
8636 setup.sound_loops = FALSE;
8637 else if (audio.loops_available)
8639 setup.sound = setup.sound_loops = TRUE;
8640 SetAudioMode(setup.sound);
8644 case SOUND_CTRL_ID_SIMPLE:
8645 if (setup.sound_simple)
8646 setup.sound_simple = FALSE;
8647 else if (audio.sound_available)
8649 setup.sound = setup.sound_simple = TRUE;
8650 SetAudioMode(setup.sound);