1 /***********************************************************
2 * Rocks'n'Diamonds -- McDuffin Strikes Back! *
3 *----------------------------------------------------------*
4 * (c) 1995-2001 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 MoveFigure() */
36 #define MF_NO_ACTION 0
40 /* for ScrollFigure() */
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 #define IS_LOOP_SOUND(s) ((s)==SND_KLAPPER || (s)==SND_ROEHR || \
80 (s)==SND_NJAM || (s)==SND_MIEP)
81 #define IS_MUSIC_SOUND(s) ((s)==SND_ALCHEMY || (s)==SND_CHASE || \
82 (s)==SND_NETWORK || (s)==SND_CZARDASZ || \
83 (s)==SND_TYGER || (s)==SND_VOYAGER || \
86 /* values for player movement speed (which is in fact a delay value) */
87 #define MOVE_DELAY_NORMAL_SPEED 8
88 #define MOVE_DELAY_HIGH_SPEED 4
90 #define DOUBLE_MOVE_DELAY(x) (x = (x <= MOVE_DELAY_HIGH_SPEED ? x * 2 : x))
91 #define HALVE_MOVE_DELAY(x) (x = (x >= MOVE_DELAY_HIGH_SPEED ? x / 2 : x))
92 #define DOUBLE_PLAYER_SPEED(p) (HALVE_MOVE_DELAY((p)->move_delay_value))
93 #define HALVE_PLAYER_SPEED(p) (DOUBLE_MOVE_DELAY((p)->move_delay_value))
95 /* game button identifiers */
96 #define GAME_CTRL_ID_STOP 0
97 #define GAME_CTRL_ID_PAUSE 1
98 #define GAME_CTRL_ID_PLAY 2
99 #define SOUND_CTRL_ID_MUSIC 3
100 #define SOUND_CTRL_ID_LOOPS 4
101 #define SOUND_CTRL_ID_SIMPLE 5
103 #define NUM_GAME_BUTTONS 6
105 /* forward declaration for internal use */
106 static void CloseAllOpenTimegates(void);
107 static void CheckGravityMovement(struct PlayerInfo *);
108 static void KillHeroUnlessProtected(int, int);
110 static void MapGameButtons();
111 static void HandleGameButtons(struct GadgetInfo *);
113 static struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS];
119 static unsigned int getStateCheckSum(int counter)
122 unsigned int mult = 1;
123 unsigned int checksum = 0;
125 static short lastFeld[MAX_LEV_FIELDX][MAX_LEV_FIELDY];
127 static boolean first_game = TRUE;
129 for (y=0; y<lev_fieldy; y++) for(x=0; x<lev_fieldx; x++)
135 lastFeld[x][y] = Feld[x][y];
136 else if (lastFeld[x][y] != Feld[x][y])
137 printf("DIFF: [%d][%d]: lastFeld == %d != %d == Feld\n",
138 x, y, lastFeld[x][y], Feld[x][y]);
142 checksum += mult++ * Ur[x][y];
143 checksum += mult++ * Feld[x][y];
146 checksum += mult++ * MovPos[x][y];
147 checksum += mult++ * MovDir[x][y];
148 checksum += mult++ * MovDelay[x][y];
149 checksum += mult++ * Store[x][y];
150 checksum += mult++ * Store2[x][y];
151 checksum += mult++ * StorePlayer[x][y];
152 checksum += mult++ * Frame[x][y];
153 checksum += mult++ * AmoebaNr[x][y];
154 checksum += mult++ * JustStopped[x][y];
155 checksum += mult++ * Stop[x][y];
159 if (counter == 3 && first_game)
170 void GetPlayerConfig()
172 if (!audio.sound_available)
175 if (!audio.loops_available)
176 setup.sound_loops = FALSE;
178 if (!audio.music_available)
179 setup.sound_music = FALSE;
181 if (!video.fullscreen_available)
182 setup.fullscreen = FALSE;
184 setup.sound_simple = setup.sound;
186 SetAudioMode(setup.sound);
190 static int getBeltNrFromElement(int element)
192 return (element < EL_BELT2_LEFT ? 0 :
193 element < EL_BELT3_LEFT ? 1 :
194 element < EL_BELT4_LEFT ? 2 : 3);
197 static int getBeltNrFromSwitchElement(int element)
199 return (element < EL_BELT2_SWITCH_LEFT ? 0 :
200 element < EL_BELT3_SWITCH_LEFT ? 1 :
201 element < EL_BELT4_SWITCH_LEFT ? 2 : 3);
204 static int getBeltDirNrFromSwitchElement(int element)
206 static int belt_base_element[4] =
208 EL_BELT1_SWITCH_LEFT,
209 EL_BELT2_SWITCH_LEFT,
210 EL_BELT3_SWITCH_LEFT,
214 int belt_nr = getBeltNrFromSwitchElement(element);
215 int belt_dir_nr = element - belt_base_element[belt_nr];
217 return (belt_dir_nr % 3);
220 static int getBeltDirFromSwitchElement(int element)
222 static int belt_move_dir[3] =
229 int belt_dir_nr = getBeltDirNrFromSwitchElement(element);
231 return belt_move_dir[belt_dir_nr];
234 static void InitField(int x, int y, boolean init_game)
241 if (stored_player[0].present)
243 Feld[x][y] = EL_SP_MURPHY_CLONE;
250 Feld[x][y] = EL_SPIELER1;
258 struct PlayerInfo *player = &stored_player[Feld[x][y] - EL_SPIELER1];
259 int jx = player->jx, jy = player->jy;
261 player->present = TRUE;
263 if (!options.network || player->connected)
265 player->active = TRUE;
267 /* remove potentially duplicate players */
268 if (StorePlayer[jx][jy] == Feld[x][y])
269 StorePlayer[jx][jy] = 0;
271 StorePlayer[x][y] = Feld[x][y];
275 printf("Player %d activated.\n", player->element_nr);
276 printf("[Local player is %d and currently %s.]\n",
277 local_player->element_nr,
278 local_player->active ? "active" : "not active");
282 Feld[x][y] = EL_LEERRAUM;
283 player->jx = player->last_jx = x;
284 player->jy = player->last_jy = y;
289 if (x < lev_fieldx-1 && Feld[x+1][y] == EL_SALZSAEURE)
290 Feld[x][y] = EL_BADEWANNE1;
291 else if (x > 0 && Feld[x-1][y] == EL_SALZSAEURE)
292 Feld[x][y] = EL_BADEWANNE2;
293 else if (y > 0 && Feld[x][y-1] == EL_BADEWANNE1)
294 Feld[x][y] = EL_BADEWANNE3;
295 else if (y > 0 && Feld[x][y-1] == EL_SALZSAEURE)
296 Feld[x][y] = EL_BADEWANNE4;
297 else if (y > 0 && Feld[x][y-1] == EL_BADEWANNE2)
298 Feld[x][y] = EL_BADEWANNE5;
301 case EL_KAEFER_RIGHT:
306 case EL_FLIEGER_RIGHT:
308 case EL_FLIEGER_LEFT:
309 case EL_FLIEGER_DOWN:
311 case EL_BUTTERFLY_RIGHT:
312 case EL_BUTTERFLY_UP:
313 case EL_BUTTERFLY_LEFT:
314 case EL_BUTTERFLY_DOWN:
316 case EL_FIREFLY_RIGHT:
318 case EL_FIREFLY_LEFT:
319 case EL_FIREFLY_DOWN:
321 case EL_PACMAN_RIGHT:
345 if (y == lev_fieldy - 1)
347 Feld[x][y] = EL_AMOEBING;
348 Store[x][y] = EL_AMOEBE_NASS;
352 case EL_DYNAMITE_ACTIVE:
357 local_player->lights_still_needed++;
360 case EL_SOKOBAN_FELD_LEER:
361 local_player->sokobanfields_still_needed++;
365 local_player->friends_still_needed++;
370 MovDir[x][y] = 1 << RND(4);
374 Feld[x][y] = EL_LEERRAUM;
377 case EL_EM_KEY_1_FILE:
378 Feld[x][y] = EL_EM_KEY_1;
380 case EL_EM_KEY_2_FILE:
381 Feld[x][y] = EL_EM_KEY_2;
383 case EL_EM_KEY_3_FILE:
384 Feld[x][y] = EL_EM_KEY_3;
386 case EL_EM_KEY_4_FILE:
387 Feld[x][y] = EL_EM_KEY_4;
390 case EL_BELT1_SWITCH_LEFT:
391 case EL_BELT1_SWITCH_MIDDLE:
392 case EL_BELT1_SWITCH_RIGHT:
393 case EL_BELT2_SWITCH_LEFT:
394 case EL_BELT2_SWITCH_MIDDLE:
395 case EL_BELT2_SWITCH_RIGHT:
396 case EL_BELT3_SWITCH_LEFT:
397 case EL_BELT3_SWITCH_MIDDLE:
398 case EL_BELT3_SWITCH_RIGHT:
399 case EL_BELT4_SWITCH_LEFT:
400 case EL_BELT4_SWITCH_MIDDLE:
401 case EL_BELT4_SWITCH_RIGHT:
404 int belt_nr = getBeltNrFromSwitchElement(Feld[x][y]);
405 int belt_dir = getBeltDirFromSwitchElement(Feld[x][y]);
406 int belt_dir_nr = getBeltDirNrFromSwitchElement(Feld[x][y]);
408 if (game.belt_dir_nr[belt_nr] == 3) /* initial value */
410 game.belt_dir[belt_nr] = belt_dir;
411 game.belt_dir_nr[belt_nr] = belt_dir_nr;
413 else /* more than one switch -- set it like the first switch */
415 Feld[x][y] = Feld[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
420 case EL_SWITCHGATE_SWITCH_2: /* always start with same switch pos */
422 Feld[x][y] = EL_SWITCHGATE_SWITCH_1;
425 case EL_LIGHT_SWITCH_ON:
427 game.light_time_left = level.time_light * FRAMES_PER_SECOND;
435 void DrawGameDoorValues()
437 DrawText(DX + XX_EMERALDS, DY + YY_EMERALDS,
438 int2str(local_player->gems_still_needed, 3), FS_SMALL, FC_YELLOW);
439 DrawText(DX + XX_DYNAMITE, DY + YY_DYNAMITE,
440 int2str(local_player->dynamite, 3), FS_SMALL, FC_YELLOW);
441 DrawText(DX + XX_SCORE, DY + YY_SCORE,
442 int2str(local_player->score, 5), FS_SMALL, FC_YELLOW);
443 DrawText(DX + XX_TIME, DY + YY_TIME,
444 int2str(TimeLeft, 3), FS_SMALL, FC_YELLOW);
450 boolean emulate_bd = TRUE; /* unless non-BOULDERDASH elements found */
451 boolean emulate_sb = TRUE; /* unless non-SOKOBAN elements found */
452 boolean emulate_sp = TRUE; /* unless non-SUPAPLEX elements found */
455 #if USE_NEW_AMOEBA_CODE
456 printf("Using new amoeba code.\n");
458 printf("Using old amoeba code.\n");
462 /* don't play tapes over network */
463 network_playing = (options.network && !tape.playing);
465 for (i=0; i<MAX_PLAYERS; i++)
467 struct PlayerInfo *player = &stored_player[i];
469 player->index_nr = i;
470 player->element_nr = EL_SPIELER1 + i;
472 player->present = FALSE;
473 player->active = FALSE;
476 player->effective_action = 0;
477 player->programmed_action = 0;
480 player->gems_still_needed = level.gems_needed;
481 player->sokobanfields_still_needed = 0;
482 player->lights_still_needed = 0;
483 player->friends_still_needed = 0;
486 player->key[j] = FALSE;
488 player->dynamite = 0;
489 player->dynabomb_count = 0;
490 player->dynabomb_size = 1;
491 player->dynabombs_left = 0;
492 player->dynabomb_xl = FALSE;
494 player->MovDir = MV_NO_MOVING;
496 player->Pushing = FALSE;
497 player->Switching = FALSE;
501 player->actual_frame_counter = 0;
503 player->frame_reset_delay = 0;
505 player->last_move_dir = MV_NO_MOVING;
506 player->is_moving = FALSE;
508 player->move_delay = -1; /* no initial move delay */
509 player->move_delay_value =
510 (level.double_speed ? MOVE_DELAY_HIGH_SPEED : MOVE_DELAY_NORMAL_SPEED);
512 player->push_delay = 0;
513 player->push_delay_value = 5;
515 player->snapped = FALSE;
517 player->last_jx = player->last_jy = 0;
518 player->jx = player->jy = 0;
520 player->shield_passive_time_left = 0;
521 player->shield_active_time_left = 0;
523 DigField(player, 0, 0, 0, 0, DF_NO_PUSH);
524 SnapField(player, 0, 0);
526 player->LevelSolved = FALSE;
527 player->GameOver = FALSE;
530 network_player_action_received = FALSE;
532 #if defined(PLATFORM_UNIX)
533 /* initial null action */
535 SendToServer_MovePlayer(MV_NO_MOVING);
543 TimeLeft = level.time;
545 ScreenMovDir = MV_NO_MOVING;
549 ScrollStepSize = 0; /* will be correctly initialized by ScrollScreen() */
551 AllPlayersGone = FALSE;
553 game.yam_content_nr = 0;
554 game.magic_wall_active = FALSE;
555 game.magic_wall_time_left = 0;
556 game.light_time_left = 0;
557 game.timegate_time_left = 0;
558 game.switchgate_pos = 0;
559 game.balloon_dir = MV_NO_MOVING;
560 game.explosions_delayed = TRUE;
564 game.belt_dir[i] = MV_NO_MOVING;
565 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
568 for (i=0; i<MAX_NUM_AMOEBA; i++)
569 AmoebaCnt[i] = AmoebaCnt2[i] = 0;
571 for (x=0; x<lev_fieldx; x++)
573 for (y=0; y<lev_fieldy; y++)
575 Feld[x][y] = Ur[x][y];
576 MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
577 Store[x][y] = Store2[x][y] = StorePlayer[x][y] = 0;
580 JustStopped[x][y] = 0;
582 ExplodeField[x][y] = EX_NO_EXPLOSION;
586 for(y=0; y<lev_fieldy; y++)
588 for(x=0; x<lev_fieldx; x++)
590 if (emulate_bd && !IS_BD_ELEMENT(Feld[x][y]))
592 if (emulate_sb && !IS_SB_ELEMENT(Feld[x][y]))
594 if (emulate_sp && !IS_SP_ELEMENT(Feld[x][y]))
597 InitField(x, y, TRUE);
601 /* correct non-moving belts to start moving left */
603 if (game.belt_dir[i] == MV_NO_MOVING)
604 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
606 /* check if any connected player was not found in playfield */
607 for (i=0; i<MAX_PLAYERS; i++)
609 struct PlayerInfo *player = &stored_player[i];
611 if (player->connected && !player->present)
613 for (j=0; j<MAX_PLAYERS; j++)
615 struct PlayerInfo *some_player = &stored_player[j];
616 int jx = some_player->jx, jy = some_player->jy;
618 /* assign first free player found that is present in the playfield */
619 if (some_player->present && !some_player->connected)
621 player->present = TRUE;
622 player->active = TRUE;
623 some_player->present = FALSE;
625 StorePlayer[jx][jy] = player->element_nr;
626 player->jx = player->last_jx = jx;
627 player->jy = player->last_jy = jy;
637 /* when playing a tape, eliminate all players who do not participate */
639 for (i=0; i<MAX_PLAYERS; i++)
641 if (stored_player[i].active && !tape.player_participates[i])
643 struct PlayerInfo *player = &stored_player[i];
644 int jx = player->jx, jy = player->jy;
646 player->active = FALSE;
647 StorePlayer[jx][jy] = 0;
648 Feld[jx][jy] = EL_LEERRAUM;
652 else if (!options.network && !setup.team_mode) /* && !tape.playing */
654 /* when in single player mode, eliminate all but the first active player */
656 for (i=0; i<MAX_PLAYERS; i++)
658 if (stored_player[i].active)
660 for (j=i+1; j<MAX_PLAYERS; j++)
662 if (stored_player[j].active)
664 struct PlayerInfo *player = &stored_player[j];
665 int jx = player->jx, jy = player->jy;
667 player->active = FALSE;
668 StorePlayer[jx][jy] = 0;
669 Feld[jx][jy] = EL_LEERRAUM;
676 /* when recording the game, store which players take part in the game */
679 for (i=0; i<MAX_PLAYERS; i++)
680 if (stored_player[i].active)
681 tape.player_participates[i] = TRUE;
686 for (i=0; i<MAX_PLAYERS; i++)
688 struct PlayerInfo *player = &stored_player[i];
690 printf("Player %d: present == %d, connected == %d, active == %d.\n",
695 if (local_player == player)
696 printf("Player %d is local player.\n", i+1);
700 game.version = (tape.playing ? tape.game_version : level.game_version);
701 game.emulation = (emulate_bd ? EMU_BOULDERDASH :
702 emulate_sb ? EMU_SOKOBAN :
703 emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
705 /* dynamically adjust element properties according to game engine version */
707 static int ep_em_slippery_wall[] =
717 static int ep_em_slippery_wall_num = SIZEOF_ARRAY_INT(ep_em_slippery_wall);
719 static int ep_em_slippery_wall_num =
720 sizeof(ep_em_slippery_wall) / sizeof(int);
724 printf("level %d: game.version == %06d\n", level_nr, level.game_version);
725 printf(" file_version == %06d\n", level.file_version);
728 for (i=0; i<ep_em_slippery_wall_num; i++)
731 if (level.em_slippery_gems) /* special EM style gems behaviour */
733 if (game.version >= GAME_VERSION_2_0)
735 Elementeigenschaften2[ep_em_slippery_wall[i]] |=
736 EP_BIT_EM_SLIPPERY_WALL;
738 Elementeigenschaften2[ep_em_slippery_wall[i]] &=
739 ~EP_BIT_EM_SLIPPERY_WALL;
743 if (BorderElement == EL_LEERRAUM)
746 SBX_Right = lev_fieldx - SCR_FIELDX;
748 SBY_Lower = lev_fieldy - SCR_FIELDY;
753 SBX_Right = lev_fieldx - SCR_FIELDX + 1;
755 SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
758 if (lev_fieldx + (SBX_Left == -1 ? 2 : 0) <= SCR_FIELDX)
759 SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
761 if (lev_fieldy + (SBY_Upper == -1 ? 2 : 0) <= SCR_FIELDY)
762 SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
765 scroll_y = SBY_Upper;
766 if (local_player->jx >= SBX_Left + MIDPOSX)
767 scroll_x = (local_player->jx <= SBX_Right + MIDPOSX ?
768 local_player->jx - MIDPOSX :
770 if (local_player->jy >= SBY_Upper + MIDPOSY)
771 scroll_y = (local_player->jy <= SBY_Lower + MIDPOSY ?
772 local_player->jy - MIDPOSY :
775 CloseDoor(DOOR_CLOSE_1);
781 /* after drawing the level, correct some elements */
782 if (game.timegate_time_left == 0)
783 CloseAllOpenTimegates();
785 if (setup.soft_scrolling)
786 BlitBitmap(fieldbuffer, backbuffer, FX, FY, SXSIZE, SYSIZE, SX, SY);
788 redraw_mask |= REDRAW_FROM_BACKBUFFER;
790 /* copy default game door content to main double buffer */
791 BlitBitmap(pix[PIX_DOOR], drawto,
792 DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
795 DrawText(DX + XX_LEVEL, DY + YY_LEVEL,
796 int2str(level_nr, 2), FS_SMALL, FC_YELLOW);
799 DrawTextExt(drawto, DX + XX_EMERALDS, DY + YY_EMERALDS,
800 int2str(level_nr, 3), FS_SMALL, FC_SPECIAL3);
801 BlitBitmap(drawto, drawto,
802 DX + XX_EMERALDS, DY + YY_EMERALDS + 1,
803 FONT5_XSIZE * 3, FONT5_YSIZE - 1,
804 DX + XX_LEVEL - 1, DY + YY_LEVEL + 1);
808 DrawGameDoorValues();
810 DrawText(DX + XX_EMERALDS, DY + YY_EMERALDS,
811 int2str(local_player->gems_still_needed, 3), FS_SMALL, FC_YELLOW);
812 DrawText(DX + XX_DYNAMITE, DY + YY_DYNAMITE,
813 int2str(local_player->dynamite, 3), FS_SMALL, FC_YELLOW);
814 DrawText(DX + XX_SCORE, DY + YY_SCORE,
815 int2str(local_player->score, 5), FS_SMALL, FC_YELLOW);
816 DrawText(DX + XX_TIME, DY + YY_TIME,
817 int2str(TimeLeft, 3), FS_SMALL, FC_YELLOW);
822 game_gadget[SOUND_CTRL_ID_MUSIC]->checked = setup.sound_music;
823 game_gadget[SOUND_CTRL_ID_LOOPS]->checked = setup.sound_loops;
824 game_gadget[SOUND_CTRL_ID_SIMPLE]->checked = setup.sound_simple;
828 /* copy actual game door content to door double buffer for OpenDoor() */
829 BlitBitmap(drawto, pix[PIX_DB_DOOR],
830 DX, DY, DXSIZE, DYSIZE, DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1);
832 OpenDoor(DOOR_OPEN_ALL);
834 if (setup.sound_music)
837 KeyboardAutoRepeatOff();
842 printf("Player %d %sactive.\n",
843 i + 1, (stored_player[i].active ? "" : "not "));
847 void InitMovDir(int x, int y)
849 int i, element = Feld[x][y];
850 static int xy[4][2] =
857 static int direction[3][4] =
859 { MV_RIGHT, MV_UP, MV_LEFT, MV_DOWN },
860 { MV_LEFT, MV_DOWN, MV_RIGHT, MV_UP },
861 { MV_LEFT, MV_RIGHT, MV_UP, MV_DOWN }
866 case EL_KAEFER_RIGHT:
870 Feld[x][y] = EL_KAEFER;
871 MovDir[x][y] = direction[0][element - EL_KAEFER_RIGHT];
874 case EL_FLIEGER_RIGHT:
876 case EL_FLIEGER_LEFT:
877 case EL_FLIEGER_DOWN:
878 Feld[x][y] = EL_FLIEGER;
879 MovDir[x][y] = direction[0][element - EL_FLIEGER_RIGHT];
882 case EL_BUTTERFLY_RIGHT:
883 case EL_BUTTERFLY_UP:
884 case EL_BUTTERFLY_LEFT:
885 case EL_BUTTERFLY_DOWN:
886 Feld[x][y] = EL_BUTTERFLY;
887 MovDir[x][y] = direction[0][element - EL_BUTTERFLY_RIGHT];
890 case EL_FIREFLY_RIGHT:
892 case EL_FIREFLY_LEFT:
893 case EL_FIREFLY_DOWN:
894 Feld[x][y] = EL_FIREFLY;
895 MovDir[x][y] = direction[0][element - EL_FIREFLY_RIGHT];
898 case EL_PACMAN_RIGHT:
902 Feld[x][y] = EL_PACMAN;
903 MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
907 MovDir[x][y] = MV_UP;
911 MovDir[x][y] = MV_LEFT;
918 Feld[x][y] = EL_MOLE;
919 MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
923 MovDir[x][y] = 1 << RND(4);
924 if (element != EL_KAEFER &&
925 element != EL_FLIEGER &&
926 element != EL_BUTTERFLY &&
927 element != EL_FIREFLY)
932 int x1 = x + xy[i][0];
933 int y1 = y + xy[i][1];
935 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
937 if (element == EL_KAEFER || element == EL_BUTTERFLY)
939 MovDir[x][y] = direction[0][i];
942 else if (element == EL_FLIEGER || element == EL_FIREFLY ||
943 element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
945 MovDir[x][y] = direction[1][i];
954 void InitAmoebaNr(int x, int y)
957 int group_nr = AmoebeNachbarNr(x, y);
961 for (i=1; i<MAX_NUM_AMOEBA; i++)
963 if (AmoebaCnt[i] == 0)
971 AmoebaNr[x][y] = group_nr;
972 AmoebaCnt[group_nr]++;
973 AmoebaCnt2[group_nr]++;
979 boolean raise_level = FALSE;
981 if (local_player->MovPos)
984 local_player->LevelSolved = FALSE;
988 if (!tape.playing && setup.sound_loops)
989 PlaySoundExt(SND_SIRR, PSND_MAX_VOLUME, PSND_MAX_RIGHT, PSND_LOOP);
993 if (!tape.playing && !setup.sound_loops)
994 PlaySoundStereo(SND_SIRR, PSND_MAX_RIGHT);
995 if (TimeLeft > 0 && !(TimeLeft % 10))
996 RaiseScore(level.score[SC_ZEITBONUS]);
997 if (TimeLeft > 100 && !(TimeLeft % 10))
1001 DrawText(DX_TIME, DY_TIME, int2str(TimeLeft, 3), FS_SMALL, FC_YELLOW);
1008 if (!tape.playing && setup.sound_loops)
1009 StopSound(SND_SIRR);
1011 else if (level.time == 0) /* level without time limit */
1013 if (!tape.playing && setup.sound_loops)
1014 PlaySoundExt(SND_SIRR, PSND_MAX_VOLUME, PSND_MAX_RIGHT, PSND_LOOP);
1016 while(TimePlayed < 999)
1018 if (!tape.playing && !setup.sound_loops)
1019 PlaySoundStereo(SND_SIRR, PSND_MAX_RIGHT);
1020 if (TimePlayed < 999 && !(TimePlayed % 10))
1021 RaiseScore(level.score[SC_ZEITBONUS]);
1022 if (TimePlayed < 900 && !(TimePlayed % 10))
1026 DrawText(DX_TIME, DY_TIME, int2str(TimePlayed, 3), FS_SMALL, FC_YELLOW);
1033 if (!tape.playing && setup.sound_loops)
1034 StopSound(SND_SIRR);
1041 /* Hero disappears */
1042 DrawLevelField(ExitX, ExitY);
1048 CloseDoor(DOOR_CLOSE_1);
1053 SaveTape(tape.level_nr); /* Ask to save tape */
1056 if (level_nr == leveldir_current->handicap_level)
1058 leveldir_current->handicap_level++;
1059 SaveLevelSetup_SeriesInfo();
1062 if (level_editor_test_game)
1063 local_player->score = -1; /* no highscore when playing from editor */
1064 else if (level_nr < leveldir_current->last_level)
1065 raise_level = TRUE; /* advance to next level */
1067 if ((hi_pos = NewHiScore()) >= 0)
1069 game_status = HALLOFFAME;
1070 DrawHallOfFame(hi_pos);
1079 game_status = MAINMENU;
1096 LoadScore(level_nr);
1098 if (strcmp(setup.player_name, EMPTY_PLAYER_NAME) == 0 ||
1099 local_player->score < highscore[MAX_SCORE_ENTRIES - 1].Score)
1102 for (k=0; k<MAX_SCORE_ENTRIES; k++)
1104 if (local_player->score > highscore[k].Score)
1106 /* player has made it to the hall of fame */
1108 if (k < MAX_SCORE_ENTRIES - 1)
1110 int m = MAX_SCORE_ENTRIES - 1;
1113 for (l=k; l<MAX_SCORE_ENTRIES; l++)
1114 if (!strcmp(setup.player_name, highscore[l].Name))
1116 if (m == k) /* player's new highscore overwrites his old one */
1122 strcpy(highscore[l].Name, highscore[l - 1].Name);
1123 highscore[l].Score = highscore[l - 1].Score;
1130 strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
1131 highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
1132 highscore[k].Score = local_player->score;
1138 else if (!strncmp(setup.player_name, highscore[k].Name,
1139 MAX_PLAYER_NAME_LEN))
1140 break; /* player already there with a higher score */
1146 SaveScore(level_nr);
1151 void InitMovingField(int x, int y, int direction)
1153 int newx = x + (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
1154 int newy = y + (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
1156 MovDir[x][y] = direction;
1157 MovDir[newx][newy] = direction;
1158 if (Feld[newx][newy] == EL_LEERRAUM)
1159 Feld[newx][newy] = EL_BLOCKED;
1162 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
1164 int direction = MovDir[x][y];
1165 int newx = x + (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
1166 int newy = y + (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
1172 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
1174 int oldx = x, oldy = y;
1175 int direction = MovDir[x][y];
1177 if (direction == MV_LEFT)
1179 else if (direction == MV_RIGHT)
1181 else if (direction == MV_UP)
1183 else if (direction == MV_DOWN)
1186 *comes_from_x = oldx;
1187 *comes_from_y = oldy;
1190 int MovingOrBlocked2Element(int x, int y)
1192 int element = Feld[x][y];
1194 if (element == EL_BLOCKED)
1198 Blocked2Moving(x, y, &oldx, &oldy);
1199 return Feld[oldx][oldy];
1205 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
1207 /* like MovingOrBlocked2Element(), but if element is moving
1208 and (x,y) is the field the moving element is just leaving,
1209 return EL_BLOCKED instead of the element value */
1210 int element = Feld[x][y];
1212 if (IS_MOVING(x, y))
1214 if (element == EL_BLOCKED)
1218 Blocked2Moving(x, y, &oldx, &oldy);
1219 return Feld[oldx][oldy];
1228 static void RemoveField(int x, int y)
1230 Feld[x][y] = EL_LEERRAUM;
1236 void RemoveMovingField(int x, int y)
1238 int oldx = x, oldy = y, newx = x, newy = y;
1240 if (Feld[x][y] != EL_BLOCKED && !IS_MOVING(x, y))
1243 if (IS_MOVING(x, y))
1245 Moving2Blocked(x, y, &newx, &newy);
1246 if (Feld[newx][newy] != EL_BLOCKED)
1249 else if (Feld[x][y] == EL_BLOCKED)
1251 Blocked2Moving(x, y, &oldx, &oldy);
1252 if (!IS_MOVING(oldx, oldy))
1256 if (Feld[x][y] == EL_BLOCKED &&
1257 (Feld[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
1258 Feld[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
1259 Feld[oldx][oldy] == EL_MAGIC_WALL_BD_EMPTYING ||
1260 Feld[oldx][oldy] == EL_AMOEBA_DRIPPING))
1261 Feld[oldx][oldy] = get_next_element(Feld[oldx][oldy]);
1263 Feld[oldx][oldy] = EL_LEERRAUM;
1265 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
1267 Feld[newx][newy] = EL_LEERRAUM;
1268 MovPos[oldx][oldy] = MovDir[oldx][oldy] = MovDelay[oldx][oldy] = 0;
1269 MovPos[newx][newy] = MovDir[newx][newy] = MovDelay[newx][newy] = 0;
1271 DrawLevelField(oldx, oldy);
1272 DrawLevelField(newx, newy);
1275 void DrawDynamite(int x, int y)
1277 int sx = SCREENX(x), sy = SCREENY(y);
1278 int graphic = el2gfx(Feld[x][y]);
1281 if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
1285 DrawGraphic(sx, sy, el2gfx(Store[x][y]));
1287 if (Feld[x][y] == EL_DYNAMITE_ACTIVE)
1289 if ((phase = (96 - MovDelay[x][y]) / 12) > 6)
1294 if ((phase = ((96 - MovDelay[x][y]) / 6) % 8) > 3)
1298 if (game.emulation == EMU_SUPAPLEX)
1299 DrawGraphic(sx, sy, GFX_SP_DISK_RED);
1300 else if (Store[x][y])
1301 DrawGraphicThruMask(sx, sy, graphic + phase);
1303 DrawGraphic(sx, sy, graphic + phase);
1306 void CheckDynamite(int x, int y)
1308 if (MovDelay[x][y]) /* dynamite is still waiting to explode */
1313 if (!(MovDelay[x][y] % 12))
1314 PlaySoundLevel(x, y, SND_ZISCH);
1316 if (IS_ACTIVE_BOMB(Feld[x][y]))
1318 int delay = (Feld[x][y] == EL_DYNAMITE_ACTIVE ? 12 : 6);
1320 if (!(MovDelay[x][y] % delay))
1328 StopSound(SND_ZISCH);
1332 void Explode(int ex, int ey, int phase, int mode)
1335 int num_phase = 9, delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
1336 int last_phase = num_phase * delay;
1337 int half_phase = (num_phase / 2) * delay;
1338 int first_phase_after_start = EX_PHASE_START + 1;
1340 if (game.explosions_delayed)
1342 ExplodeField[ex][ey] = mode;
1346 if (phase == EX_PHASE_START) /* initialize 'Store[][]' field */
1348 int center_element = Feld[ex][ey];
1350 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
1352 /* put moving element to center field (and let it explode there) */
1353 center_element = MovingOrBlocked2Element(ex, ey);
1354 RemoveMovingField(ex, ey);
1355 Feld[ex][ey] = center_element;
1358 for (y=ey-1; y<=ey+1; y++) for(x=ex-1; x<=ex+1; x++)
1362 if (!IN_LEV_FIELD(x, y) ||
1363 ((mode != EX_NORMAL || center_element == EL_AMOEBA2DIAM) &&
1364 (x != ex || y != ey)))
1367 element = Feld[x][y];
1369 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
1371 element = MovingOrBlocked2Element(x, y);
1372 RemoveMovingField(x, y);
1375 if (IS_MASSIVE(element) || element == EL_BURNING)
1378 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
1380 if (IS_ACTIVE_BOMB(element))
1382 /* re-activate things under the bomb like gate or penguin */
1383 Feld[x][y] = (Store[x][y] ? Store[x][y] : EL_LEERRAUM);
1390 if (element == EL_EXPLODING)
1391 element = Store2[x][y];
1393 if (IS_PLAYER(ex, ey) && !PLAYER_PROTECTED(ex, ey))
1395 switch(StorePlayer[ex][ey])
1398 Store[x][y] = EL_EDELSTEIN_ROT;
1401 Store[x][y] = EL_EDELSTEIN;
1404 Store[x][y] = EL_EDELSTEIN_LILA;
1408 Store[x][y] = EL_EDELSTEIN_GELB;
1412 if (game.emulation == EMU_SUPAPLEX)
1413 Store[x][y] = EL_LEERRAUM;
1415 else if (center_element == EL_MOLE)
1416 Store[x][y] = EL_EDELSTEIN_ROT;
1417 else if (center_element == EL_PINGUIN)
1418 Store[x][y] = EL_EDELSTEIN_LILA;
1419 else if (center_element == EL_KAEFER)
1420 Store[x][y] = ((x == ex && y == ey) ? EL_DIAMANT : EL_EDELSTEIN);
1421 else if (center_element == EL_BUTTERFLY)
1422 Store[x][y] = EL_EDELSTEIN_BD;
1423 else if (center_element == EL_SP_ELECTRON)
1424 Store[x][y] = EL_SP_INFOTRON;
1425 else if (center_element == EL_MAMPFER)
1426 Store[x][y] = level.yam_content[game.yam_content_nr][x-ex+1][y-ey+1];
1427 else if (center_element == EL_AMOEBA2DIAM)
1428 Store[x][y] = level.amoeba_content;
1429 else if (element == EL_ERZ_EDEL)
1430 Store[x][y] = EL_EDELSTEIN;
1431 else if (element == EL_ERZ_DIAM)
1432 Store[x][y] = EL_DIAMANT;
1433 else if (element == EL_ERZ_EDEL_BD)
1434 Store[x][y] = EL_EDELSTEIN_BD;
1435 else if (element == EL_ERZ_EDEL_GELB)
1436 Store[x][y] = EL_EDELSTEIN_GELB;
1437 else if (element == EL_ERZ_EDEL_ROT)
1438 Store[x][y] = EL_EDELSTEIN_ROT;
1439 else if (element == EL_ERZ_EDEL_LILA)
1440 Store[x][y] = EL_EDELSTEIN_LILA;
1441 else if (element == EL_WALL_PEARL)
1442 Store[x][y] = EL_PEARL;
1443 else if (element == EL_WALL_CRYSTAL)
1444 Store[x][y] = EL_CRYSTAL;
1445 else if (!IS_PFORTE(Store[x][y]))
1446 Store[x][y] = EL_LEERRAUM;
1448 if (x != ex || y != ey ||
1449 center_element == EL_AMOEBA2DIAM || mode == EX_BORDER)
1450 Store2[x][y] = element;
1452 if (AmoebaNr[x][y] &&
1453 (element == EL_AMOEBE_VOLL ||
1454 element == EL_AMOEBE_BD ||
1455 element == EL_AMOEBING))
1457 AmoebaCnt[AmoebaNr[x][y]]--;
1458 AmoebaCnt2[AmoebaNr[x][y]]--;
1461 Feld[x][y] = EL_EXPLODING;
1462 MovDir[x][y] = MovPos[x][y] = 0;
1468 if (center_element == EL_MAMPFER)
1469 game.yam_content_nr = (game.yam_content_nr + 1) % level.num_yam_contents;
1480 Frame[x][y] = (phase < last_phase ? phase + 1 : 0);
1482 if (phase == first_phase_after_start)
1484 int element = Store2[x][y];
1486 if (element == EL_BLACK_ORB)
1488 Feld[x][y] = Store2[x][y];
1493 else if (phase == half_phase)
1495 int element = Store2[x][y];
1497 if (IS_PLAYER(x, y))
1498 KillHeroUnlessProtected(x, y);
1499 else if (IS_EXPLOSIVE(element))
1501 Feld[x][y] = Store2[x][y];
1505 else if (element == EL_AMOEBA2DIAM)
1506 AmoebeUmwandeln(x, y);
1509 if (phase == last_phase)
1513 element = Feld[x][y] = Store[x][y];
1514 Store[x][y] = Store2[x][y] = 0;
1515 MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
1516 InitField(x, y, FALSE);
1517 if (CAN_MOVE(element) || COULD_MOVE(element))
1519 DrawLevelField(x, y);
1521 if (IS_PLAYER(x, y) && !PLAYERINFO(x,y)->present)
1522 StorePlayer[x][y] = 0;
1524 else if (!(phase % delay) && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
1526 int graphic = GFX_EXPLOSION;
1528 if (game.emulation == EMU_SUPAPLEX)
1529 graphic = (Store[x][y] == EL_SP_INFOTRON ?
1530 GFX_SP_EXPLODE_INFOTRON :
1531 GFX_SP_EXPLODE_EMPTY);
1534 ErdreichAnbroeckeln(SCREENX(x), SCREENY(y));
1536 graphic += (phase / delay - 1);
1538 if (IS_PFORTE(Store[x][y]))
1540 DrawLevelElement(x, y, Store[x][y]);
1541 DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic);
1544 DrawGraphic(SCREENX(x), SCREENY(y), graphic);
1548 void DynaExplode(int ex, int ey)
1551 int dynabomb_size = 1;
1552 boolean dynabomb_xl = FALSE;
1553 struct PlayerInfo *player;
1554 static int xy[4][2] =
1562 if (IS_ACTIVE_BOMB(Feld[ex][ey]))
1564 player = &stored_player[Feld[ex][ey] - EL_DYNABOMB_ACTIVE_1];
1565 dynabomb_size = player->dynabomb_size;
1566 dynabomb_xl = player->dynabomb_xl;
1567 player->dynabombs_left++;
1570 Explode(ex, ey, EX_PHASE_START, EX_CENTER);
1574 for (j=1; j<=dynabomb_size; j++)
1576 int x = ex + j * xy[i % 4][0];
1577 int y = ey + j * xy[i % 4][1];
1580 if (!IN_LEV_FIELD(x, y) || IS_MASSIVE(Feld[x][y]))
1583 element = Feld[x][y];
1585 /* do not restart explosions of fields with active bombs */
1586 if (element == EL_EXPLODING && IS_ACTIVE_BOMB(Store2[x][y]))
1589 Explode(x, y, EX_PHASE_START, EX_BORDER);
1591 if (element != EL_LEERRAUM &&
1592 element != EL_ERDREICH &&
1593 element != EL_EXPLODING &&
1600 void Bang(int x, int y)
1602 int element = Feld[x][y];
1604 if (game.emulation == EMU_SUPAPLEX)
1605 PlaySoundLevel(x, y, SND_SP_BOOOM);
1607 PlaySoundLevel(x, y, SND_ROAAAR);
1610 if (IS_PLAYER(x, y)) /* remove objects that might cause smaller explosion */
1611 element = EL_LEERRAUM;
1625 RaiseScoreElement(element);
1626 Explode(x, y, EX_PHASE_START, EX_NORMAL);
1628 case EL_DYNABOMB_ACTIVE_1:
1629 case EL_DYNABOMB_ACTIVE_2:
1630 case EL_DYNABOMB_ACTIVE_3:
1631 case EL_DYNABOMB_ACTIVE_4:
1632 case EL_DYNABOMB_NR:
1633 case EL_DYNABOMB_SZ:
1634 case EL_DYNABOMB_XL:
1640 if (IS_PLAYER(x, y))
1641 Explode(x, y, EX_PHASE_START, EX_NORMAL);
1643 Explode(x, y, EX_PHASE_START, EX_CENTER);
1646 Explode(x, y, EX_PHASE_START, EX_NORMAL);
1651 void Blurb(int x, int y)
1653 int element = Feld[x][y];
1655 if (element != EL_BLURB_LEFT && element != EL_BLURB_RIGHT) /* start */
1657 PlaySoundLevel(x, y, SND_BLURB);
1658 if (IN_LEV_FIELD(x-1, y) && IS_FREE(x-1, y) &&
1659 (!IN_LEV_FIELD(x-1, y-1) ||
1660 !CAN_FALL(MovingOrBlocked2Element(x-1, y-1))))
1662 Feld[x-1][y] = EL_BLURB_LEFT;
1664 if (IN_LEV_FIELD(x+1, y) && IS_FREE(x+1, y) &&
1665 (!IN_LEV_FIELD(x+1, y-1) ||
1666 !CAN_FALL(MovingOrBlocked2Element(x+1, y-1))))
1668 Feld[x+1][y] = EL_BLURB_RIGHT;
1673 int graphic = (element==EL_BLURB_LEFT ? GFX_BLURB_LEFT : GFX_BLURB_RIGHT);
1675 if (!MovDelay[x][y]) /* initialize animation counter */
1678 if (MovDelay[x][y]) /* continue animation */
1681 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
1682 DrawGraphic(SCREENX(x), SCREENY(y), graphic+4-MovDelay[x][y]/2);
1684 if (!MovDelay[x][y])
1686 Feld[x][y] = EL_LEERRAUM;
1687 DrawLevelField(x, y);
1693 static void ToggleBeltSwitch(int x, int y)
1695 static int belt_base_element[4] =
1697 EL_BELT1_SWITCH_LEFT,
1698 EL_BELT2_SWITCH_LEFT,
1699 EL_BELT3_SWITCH_LEFT,
1700 EL_BELT4_SWITCH_LEFT
1702 static int belt_move_dir[4] =
1710 int element = Feld[x][y];
1711 int belt_nr = getBeltNrFromSwitchElement(element);
1712 int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
1713 int belt_dir = belt_move_dir[belt_dir_nr];
1716 if (!IS_BELT_SWITCH(element))
1719 game.belt_dir_nr[belt_nr] = belt_dir_nr;
1720 game.belt_dir[belt_nr] = belt_dir;
1722 if (belt_dir_nr == 3)
1725 for (yy=0; yy<lev_fieldy; yy++)
1727 for (xx=0; xx<lev_fieldx; xx++)
1729 int element = Feld[xx][yy];
1731 if (IS_BELT_SWITCH(element))
1733 int e_belt_nr = getBeltNrFromSwitchElement(element);
1735 if (e_belt_nr == belt_nr)
1737 Feld[xx][yy] = belt_base_element[belt_nr] + belt_dir_nr;
1738 DrawLevelField(xx, yy);
1741 else if (belt_dir == MV_NO_MOVING && IS_BELT(element))
1743 int e_belt_nr = getBeltNrFromElement(element);
1745 if (e_belt_nr == belt_nr)
1746 DrawLevelField(xx, yy); /* set belt to parking position */
1752 static void ToggleSwitchgateSwitch(int x, int y)
1756 game.switchgate_pos = !game.switchgate_pos;
1758 for (yy=0; yy<lev_fieldy; yy++)
1760 for (xx=0; xx<lev_fieldx; xx++)
1762 int element = Feld[xx][yy];
1764 if (element == EL_SWITCHGATE_SWITCH_1 ||
1765 element == EL_SWITCHGATE_SWITCH_2)
1767 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_1 + game.switchgate_pos;
1768 DrawLevelField(xx, yy);
1770 else if (element == EL_SWITCHGATE_OPEN ||
1771 element == EL_SWITCHGATE_OPENING)
1773 Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
1774 PlaySoundLevel(xx, yy, SND_OEFFNEN);
1776 else if (element == EL_SWITCHGATE_CLOSED ||
1777 element == EL_SWITCHGATE_CLOSING)
1779 Feld[xx][yy] = EL_SWITCHGATE_OPENING;
1780 PlaySoundLevel(xx, yy, SND_OEFFNEN);
1786 static void RedrawAllLightSwitchesAndInvisibleElements()
1790 for (y=0; y<lev_fieldy; y++)
1792 for (x=0; x<lev_fieldx; x++)
1794 int element = Feld[x][y];
1796 if (element == EL_LIGHT_SWITCH_OFF &&
1797 game.light_time_left > 0)
1799 Feld[x][y] = EL_LIGHT_SWITCH_ON;
1800 DrawLevelField(x, y);
1802 else if (element == EL_LIGHT_SWITCH_ON &&
1803 game.light_time_left == 0)
1805 Feld[x][y] = EL_LIGHT_SWITCH_OFF;
1806 DrawLevelField(x, y);
1809 if (element == EL_INVISIBLE_STEEL ||
1810 element == EL_UNSICHTBAR ||
1811 element == EL_SAND_INVISIBLE)
1812 DrawLevelField(x, y);
1817 static void ToggleLightSwitch(int x, int y)
1819 int element = Feld[x][y];
1821 game.light_time_left =
1822 (element == EL_LIGHT_SWITCH_OFF ?
1823 level.time_light * FRAMES_PER_SECOND : 0);
1825 RedrawAllLightSwitchesAndInvisibleElements();
1828 static void ActivateTimegateSwitch(int x, int y)
1832 game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
1834 for (yy=0; yy<lev_fieldy; yy++)
1836 for (xx=0; xx<lev_fieldx; xx++)
1838 int element = Feld[xx][yy];
1840 if (element == EL_TIMEGATE_CLOSED ||
1841 element == EL_TIMEGATE_CLOSING)
1843 Feld[xx][yy] = EL_TIMEGATE_OPENING;
1844 PlaySoundLevel(xx, yy, SND_OEFFNEN);
1848 else if (element == EL_TIMEGATE_SWITCH_ON)
1850 Feld[xx][yy] = EL_TIMEGATE_SWITCH_OFF;
1851 DrawLevelField(xx, yy);
1858 Feld[x][y] = EL_TIMEGATE_SWITCH_ON;
1861 void Impact(int x, int y)
1863 boolean lastline = (y == lev_fieldy-1);
1864 boolean object_hit = FALSE;
1865 int element = Feld[x][y];
1868 if (!lastline) /* check if element below was hit */
1870 if (Feld[x][y+1] == EL_PLAYER_IS_LEAVING)
1873 object_hit = (!IS_FREE(x, y+1) && (!IS_MOVING(x, y+1) ||
1874 MovDir[x][y+1]!=MV_DOWN ||
1875 MovPos[x][y+1]<=TILEY/2));
1877 smashed = MovingOrBlocked2Element(x, y+1);
1880 if (!lastline && smashed == EL_SALZSAEURE) /* element falls into acid */
1886 if ((element == EL_BOMBE ||
1887 element == EL_SP_DISK_ORANGE ||
1888 element == EL_DX_SUPABOMB) &&
1889 (lastline || object_hit)) /* element is bomb */
1894 else if (element == EL_PEARL)
1896 Feld[x][y] = EL_PEARL_BREAKING;
1897 PlaySoundLevel(x, y, SND_KNACK);
1901 if (element == EL_TROPFEN && (lastline || object_hit)) /* acid drop */
1903 if (object_hit && IS_PLAYER(x, y+1))
1904 KillHeroUnlessProtected(x, y+1);
1905 else if (object_hit && smashed == EL_PINGUIN)
1909 Feld[x][y] = EL_AMOEBING;
1910 Store[x][y] = EL_AMOEBE_NASS;
1915 if (!lastline && object_hit) /* check which object was hit */
1917 if (CAN_CHANGE(element) &&
1918 (smashed == EL_MAGIC_WALL_OFF || smashed == EL_MAGIC_WALL_BD_OFF))
1921 int activated_magic_wall =
1922 (smashed == EL_MAGIC_WALL_OFF ? EL_MAGIC_WALL_EMPTY :
1923 EL_MAGIC_WALL_BD_EMPTY);
1925 /* activate magic wall / mill */
1927 for (y=0; y<lev_fieldy; y++)
1928 for (x=0; x<lev_fieldx; x++)
1929 if (Feld[x][y] == smashed)
1930 Feld[x][y] = activated_magic_wall;
1932 game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
1933 game.magic_wall_active = TRUE;
1936 if (IS_PLAYER(x, y+1))
1938 KillHeroUnlessProtected(x, y+1);
1941 else if (smashed == EL_PINGUIN)
1946 else if (element == EL_EDELSTEIN_BD)
1948 if (IS_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
1954 else if ((element == EL_SP_INFOTRON || element == EL_SP_ZONK) &&
1955 (smashed == EL_SP_SNIKSNAK || smashed == EL_SP_ELECTRON ||
1956 smashed == EL_SP_DISK_ORANGE))
1961 else if (element == EL_FELSBROCKEN ||
1962 element == EL_SP_ZONK ||
1963 element == EL_BD_ROCK)
1965 if (IS_ENEMY(smashed) ||
1966 smashed == EL_BOMBE || smashed == EL_SP_DISK_ORANGE ||
1967 smashed == EL_DX_SUPABOMB ||
1968 smashed == EL_SONDE || smashed == EL_SCHWEIN ||
1969 smashed == EL_DRACHE || smashed == EL_MOLE)
1974 else if (!IS_MOVING(x, y+1))
1976 if (smashed == EL_BIRNE_AUS || smashed == EL_BIRNE_EIN)
1981 else if (smashed == EL_KOKOSNUSS)
1983 Feld[x][y+1] = EL_CRACKINGNUT;
1984 PlaySoundLevel(x, y, SND_KNACK);
1985 RaiseScoreElement(EL_KOKOSNUSS);
1988 else if (smashed == EL_PEARL)
1990 Feld[x][y+1] = EL_PEARL_BREAKING;
1991 PlaySoundLevel(x, y, SND_KNACK);
1994 else if (smashed == EL_DIAMANT)
1996 Feld[x][y+1] = EL_LEERRAUM;
1997 PlaySoundLevel(x, y, SND_QUIRK);
2000 else if (IS_BELT_SWITCH(smashed))
2002 ToggleBeltSwitch(x, y+1);
2004 else if (smashed == EL_SWITCHGATE_SWITCH_1 ||
2005 smashed == EL_SWITCHGATE_SWITCH_2)
2007 ToggleSwitchgateSwitch(x, y+1);
2009 else if (smashed == EL_LIGHT_SWITCH_OFF ||
2010 smashed == EL_LIGHT_SWITCH_ON)
2012 ToggleLightSwitch(x, y+1);
2018 /* play sound of magic wall / mill */
2020 (Feld[x][y+1] == EL_MAGIC_WALL_EMPTY ||
2021 Feld[x][y+1] == EL_MAGIC_WALL_BD_EMPTY))
2023 PlaySoundLevel(x, y, SND_QUIRK);
2027 /* play sound of object that hits the ground */
2028 if (lastline || object_hit)
2035 case EL_EDELSTEIN_BD:
2036 case EL_EDELSTEIN_GELB:
2037 case EL_EDELSTEIN_ROT:
2038 case EL_EDELSTEIN_LILA:
2040 case EL_SP_INFOTRON:
2046 case EL_FELSBROCKEN:
2051 sound = SND_SP_ZONKDOWN;
2063 PlaySoundLevel(x, y, sound);
2067 void TurnRound(int x, int y)
2079 { 0, 0 }, { 0, 0 }, { 0, 0 },
2084 int left, right, back;
2088 { MV_DOWN, MV_UP, MV_RIGHT },
2089 { MV_UP, MV_DOWN, MV_LEFT },
2091 { MV_LEFT, MV_RIGHT, MV_DOWN },
2092 { 0,0,0 }, { 0,0,0 }, { 0,0,0 },
2093 { MV_RIGHT, MV_LEFT, MV_UP }
2096 int element = Feld[x][y];
2097 int old_move_dir = MovDir[x][y];
2098 int left_dir = turn[old_move_dir].left;
2099 int right_dir = turn[old_move_dir].right;
2100 int back_dir = turn[old_move_dir].back;
2102 int left_dx = move_xy[left_dir].x, left_dy = move_xy[left_dir].y;
2103 int right_dx = move_xy[right_dir].x, right_dy = move_xy[right_dir].y;
2104 int move_dx = move_xy[old_move_dir].x, move_dy = move_xy[old_move_dir].y;
2105 int back_dx = move_xy[back_dir].x, back_dy = move_xy[back_dir].y;
2107 int left_x = x+left_dx, left_y = y+left_dy;
2108 int right_x = x+right_dx, right_y = y+right_dy;
2109 int move_x = x+move_dx, move_y = y+move_dy;
2111 if (element == EL_KAEFER || element == EL_BUTTERFLY)
2113 TestIfBadThingTouchesOtherBadThing(x, y);
2115 if (IN_LEV_FIELD(right_x, right_y) &&
2116 IS_FREE(right_x, right_y))
2117 MovDir[x][y] = right_dir;
2118 else if (!IN_LEV_FIELD(move_x, move_y) ||
2119 !IS_FREE(move_x, move_y))
2120 MovDir[x][y] = left_dir;
2122 if (element == EL_KAEFER && MovDir[x][y] != old_move_dir)
2124 else if (element == EL_BUTTERFLY) /* && MovDir[x][y] == left_dir) */
2127 else if (element == EL_FLIEGER || element == EL_FIREFLY ||
2128 element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
2130 TestIfBadThingTouchesOtherBadThing(x, y);
2132 if (IN_LEV_FIELD(left_x, left_y) &&
2133 IS_FREE(left_x, left_y))
2134 MovDir[x][y] = left_dir;
2135 else if (!IN_LEV_FIELD(move_x, move_y) ||
2136 !IS_FREE(move_x, move_y))
2137 MovDir[x][y] = right_dir;
2139 if ((element == EL_FLIEGER ||
2140 element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
2141 && MovDir[x][y] != old_move_dir)
2143 else if (element == EL_FIREFLY) /* && MovDir[x][y] == right_dir) */
2146 else if (element == EL_MAMPFER)
2148 boolean can_turn_left = FALSE, can_turn_right = FALSE;
2150 if (IN_LEV_FIELD(left_x, left_y) &&
2151 (IS_FREE_OR_PLAYER(left_x, left_y) ||
2152 Feld[left_x][left_y] == EL_DIAMANT))
2153 can_turn_left = TRUE;
2154 if (IN_LEV_FIELD(right_x, right_y) &&
2155 (IS_FREE_OR_PLAYER(right_x, right_y) ||
2156 Feld[right_x][right_y] == EL_DIAMANT))
2157 can_turn_right = TRUE;
2159 if (can_turn_left && can_turn_right)
2160 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
2161 else if (can_turn_left)
2162 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
2163 else if (can_turn_right)
2164 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
2166 MovDir[x][y] = back_dir;
2168 MovDelay[x][y] = 16+16*RND(3);
2170 else if (element == EL_MAMPFER2)
2172 boolean can_turn_left = FALSE, can_turn_right = FALSE;
2174 if (IN_LEV_FIELD(left_x, left_y) &&
2175 (IS_FREE_OR_PLAYER(left_x, left_y) ||
2176 IS_MAMPF2(Feld[left_x][left_y])))
2177 can_turn_left = TRUE;
2178 if (IN_LEV_FIELD(right_x, right_y) &&
2179 (IS_FREE_OR_PLAYER(right_x, right_y) ||
2180 IS_MAMPF2(Feld[right_x][right_y])))
2181 can_turn_right = TRUE;
2183 if (can_turn_left && can_turn_right)
2184 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
2185 else if (can_turn_left)
2186 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
2187 else if (can_turn_right)
2188 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
2190 MovDir[x][y] = back_dir;
2192 MovDelay[x][y] = 16+16*RND(3);
2194 else if (element == EL_PACMAN)
2196 boolean can_turn_left = FALSE, can_turn_right = FALSE;
2198 if (IN_LEV_FIELD(left_x, left_y) &&
2199 (IS_FREE_OR_PLAYER(left_x, left_y) ||
2200 IS_AMOEBOID(Feld[left_x][left_y])))
2201 can_turn_left = TRUE;
2202 if (IN_LEV_FIELD(right_x, right_y) &&
2203 (IS_FREE_OR_PLAYER(right_x, right_y) ||
2204 IS_AMOEBOID(Feld[right_x][right_y])))
2205 can_turn_right = TRUE;
2207 if (can_turn_left && can_turn_right)
2208 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
2209 else if (can_turn_left)
2210 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
2211 else if (can_turn_right)
2212 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
2214 MovDir[x][y] = back_dir;
2216 MovDelay[x][y] = 6+RND(40);
2218 else if (element == EL_SCHWEIN)
2220 boolean can_turn_left = FALSE, can_turn_right = FALSE, can_move_on = FALSE;
2221 boolean should_turn_left = FALSE, should_turn_right = FALSE;
2222 boolean should_move_on = FALSE;
2224 int rnd = RND(rnd_value);
2226 if (IN_LEV_FIELD(left_x, left_y) &&
2227 (IS_FREE(left_x, left_y) || IS_GEM(Feld[left_x][left_y])))
2228 can_turn_left = TRUE;
2229 if (IN_LEV_FIELD(right_x, right_y) &&
2230 (IS_FREE(right_x, right_y) || IS_GEM(Feld[right_x][right_y])))
2231 can_turn_right = TRUE;
2232 if (IN_LEV_FIELD(move_x, move_y) &&
2233 (IS_FREE(move_x, move_y) || IS_GEM(Feld[move_x][move_y])))
2236 if (can_turn_left &&
2238 (IN_LEV_FIELD(x+back_dx+left_dx, y+back_dy+left_dy) &&
2239 !IS_FREE(x+back_dx+left_dx, y+back_dy+left_dy))))
2240 should_turn_left = TRUE;
2241 if (can_turn_right &&
2243 (IN_LEV_FIELD(x+back_dx+right_dx, y+back_dy+right_dy) &&
2244 !IS_FREE(x+back_dx+right_dx, y+back_dy+right_dy))))
2245 should_turn_right = TRUE;
2247 (!can_turn_left || !can_turn_right ||
2248 (IN_LEV_FIELD(x+move_dx+left_dx, y+move_dy+left_dy) &&
2249 !IS_FREE(x+move_dx+left_dx, y+move_dy+left_dy)) ||
2250 (IN_LEV_FIELD(x+move_dx+right_dx, y+move_dy+right_dy) &&
2251 !IS_FREE(x+move_dx+right_dx, y+move_dy+right_dy))))
2252 should_move_on = TRUE;
2254 if (should_turn_left || should_turn_right || should_move_on)
2256 if (should_turn_left && should_turn_right && should_move_on)
2257 MovDir[x][y] = (rnd < rnd_value/3 ? left_dir :
2258 rnd < 2*rnd_value/3 ? right_dir :
2260 else if (should_turn_left && should_turn_right)
2261 MovDir[x][y] = (rnd < rnd_value/2 ? left_dir : right_dir);
2262 else if (should_turn_left && should_move_on)
2263 MovDir[x][y] = (rnd < rnd_value/2 ? left_dir : old_move_dir);
2264 else if (should_turn_right && should_move_on)
2265 MovDir[x][y] = (rnd < rnd_value/2 ? right_dir : old_move_dir);
2266 else if (should_turn_left)
2267 MovDir[x][y] = left_dir;
2268 else if (should_turn_right)
2269 MovDir[x][y] = right_dir;
2270 else if (should_move_on)
2271 MovDir[x][y] = old_move_dir;
2273 else if (can_move_on && rnd > rnd_value/8)
2274 MovDir[x][y] = old_move_dir;
2275 else if (can_turn_left && can_turn_right)
2276 MovDir[x][y] = (rnd < rnd_value/2 ? left_dir : right_dir);
2277 else if (can_turn_left && rnd > rnd_value/8)
2278 MovDir[x][y] = left_dir;
2279 else if (can_turn_right && rnd > rnd_value/8)
2280 MovDir[x][y] = right_dir;
2282 MovDir[x][y] = back_dir;
2284 if (!IS_FREE(x+move_xy[MovDir[x][y]].x, y+move_xy[MovDir[x][y]].y) &&
2285 !IS_GEM(Feld[x+move_xy[MovDir[x][y]].x][y+move_xy[MovDir[x][y]].y]))
2286 MovDir[x][y] = old_move_dir;
2290 else if (element == EL_DRACHE)
2292 boolean can_turn_left = FALSE, can_turn_right = FALSE, can_move_on = FALSE;
2294 int rnd = RND(rnd_value);
2296 if (IN_LEV_FIELD(left_x, left_y) && IS_FREE(left_x, left_y))
2297 can_turn_left = TRUE;
2298 if (IN_LEV_FIELD(right_x, right_y) && IS_FREE(right_x, right_y))
2299 can_turn_right = TRUE;
2300 if (IN_LEV_FIELD(move_x, move_y) && IS_FREE(move_x, move_y))
2303 if (can_move_on && rnd > rnd_value/8)
2304 MovDir[x][y] = old_move_dir;
2305 else if (can_turn_left && can_turn_right)
2306 MovDir[x][y] = (rnd < rnd_value/2 ? left_dir : right_dir);
2307 else if (can_turn_left && rnd > rnd_value/8)
2308 MovDir[x][y] = left_dir;
2309 else if (can_turn_right && rnd > rnd_value/8)
2310 MovDir[x][y] = right_dir;
2312 MovDir[x][y] = back_dir;
2314 if (!IS_FREE(x+move_xy[MovDir[x][y]].x, y+move_xy[MovDir[x][y]].y))
2315 MovDir[x][y] = old_move_dir;
2319 else if (element == EL_MOLE)
2321 boolean can_turn_left = FALSE, can_turn_right = FALSE, can_move_on = FALSE;
2323 if (IN_LEV_FIELD(move_x, move_y) &&
2324 (IS_FREE(move_x, move_y) || IS_AMOEBOID(Feld[move_x][move_y]) ||
2325 Feld[move_x][move_y] == EL_DEAMOEBING))
2330 if (IN_LEV_FIELD(left_x, left_y) &&
2331 (IS_FREE(left_x, left_y) || IS_AMOEBOID(Feld[left_x][left_y])))
2332 can_turn_left = TRUE;
2333 if (IN_LEV_FIELD(right_x, right_y) &&
2334 (IS_FREE(right_x, right_y) || IS_AMOEBOID(Feld[right_x][right_y])))
2335 can_turn_right = TRUE;
2337 if (can_turn_left && can_turn_right)
2338 MovDir[x][y] = (RND(2) ? left_dir : right_dir);
2339 else if (can_turn_left)
2340 MovDir[x][y] = left_dir;
2342 MovDir[x][y] = right_dir;
2345 if (MovDir[x][y] != old_move_dir)
2348 else if (element == EL_BALLOON)
2350 MovDir[x][y] = game.balloon_dir;
2353 else if (element == EL_SPRING_MOVING)
2355 if (!IN_LEV_FIELD(move_x, move_y) || !IS_FREE(move_x, move_y) ||
2356 (IN_LEV_FIELD(x, y+1) && IS_FREE(x, y+1)))
2358 Feld[x][y] = EL_SPRING;
2359 MovDir[x][y] = MV_NO_MOVING;
2363 else if (element == EL_ROBOT || element == EL_SONDE || element == EL_PINGUIN)
2365 int attr_x = -1, attr_y = -1;
2376 for (i=0; i<MAX_PLAYERS; i++)
2378 struct PlayerInfo *player = &stored_player[i];
2379 int jx = player->jx, jy = player->jy;
2381 if (!player->active)
2384 if (attr_x == -1 || ABS(jx-x)+ABS(jy-y) < ABS(attr_x-x)+ABS(attr_y-y))
2392 if (element == EL_ROBOT && ZX>=0 && ZY>=0)
2398 if (element == EL_PINGUIN)
2401 static int xy[4][2] =
2411 int ex = x + xy[i%4][0];
2412 int ey = y + xy[i%4][1];
2414 if (IN_LEV_FIELD(ex, ey) && Feld[ex][ey] == EL_AUSGANG_AUF)
2423 MovDir[x][y] = MV_NO_MOVING;
2425 MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
2427 MovDir[x][y] |= (AllPlayersGone ? MV_LEFT : MV_RIGHT);
2429 MovDir[x][y] |= (AllPlayersGone ? MV_DOWN : MV_UP);
2431 MovDir[x][y] |= (AllPlayersGone ? MV_UP : MV_DOWN);
2433 if (element == EL_ROBOT)
2437 if ((MovDir[x][y]&(MV_LEFT|MV_RIGHT)) && (MovDir[x][y]&(MV_UP|MV_DOWN)))
2438 MovDir[x][y] &= (RND(2) ? (MV_LEFT|MV_RIGHT) : (MV_UP|MV_DOWN));
2439 Moving2Blocked(x, y, &newx, &newy);
2441 if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
2442 MovDelay[x][y] = 8+8*!RND(3);
2444 MovDelay[x][y] = 16;
2452 if ((MovDir[x][y]&(MV_LEFT|MV_RIGHT)) && (MovDir[x][y]&(MV_UP|MV_DOWN)))
2454 boolean first_horiz = RND(2);
2455 int new_move_dir = MovDir[x][y];
2458 new_move_dir & (first_horiz ? (MV_LEFT|MV_RIGHT) : (MV_UP|MV_DOWN));
2459 Moving2Blocked(x, y, &newx, &newy);
2461 if (IN_LEV_FIELD(newx, newy) &&
2462 (IS_FREE(newx, newy) ||
2463 Feld[newx][newy] == EL_SALZSAEURE ||
2464 (element == EL_PINGUIN &&
2465 (Feld[newx][newy] == EL_AUSGANG_AUF ||
2466 IS_MAMPF3(Feld[newx][newy])))))
2470 new_move_dir & (!first_horiz ? (MV_LEFT|MV_RIGHT) : (MV_UP|MV_DOWN));
2471 Moving2Blocked(x, y, &newx, &newy);
2473 if (IN_LEV_FIELD(newx, newy) &&
2474 (IS_FREE(newx, newy) ||
2475 Feld[newx][newy] == EL_SALZSAEURE ||
2476 (element == EL_PINGUIN &&
2477 (Feld[newx][newy] == EL_AUSGANG_AUF ||
2478 IS_MAMPF3(Feld[newx][newy])))))
2481 MovDir[x][y] = old_move_dir;
2488 static boolean JustBeingPushed(int x, int y)
2492 for (i=0; i<MAX_PLAYERS; i++)
2494 struct PlayerInfo *player = &stored_player[i];
2496 if (player->active && player->Pushing && player->MovPos)
2498 int next_jx = player->jx + (player->jx - player->last_jx);
2499 int next_jy = player->jy + (player->jy - player->last_jy);
2501 if (x == next_jx && y == next_jy)
2509 void StartMoving(int x, int y)
2511 int element = Feld[x][y];
2516 if (CAN_FALL(element) && y<lev_fieldy-1)
2518 if ((x>0 && IS_PLAYER(x-1, y)) || (x<lev_fieldx-1 && IS_PLAYER(x+1, y)))
2519 if (JustBeingPushed(x, y))
2522 if (element == EL_MORAST_VOLL)
2524 if (IS_FREE(x, y+1))
2526 InitMovingField(x, y, MV_DOWN);
2527 Feld[x][y] = EL_QUICKSAND_EMPTYING;
2528 Store[x][y] = EL_FELSBROCKEN;
2530 else if (Feld[x][y+1] == EL_MORAST_LEER)
2532 if (!MovDelay[x][y])
2533 MovDelay[x][y] = TILEY + 1;
2542 Feld[x][y] = EL_MORAST_LEER;
2543 Feld[x][y+1] = EL_MORAST_VOLL;
2544 Store[x][y+1] = Store[x][y];
2548 else if ((element == EL_FELSBROCKEN || element == EL_BD_ROCK) &&
2549 Feld[x][y+1] == EL_MORAST_LEER)
2551 InitMovingField(x, y, MV_DOWN);
2552 Feld[x][y] = EL_QUICKSAND_FILLING;
2553 Store[x][y] = element;
2555 else if (element == EL_MAGIC_WALL_FULL)
2557 if (IS_FREE(x, y+1))
2559 InitMovingField(x, y, MV_DOWN);
2560 Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
2561 Store[x][y] = EL_CHANGED(Store[x][y]);
2563 else if (Feld[x][y+1] == EL_MAGIC_WALL_EMPTY)
2565 if (!MovDelay[x][y])
2566 MovDelay[x][y] = TILEY/4 + 1;
2575 Feld[x][y] = EL_MAGIC_WALL_EMPTY;
2576 Feld[x][y+1] = EL_MAGIC_WALL_FULL;
2577 Store[x][y+1] = EL_CHANGED(Store[x][y]);
2581 else if (element == EL_MAGIC_WALL_BD_FULL)
2583 if (IS_FREE(x, y+1))
2585 InitMovingField(x, y, MV_DOWN);
2586 Feld[x][y] = EL_MAGIC_WALL_BD_EMPTYING;
2587 Store[x][y] = EL_CHANGED2(Store[x][y]);
2589 else if (Feld[x][y+1] == EL_MAGIC_WALL_BD_EMPTY)
2591 if (!MovDelay[x][y])
2592 MovDelay[x][y] = TILEY/4 + 1;
2601 Feld[x][y] = EL_MAGIC_WALL_BD_EMPTY;
2602 Feld[x][y+1] = EL_MAGIC_WALL_BD_FULL;
2603 Store[x][y+1] = EL_CHANGED2(Store[x][y]);
2607 else if (CAN_CHANGE(element) &&
2608 (Feld[x][y+1] == EL_MAGIC_WALL_EMPTY ||
2609 Feld[x][y+1] == EL_MAGIC_WALL_BD_EMPTY))
2611 InitMovingField(x, y, MV_DOWN);
2613 (Feld[x][y+1] == EL_MAGIC_WALL_EMPTY ? EL_MAGIC_WALL_FILLING :
2614 EL_MAGIC_WALL_BD_FILLING);
2615 Store[x][y] = element;
2617 else if (CAN_SMASH(element) && Feld[x][y+1] == EL_SALZSAEURE)
2620 InitMovingField(x, y, MV_DOWN);
2621 Store[x][y] = EL_SALZSAEURE;
2623 else if (CAN_SMASH(element) && Feld[x][y+1] == EL_BLOCKED &&
2628 else if (IS_FREE(x, y+1))
2630 InitMovingField(x, y, MV_DOWN);
2632 else if (element == EL_TROPFEN)
2634 Feld[x][y] = EL_AMOEBING;
2635 Store[x][y] = EL_AMOEBE_NASS;
2637 /* Store[x][y+1] must be zero, because:
2638 (EL_MORAST_VOLL -> EL_FELSBROCKEN): Store[x][y+1] == EL_MORAST_LEER
2641 #if OLD_GAME_BEHAVIOUR
2642 else if (IS_SLIPPERY(Feld[x][y+1]) && !Store[x][y+1])
2644 else if (IS_SLIPPERY(Feld[x][y+1]) && !Store[x][y+1] &&
2645 !IS_FALLING(x, y+1) && !JustStopped[x][y+1] &&
2646 element != EL_DX_SUPABOMB)
2649 else if ((IS_SLIPPERY(Feld[x][y+1]) ||
2650 (IS_EM_SLIPPERY_WALL(Feld[x][y+1]) && IS_GEM(element))) &&
2651 !IS_FALLING(x, y+1) && !JustStopped[x][y+1] &&
2652 element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
2655 boolean left = (x>0 && IS_FREE(x-1, y) &&
2656 (IS_FREE(x-1, y+1) || Feld[x-1][y+1] == EL_SALZSAEURE));
2657 boolean right = (x<lev_fieldx-1 && IS_FREE(x+1, y) &&
2658 (IS_FREE(x+1, y+1) || Feld[x+1][y+1] == EL_SALZSAEURE));
2662 if (left && right &&
2663 (game.emulation != EMU_BOULDERDASH &&
2664 element != EL_BD_ROCK && element != EL_EDELSTEIN_BD))
2665 left = !(right = RND(2));
2667 InitMovingField(x, y, left ? MV_LEFT : MV_RIGHT);
2670 else if (IS_BELT(Feld[x][y+1]))
2672 boolean left_is_free = (x>0 && IS_FREE(x-1, y));
2673 boolean right_is_free = (x<lev_fieldx-1 && IS_FREE(x+1, y));
2674 int belt_nr = getBeltNrFromElement(Feld[x][y+1]);
2675 int belt_dir = game.belt_dir[belt_nr];
2677 if ((belt_dir == MV_LEFT && left_is_free) ||
2678 (belt_dir == MV_RIGHT && right_is_free))
2679 InitMovingField(x, y, belt_dir);
2682 else if (CAN_MOVE(element))
2686 if ((element == EL_SONDE || element == EL_BALLOON ||
2687 element == EL_SPRING_MOVING)
2688 && JustBeingPushed(x, y))
2691 if (!MovDelay[x][y]) /* start new movement phase */
2693 /* all objects that can change their move direction after each step */
2694 /* (MAMPFER, MAMPFER2 and PACMAN go straight until they hit a wall */
2696 if (element!=EL_MAMPFER && element!=EL_MAMPFER2 && element!=EL_PACMAN)
2699 if (MovDelay[x][y] && (element == EL_KAEFER ||
2700 element == EL_FLIEGER ||
2701 element == EL_SP_SNIKSNAK ||
2702 element == EL_SP_ELECTRON ||
2703 element == EL_MOLE))
2704 DrawLevelField(x, y);
2708 if (MovDelay[x][y]) /* wait some time before next movement */
2712 if (element == EL_ROBOT ||
2713 element == EL_MAMPFER || element == EL_MAMPFER2)
2715 int phase = MovDelay[x][y] % 8;
2720 if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
2721 DrawGraphic(SCREENX(x), SCREENY(y), el2gfx(element)+phase);
2723 if ((element == EL_MAMPFER || element == EL_MAMPFER2)
2724 && MovDelay[x][y]%4 == 3)
2725 PlaySoundLevel(x, y, SND_NJAM);
2727 else if (element == EL_SP_ELECTRON)
2728 DrawGraphicAnimation(x, y, GFX2_SP_ELECTRON, 8, 2, ANIM_NORMAL);
2729 else if (element == EL_DRACHE)
2732 int dir = MovDir[x][y];
2733 int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
2734 int dy = (dir == MV_UP ? -1 : dir == MV_DOWN ? +1 : 0);
2735 int graphic = (dir == MV_LEFT ? GFX_FLAMMEN_LEFT :
2736 dir == MV_RIGHT ? GFX_FLAMMEN_RIGHT :
2737 dir == MV_UP ? GFX_FLAMMEN_UP :
2738 dir == MV_DOWN ? GFX_FLAMMEN_DOWN : GFX_LEERRAUM);
2739 int phase = FrameCounter % 2;
2741 for (i=1; i<=3; i++)
2743 int xx = x + i*dx, yy = y + i*dy;
2744 int sx = SCREENX(xx), sy = SCREENY(yy);
2746 if (!IN_LEV_FIELD(xx, yy) ||
2747 IS_SOLID(Feld[xx][yy]) || Feld[xx][yy] == EL_EXPLODING)
2752 int flamed = MovingOrBlocked2Element(xx, yy);
2754 if (IS_ENEMY(flamed) || IS_EXPLOSIVE(flamed))
2757 RemoveMovingField(xx, yy);
2759 Feld[xx][yy] = EL_BURNING;
2760 if (IN_SCR_FIELD(sx, sy))
2761 DrawGraphic(sx, sy, graphic + phase*3 + i-1);
2765 if (Feld[xx][yy] == EL_BURNING)
2766 Feld[xx][yy] = EL_LEERRAUM;
2767 DrawLevelField(xx, yy);
2776 if (element == EL_KAEFER || element == EL_BUTTERFLY)
2778 PlaySoundLevel(x, y, SND_KLAPPER);
2780 else if (element == EL_FLIEGER || element == EL_FIREFLY)
2782 PlaySoundLevel(x, y, SND_ROEHR);
2785 /* now make next step */
2787 Moving2Blocked(x, y, &newx, &newy); /* get next screen position */
2789 if (IS_ENEMY(element) && IS_PLAYER(newx, newy) &&
2790 !PLAYER_PROTECTED(newx, newy))
2794 TestIfBadThingRunsIntoHero(x, y, MovDir[x][y]);
2797 /* enemy got the player */
2799 KillHero(PLAYERINFO(newx, newy));
2804 else if ((element == EL_PINGUIN || element == EL_ROBOT ||
2805 element == EL_SONDE || element == EL_BALLOON) &&
2806 IN_LEV_FIELD(newx, newy) &&
2807 MovDir[x][y] == MV_DOWN && Feld[newx][newy] == EL_SALZSAEURE)
2810 Store[x][y] = EL_SALZSAEURE;
2812 else if (element == EL_PINGUIN && IN_LEV_FIELD(newx, newy))
2814 if (Feld[newx][newy] == EL_AUSGANG_AUF)
2816 Feld[x][y] = EL_LEERRAUM;
2817 DrawLevelField(x, y);
2819 PlaySoundLevel(newx, newy, SND_BUING);
2820 if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
2821 DrawGraphicThruMask(SCREENX(newx), SCREENY(newy), el2gfx(element));
2823 local_player->friends_still_needed--;
2824 if (!local_player->friends_still_needed &&
2825 !local_player->GameOver && AllPlayersGone)
2826 local_player->LevelSolved = local_player->GameOver = TRUE;
2830 else if (IS_MAMPF3(Feld[newx][newy]))
2832 if (DigField(local_player, newx, newy, 0, 0, DF_DIG) == MF_MOVING)
2833 DrawLevelField(newx, newy);
2835 MovDir[x][y] = MV_NO_MOVING;
2837 else if (!IS_FREE(newx, newy))
2839 if (IS_PLAYER(x, y))
2840 DrawPlayerField(x, y);
2842 DrawLevelField(x, y);
2846 else if (element == EL_SCHWEIN && IN_LEV_FIELD(newx, newy))
2848 if (IS_GEM(Feld[newx][newy]))
2850 if (IS_MOVING(newx, newy))
2851 RemoveMovingField(newx, newy);
2854 Feld[newx][newy] = EL_LEERRAUM;
2855 DrawLevelField(newx, newy);
2858 else if (!IS_FREE(newx, newy))
2860 if (IS_PLAYER(x, y))
2861 DrawPlayerField(x, y);
2863 DrawLevelField(x, y);
2867 else if (element == EL_DRACHE && IN_LEV_FIELD(newx, newy))
2869 if (!IS_FREE(newx, newy))
2871 if (IS_PLAYER(x, y))
2872 DrawPlayerField(x, y);
2874 DrawLevelField(x, y);
2879 boolean wanna_flame = !RND(10);
2880 int dx = newx - x, dy = newy - y;
2881 int newx1 = newx+1*dx, newy1 = newy+1*dy;
2882 int newx2 = newx+2*dx, newy2 = newy+2*dy;
2883 int element1 = (IN_LEV_FIELD(newx1, newy1) ?
2884 MovingOrBlocked2Element(newx1, newy1) : EL_BETON);
2885 int element2 = (IN_LEV_FIELD(newx2, newy2) ?
2886 MovingOrBlocked2Element(newx2, newy2) : EL_BETON);
2888 if ((wanna_flame || IS_ENEMY(element1) || IS_ENEMY(element2)) &&
2889 element1 != EL_DRACHE && element2 != EL_DRACHE &&
2890 element1 != EL_BURNING && element2 != EL_BURNING)
2892 if (IS_PLAYER(x, y))
2893 DrawPlayerField(x, y);
2895 DrawLevelField(x, y);
2897 MovDelay[x][y] = 50;
2898 Feld[newx][newy] = EL_BURNING;
2899 if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_LEERRAUM)
2900 Feld[newx1][newy1] = EL_BURNING;
2901 if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_LEERRAUM)
2902 Feld[newx2][newy2] = EL_BURNING;
2907 else if (element == EL_MAMPFER && IN_LEV_FIELD(newx, newy) &&
2908 Feld[newx][newy] == EL_DIAMANT)
2910 if (IS_MOVING(newx, newy))
2911 RemoveMovingField(newx, newy);
2914 Feld[newx][newy] = EL_LEERRAUM;
2915 DrawLevelField(newx, newy);
2918 else if (element == EL_MAMPFER2 && IN_LEV_FIELD(newx, newy) &&
2919 IS_MAMPF2(Feld[newx][newy]))
2921 if (AmoebaNr[newx][newy])
2923 AmoebaCnt2[AmoebaNr[newx][newy]]--;
2924 if (Feld[newx][newy] == EL_AMOEBE_VOLL ||
2925 Feld[newx][newy] == EL_AMOEBE_BD)
2926 AmoebaCnt[AmoebaNr[newx][newy]]--;
2929 if (IS_MOVING(newx, newy))
2930 RemoveMovingField(newx, newy);
2933 Feld[newx][newy] = EL_LEERRAUM;
2934 DrawLevelField(newx, newy);
2937 else if ((element == EL_PACMAN || element == EL_MOLE)
2938 && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
2940 if (AmoebaNr[newx][newy])
2942 AmoebaCnt2[AmoebaNr[newx][newy]]--;
2943 if (Feld[newx][newy] == EL_AMOEBE_VOLL ||
2944 Feld[newx][newy] == EL_AMOEBE_BD)
2945 AmoebaCnt[AmoebaNr[newx][newy]]--;
2948 if (element == EL_MOLE)
2950 Feld[newx][newy] = EL_DEAMOEBING;
2951 MovDelay[newx][newy] = 0; /* start amoeba shrinking delay */
2952 return; /* wait for shrinking amoeba */
2954 else /* element == EL_PACMAN */
2956 Feld[newx][newy] = EL_LEERRAUM;
2957 DrawLevelField(newx, newy);
2960 else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
2961 (Feld[newx][newy] == EL_DEAMOEBING ||
2962 (Feld[newx][newy] == EL_LEERRAUM && Stop[newx][newy])))
2964 /* wait for shrinking amoeba to completely disappear */
2967 else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
2969 /* object was running against a wall */
2973 if (element == EL_KAEFER || element == EL_FLIEGER ||
2974 element == EL_SP_SNIKSNAK || element == EL_MOLE)
2975 DrawLevelField(x, y);
2976 else if (element == EL_BUTTERFLY || element == EL_FIREFLY)
2977 DrawGraphicAnimation(x, y, el2gfx(element), 2, 4, ANIM_NORMAL);
2978 else if (element == EL_SONDE)
2979 DrawGraphicAnimation(x, y, GFX_SONDE_START, 8, 2, ANIM_NORMAL);
2980 else if (element == EL_SP_ELECTRON)
2981 DrawGraphicAnimation(x, y, GFX2_SP_ELECTRON, 8, 2, ANIM_NORMAL);
2983 if (DONT_TOUCH(element))
2984 TestIfBadThingTouchesHero(x, y);
2989 if (element == EL_ROBOT && IN_SCR_FIELD(x, y))
2990 PlaySoundLevel(x, y, SND_SCHLURF);
2992 InitMovingField(x, y, MovDir[x][y]);
2996 ContinueMoving(x, y);
2999 void ContinueMoving(int x, int y)
3001 int element = Feld[x][y];
3002 int direction = MovDir[x][y];
3003 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
3004 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
3005 int horiz_move = (dx!=0);
3006 int newx = x + dx, newy = y + dy;
3007 int step = (horiz_move ? dx : dy) * TILEX / 8;
3009 if (element == EL_TROPFEN || element == EL_AMOEBA_DRIPPING)
3011 else if (element == EL_QUICKSAND_FILLING ||
3012 element == EL_QUICKSAND_EMPTYING)
3014 else if (element == EL_MAGIC_WALL_FILLING ||
3015 element == EL_MAGIC_WALL_BD_FILLING ||
3016 element == EL_MAGIC_WALL_EMPTYING ||
3017 element == EL_MAGIC_WALL_BD_EMPTYING)
3019 else if (CAN_FALL(element) && horiz_move &&
3020 y < lev_fieldy-1 && IS_BELT(Feld[x][y+1]))
3022 else if (element == EL_SPRING_MOVING)
3025 #if OLD_GAME_BEHAVIOUR
3026 else if (CAN_FALL(element) && horiz_move && !IS_SP_ELEMENT(element))
3030 MovPos[x][y] += step;
3032 if (ABS(MovPos[x][y])>=TILEX) /* object reached its destination */
3034 Feld[x][y] = EL_LEERRAUM;
3035 Feld[newx][newy] = element;
3037 if (element == EL_MOLE)
3040 static int xy[4][2] =
3048 Feld[x][y] = EL_ERDREICH;
3049 DrawLevelField(x, y);
3058 if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_ERDREICH)
3059 DrawLevelField(xx, yy); /* for "ErdreichAnbroeckeln()" */
3063 if (element == EL_QUICKSAND_FILLING)
3065 element = Feld[newx][newy] = get_next_element(element);
3066 Store[newx][newy] = Store[x][y];
3068 else if (element == EL_QUICKSAND_EMPTYING)
3070 Feld[x][y] = get_next_element(element);
3071 element = Feld[newx][newy] = Store[x][y];
3073 else if (element == EL_MAGIC_WALL_FILLING)
3075 element = Feld[newx][newy] = get_next_element(element);
3076 if (!game.magic_wall_active)
3077 element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
3078 Store[newx][newy] = Store[x][y];
3080 else if (element == EL_MAGIC_WALL_EMPTYING)
3082 Feld[x][y] = get_next_element(element);
3083 if (!game.magic_wall_active)
3084 Feld[x][y] = EL_MAGIC_WALL_DEAD;
3085 element = Feld[newx][newy] = Store[x][y];
3087 else if (element == EL_MAGIC_WALL_BD_FILLING)
3089 element = Feld[newx][newy] = get_next_element(element);
3090 if (!game.magic_wall_active)
3091 element = Feld[newx][newy] = EL_MAGIC_WALL_BD_DEAD;
3092 Store[newx][newy] = Store[x][y];
3094 else if (element == EL_MAGIC_WALL_BD_EMPTYING)
3096 Feld[x][y] = get_next_element(element);
3097 if (!game.magic_wall_active)
3098 Feld[x][y] = EL_MAGIC_WALL_BD_DEAD;
3099 element = Feld[newx][newy] = Store[x][y];
3101 else if (element == EL_AMOEBA_DRIPPING)
3103 Feld[x][y] = get_next_element(element);
3104 element = Feld[newx][newy] = Store[x][y];
3106 else if (Store[x][y] == EL_SALZSAEURE)
3108 element = Feld[newx][newy] = EL_SALZSAEURE;
3112 MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
3113 MovDelay[newx][newy] = 0;
3115 if (!CAN_MOVE(element))
3116 MovDir[newx][newy] = 0;
3118 DrawLevelField(x, y);
3119 DrawLevelField(newx, newy);
3121 Stop[newx][newy] = TRUE;
3122 JustStopped[newx][newy] = 3;
3124 if (DONT_TOUCH(element)) /* object may be nasty to player or others */
3126 TestIfBadThingTouchesHero(newx, newy);
3127 TestIfBadThingTouchesFriend(newx, newy);
3128 TestIfBadThingTouchesOtherBadThing(newx, newy);
3130 else if (element == EL_PINGUIN)
3131 TestIfFriendTouchesBadThing(newx, newy);
3133 if (CAN_SMASH(element) && direction == MV_DOWN &&
3134 (newy == lev_fieldy-1 || !IS_FREE(x, newy+1)))
3137 else /* still moving on */
3138 DrawLevelField(x, y);
3141 int AmoebeNachbarNr(int ax, int ay)
3144 int element = Feld[ax][ay];
3146 static int xy[4][2] =
3156 int x = ax + xy[i][0];
3157 int y = ay + xy[i][1];
3159 if (!IN_LEV_FIELD(x, y))
3162 if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
3163 group_nr = AmoebaNr[x][y];
3169 void AmoebenVereinigen(int ax, int ay)
3171 int i, x, y, xx, yy;
3172 int new_group_nr = AmoebaNr[ax][ay];
3173 static int xy[4][2] =
3181 if (new_group_nr == 0)
3189 if (!IN_LEV_FIELD(x, y))
3192 if ((Feld[x][y] == EL_AMOEBE_VOLL ||
3193 Feld[x][y] == EL_AMOEBE_BD ||
3194 Feld[x][y] == EL_AMOEBE_TOT) &&
3195 AmoebaNr[x][y] != new_group_nr)
3197 int old_group_nr = AmoebaNr[x][y];
3199 if (old_group_nr == 0)
3202 AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
3203 AmoebaCnt[old_group_nr] = 0;
3204 AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
3205 AmoebaCnt2[old_group_nr] = 0;
3207 for (yy=0; yy<lev_fieldy; yy++)
3209 for (xx=0; xx<lev_fieldx; xx++)
3211 if (AmoebaNr[xx][yy] == old_group_nr)
3212 AmoebaNr[xx][yy] = new_group_nr;
3219 void AmoebeUmwandeln(int ax, int ay)
3223 if (Feld[ax][ay] == EL_AMOEBE_TOT)
3225 int group_nr = AmoebaNr[ax][ay];
3230 printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
3231 printf("AmoebeUmwandeln(): This should never happen!\n");
3236 for (y=0; y<lev_fieldy; y++)
3238 for (x=0; x<lev_fieldx; x++)
3240 if (Feld[x][y] == EL_AMOEBE_TOT && AmoebaNr[x][y] == group_nr)
3243 Feld[x][y] = EL_AMOEBA2DIAM;
3251 static int xy[4][2] =
3264 if (!IN_LEV_FIELD(x, y))
3267 if (Feld[x][y] == EL_AMOEBA2DIAM)
3273 void AmoebeUmwandelnBD(int ax, int ay, int new_element)
3276 int group_nr = AmoebaNr[ax][ay];
3277 boolean done = FALSE;
3282 printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
3283 printf("AmoebeUmwandelnBD(): This should never happen!\n");
3288 for (y=0; y<lev_fieldy; y++)
3290 for (x=0; x<lev_fieldx; x++)
3292 if (AmoebaNr[x][y] == group_nr &&
3293 (Feld[x][y] == EL_AMOEBE_TOT ||
3294 Feld[x][y] == EL_AMOEBE_BD ||
3295 Feld[x][y] == EL_AMOEBING))
3298 Feld[x][y] = new_element;
3299 InitField(x, y, FALSE);
3300 DrawLevelField(x, y);
3307 PlaySoundLevel(ax, ay,
3308 (new_element == EL_BD_ROCK ? SND_KLOPF : SND_PLING));
3311 void AmoebeWaechst(int x, int y)
3313 static unsigned long sound_delay = 0;
3314 static unsigned long sound_delay_value = 0;
3316 if (!MovDelay[x][y]) /* start new growing cycle */
3320 if (DelayReached(&sound_delay, sound_delay_value))
3322 PlaySoundLevel(x, y, SND_AMOEBE);
3323 sound_delay_value = 30;
3327 if (MovDelay[x][y]) /* wait some time before growing bigger */
3330 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
3331 DrawGraphic(SCREENX(x), SCREENY(y), GFX_AMOEBING + 3 - MovDelay[x][y]/2);
3333 if (!MovDelay[x][y])
3335 Feld[x][y] = Store[x][y];
3337 DrawLevelField(x, y);
3342 void AmoebeSchrumpft(int x, int y)
3344 static unsigned long sound_delay = 0;
3345 static unsigned long sound_delay_value = 0;
3347 if (!MovDelay[x][y]) /* start new shrinking cycle */
3351 if (DelayReached(&sound_delay, sound_delay_value))
3353 PlaySoundLevel(x, y, SND_BLURB);
3354 sound_delay_value = 30;
3358 if (MovDelay[x][y]) /* wait some time before shrinking */
3361 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
3362 DrawGraphic(SCREENX(x), SCREENY(y), GFX_AMOEBING + MovDelay[x][y]/2);
3364 if (!MovDelay[x][y])
3366 Feld[x][y] = EL_LEERRAUM;
3367 DrawLevelField(x, y);
3369 /* don't let mole enter this field in this cycle;
3370 (give priority to objects falling to this field from above) */
3376 void AmoebeAbleger(int ax, int ay)
3379 int element = Feld[ax][ay];
3380 int newax = ax, neway = ay;
3381 static int xy[4][2] =
3389 if (!level.amoeba_speed)
3391 Feld[ax][ay] = EL_AMOEBE_TOT;
3392 DrawLevelField(ax, ay);
3396 if (!MovDelay[ax][ay]) /* start making new amoeba field */
3397 MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
3399 if (MovDelay[ax][ay]) /* wait some time before making new amoeba */
3402 if (MovDelay[ax][ay])
3406 if (element == EL_AMOEBE_NASS) /* object is an acid / amoeba drop */
3409 int x = ax + xy[start][0];
3410 int y = ay + xy[start][1];
3412 if (!IN_LEV_FIELD(x, y))
3415 if (IS_FREE(x, y) ||
3416 Feld[x][y] == EL_ERDREICH || Feld[x][y] == EL_MORAST_LEER)
3422 if (newax == ax && neway == ay)
3425 else /* normal or "filled" (BD style) amoeba */
3428 boolean waiting_for_player = FALSE;
3432 int j = (start + i) % 4;
3433 int x = ax + xy[j][0];
3434 int y = ay + xy[j][1];
3436 if (!IN_LEV_FIELD(x, y))
3439 if (IS_FREE(x, y) ||
3440 Feld[x][y] == EL_ERDREICH || Feld[x][y] == EL_MORAST_LEER)
3446 else if (IS_PLAYER(x, y))
3447 waiting_for_player = TRUE;
3450 if (newax == ax && neway == ay) /* amoeba cannot grow */
3452 if (i == 4 && (!waiting_for_player || game.emulation == EMU_BOULDERDASH))
3454 Feld[ax][ay] = EL_AMOEBE_TOT;
3455 DrawLevelField(ax, ay);
3456 AmoebaCnt[AmoebaNr[ax][ay]]--;
3458 if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0) /* amoeba is completely dead */
3460 if (element == EL_AMOEBE_VOLL)
3461 AmoebeUmwandeln(ax, ay);
3462 else if (element == EL_AMOEBE_BD)
3463 AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
3468 else if (element == EL_AMOEBE_VOLL || element == EL_AMOEBE_BD)
3470 /* amoeba gets larger by growing in some direction */
3472 int new_group_nr = AmoebaNr[ax][ay];
3475 if (new_group_nr == 0)
3477 printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
3478 printf("AmoebeAbleger(): This should never happen!\n");
3483 AmoebaNr[newax][neway] = new_group_nr;
3484 AmoebaCnt[new_group_nr]++;
3485 AmoebaCnt2[new_group_nr]++;
3487 /* if amoeba touches other amoeba(s) after growing, unify them */
3488 AmoebenVereinigen(newax, neway);
3490 if (element == EL_AMOEBE_BD && AmoebaCnt2[new_group_nr] >= 200)
3492 AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
3498 if (element != EL_AMOEBE_NASS || neway < ay || !IS_FREE(newax, neway) ||
3499 (neway == lev_fieldy - 1 && newax != ax))
3501 Feld[newax][neway] = EL_AMOEBING; /* simple growth of new amoeba tile */
3502 Store[newax][neway] = element;
3504 else if (neway == ay)
3505 Feld[newax][neway] = EL_TROPFEN; /* drop left or right from amoeba */
3508 InitMovingField(ax, ay, MV_DOWN); /* drop dripping out of amoeba */
3509 Feld[ax][ay] = EL_AMOEBA_DRIPPING;
3510 Store[ax][ay] = EL_TROPFEN;
3511 ContinueMoving(ax, ay);
3515 DrawLevelField(newax, neway);
3518 void Life(int ax, int ay)
3521 static int life[4] = { 2, 3, 3, 3 }; /* parameters for "game of life" */
3523 int element = Feld[ax][ay];
3528 if (!MovDelay[ax][ay]) /* start new "game of life" cycle */
3529 MovDelay[ax][ay] = life_time;
3531 if (MovDelay[ax][ay]) /* wait some time before next cycle */
3534 if (MovDelay[ax][ay])
3538 for (y1=-1; y1<2; y1++) for(x1=-1; x1<2; x1++)
3540 int xx = ax+x1, yy = ay+y1;
3543 if (!IN_LEV_FIELD(xx, yy))
3546 for (y2=-1; y2<2; y2++) for (x2=-1; x2<2; x2++)
3548 int x = xx+x2, y = yy+y2;
3550 if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
3553 if (((Feld[x][y] == element ||
3554 (element == EL_LIFE && IS_PLAYER(x, y))) &&
3556 (IS_FREE(x, y) && Stop[x][y]))
3560 if (xx == ax && yy == ay) /* field in the middle */
3562 if (nachbarn<life[0] || nachbarn>life[1])
3564 Feld[xx][yy] = EL_LEERRAUM;
3566 DrawLevelField(xx, yy);
3567 Stop[xx][yy] = TRUE;
3570 else if (IS_FREE(xx, yy) || Feld[xx][yy] == EL_ERDREICH)
3571 { /* free border field */
3572 if (nachbarn>=life[2] && nachbarn<=life[3])
3574 Feld[xx][yy] = element;
3575 MovDelay[xx][yy] = (element == EL_LIFE ? 0 : life_time-1);
3577 DrawLevelField(xx, yy);
3578 Stop[xx][yy] = TRUE;
3584 void Ablenk(int x, int y)
3586 if (!MovDelay[x][y]) /* next animation frame */
3587 MovDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
3589 if (MovDelay[x][y]) /* wait some time before next frame */
3594 if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
3595 DrawGraphic(SCREENX(x), SCREENY(y), GFX_ABLENK+MovDelay[x][y]%4);
3596 if (!(MovDelay[x][y]%4))
3597 PlaySoundLevel(x, y, SND_MIEP);
3602 Feld[x][y] = EL_ABLENK_AUS;
3603 DrawLevelField(x, y);
3604 if (ZX == x && ZY == y)
3608 void TimegateWheel(int x, int y)
3610 if (!MovDelay[x][y]) /* next animation frame */
3611 MovDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
3613 if (MovDelay[x][y]) /* wait some time before next frame */
3618 if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
3619 DrawGraphic(SCREENX(x), SCREENY(y),
3620 GFX_TIMEGATE_SWITCH + MovDelay[x][y]%4);
3621 if (!(MovDelay[x][y]%4))
3622 PlaySoundLevel(x, y, SND_MIEP);
3627 Feld[x][y] = EL_TIMEGATE_SWITCH_OFF;
3628 DrawLevelField(x, y);
3629 if (ZX == x && ZY == y)
3633 void Birne(int x, int y)
3635 if (!MovDelay[x][y]) /* next animation frame */
3636 MovDelay[x][y] = 800;
3638 if (MovDelay[x][y]) /* wait some time before next frame */
3643 if (!(MovDelay[x][y]%5))
3645 if (!(MovDelay[x][y]%10))
3646 Feld[x][y]=EL_ABLENK_EIN;
3648 Feld[x][y]=EL_ABLENK_AUS;
3649 DrawLevelField(x, y);
3650 Feld[x][y]=EL_ABLENK_EIN;
3656 Feld[x][y]=EL_ABLENK_AUS;
3657 DrawLevelField(x, y);
3658 if (ZX == x && ZY == y)
3662 void Blubber(int x, int y)
3664 if (y > 0 && IS_MOVING(x, y-1) && MovDir[x][y-1] == MV_DOWN)
3665 DrawLevelField(x, y-1);
3667 DrawGraphicAnimation(x, y, GFX_GEBLUBBER, 4, 10, ANIM_NORMAL);
3670 void NussKnacken(int x, int y)
3672 if (!MovDelay[x][y]) /* next animation frame */
3675 if (MovDelay[x][y]) /* wait some time before next frame */
3678 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
3679 DrawGraphic(SCREENX(x), SCREENY(y),
3680 GFX_CRACKINGNUT + 3 - MovDelay[x][y]/2);
3682 if (!MovDelay[x][y])
3684 Feld[x][y] = EL_EDELSTEIN;
3685 DrawLevelField(x, y);
3690 void BreakingPearl(int x, int y)
3692 if (!MovDelay[x][y]) /* next animation frame */
3695 if (MovDelay[x][y]) /* wait some time before next frame */
3698 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
3699 DrawGraphic(SCREENX(x), SCREENY(y),
3700 GFX_PEARL_BREAKING + 4 - MovDelay[x][y]/2);
3702 if (!MovDelay[x][y])
3704 Feld[x][y] = EL_LEERRAUM;
3705 DrawLevelField(x, y);
3710 void SiebAktivieren(int x, int y, int typ)
3712 int graphic = (typ == 1 ? GFX_MAGIC_WALL_FULL : GFX_MAGIC_WALL_BD_FULL) + 3;
3714 DrawGraphicAnimation(x, y, graphic, 4, 4, ANIM_REVERSE);
3717 void AusgangstuerPruefen(int x, int y)
3719 if (!local_player->gems_still_needed &&
3720 !local_player->sokobanfields_still_needed &&
3721 !local_player->lights_still_needed)
3723 Feld[x][y] = EL_AUSGANG_ACT;
3725 PlaySoundLevel(x < LEVELX(BX1) ? LEVELX(BX1) :
3726 (x > LEVELX(BX2) ? LEVELX(BX2) : x),
3727 y < LEVELY(BY1) ? LEVELY(BY1) :
3728 (y > LEVELY(BY2) ? LEVELY(BY2) : y),
3733 void AusgangstuerOeffnen(int x, int y)
3737 if (!MovDelay[x][y]) /* next animation frame */
3738 MovDelay[x][y] = 5*delay;
3740 if (MovDelay[x][y]) /* wait some time before next frame */
3745 tuer = MovDelay[x][y]/delay;
3746 if (!(MovDelay[x][y]%delay) && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
3747 DrawGraphic(SCREENX(x), SCREENY(y), GFX_AUSGANG_AUF-tuer);
3749 if (!MovDelay[x][y])
3751 Feld[x][y] = EL_AUSGANG_AUF;
3752 DrawLevelField(x, y);
3757 void AusgangstuerBlinken(int x, int y)
3759 DrawGraphicAnimation(x, y, GFX_AUSGANG_AUF, 4, 4, ANIM_OSCILLATE);
3762 void OpenSwitchgate(int x, int y)
3766 if (!MovDelay[x][y]) /* next animation frame */
3767 MovDelay[x][y] = 5 * delay;
3769 if (MovDelay[x][y]) /* wait some time before next frame */
3774 phase = MovDelay[x][y] / delay;
3775 if (!(MovDelay[x][y] % delay) && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
3776 DrawGraphic(SCREENX(x), SCREENY(y), GFX_SWITCHGATE_OPEN - phase);
3778 if (!MovDelay[x][y])
3780 Feld[x][y] = EL_SWITCHGATE_OPEN;
3781 DrawLevelField(x, y);
3786 void CloseSwitchgate(int x, int y)
3790 if (!MovDelay[x][y]) /* next animation frame */
3791 MovDelay[x][y] = 5 * delay;
3793 if (MovDelay[x][y]) /* wait some time before next frame */
3798 phase = MovDelay[x][y] / delay;
3799 if (!(MovDelay[x][y] % delay) && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
3800 DrawGraphic(SCREENX(x), SCREENY(y), GFX_SWITCHGATE_CLOSED + phase);
3802 if (!MovDelay[x][y])
3804 Feld[x][y] = EL_SWITCHGATE_CLOSED;
3805 DrawLevelField(x, y);
3810 void OpenTimegate(int x, int y)
3814 if (!MovDelay[x][y]) /* next animation frame */
3815 MovDelay[x][y] = 5 * delay;
3817 if (MovDelay[x][y]) /* wait some time before next frame */
3822 phase = MovDelay[x][y] / delay;
3823 if (!(MovDelay[x][y] % delay) && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
3824 DrawGraphic(SCREENX(x), SCREENY(y), GFX_TIMEGATE_OPEN - phase);
3826 if (!MovDelay[x][y])
3828 Feld[x][y] = EL_TIMEGATE_OPEN;
3829 DrawLevelField(x, y);
3834 void CloseTimegate(int x, int y)
3838 if (!MovDelay[x][y]) /* next animation frame */
3839 MovDelay[x][y] = 5 * delay;
3841 if (MovDelay[x][y]) /* wait some time before next frame */
3846 phase = MovDelay[x][y] / delay;
3847 if (!(MovDelay[x][y] % delay) && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
3848 DrawGraphic(SCREENX(x), SCREENY(y), GFX_TIMEGATE_CLOSED + phase);
3850 if (!MovDelay[x][y])
3852 Feld[x][y] = EL_TIMEGATE_CLOSED;
3853 DrawLevelField(x, y);
3858 static void CloseAllOpenTimegates()
3862 for (y=0; y<lev_fieldy; y++)
3864 for (x=0; x<lev_fieldx; x++)
3866 int element = Feld[x][y];
3868 if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
3870 Feld[x][y] = EL_TIMEGATE_CLOSING;
3871 PlaySoundLevel(x, y, SND_OEFFNEN);
3877 void EdelsteinFunkeln(int x, int y)
3879 if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
3882 if (Feld[x][y] == EL_EDELSTEIN_BD)
3883 DrawGraphicAnimation(x, y, GFX_EDELSTEIN_BD, 4, 4, ANIM_REVERSE);
3886 if (!MovDelay[x][y]) /* next animation frame */
3887 MovDelay[x][y] = 11 * !SimpleRND(500);
3889 if (MovDelay[x][y]) /* wait some time before next frame */
3893 if (setup.direct_draw && MovDelay[x][y])
3894 SetDrawtoField(DRAW_BUFFERED);
3896 DrawGraphic(SCREENX(x), SCREENY(y), el2gfx(Feld[x][y]));
3900 int phase = (MovDelay[x][y]-1)/2;
3905 DrawGraphicThruMask(SCREENX(x), SCREENY(y), GFX_FUNKELN_WEISS + phase);
3907 if (setup.direct_draw)
3911 dest_x = FX + SCREENX(x)*TILEX;
3912 dest_y = FY + SCREENY(y)*TILEY;
3914 BlitBitmap(drawto_field, window,
3915 dest_x, dest_y, TILEX, TILEY, dest_x, dest_y);
3916 SetDrawtoField(DRAW_DIRECT);
3923 void MauerWaechst(int x, int y)
3927 if (!MovDelay[x][y]) /* next animation frame */
3928 MovDelay[x][y] = 3*delay;
3930 if (MovDelay[x][y]) /* wait some time before next frame */
3935 phase = 2-MovDelay[x][y]/delay;
3936 if (!(MovDelay[x][y]%delay) && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
3937 DrawGraphic(SCREENX(x), SCREENY(y),
3938 (MovDir[x][y] == MV_LEFT ? GFX_MAUER_LEFT :
3939 MovDir[x][y] == MV_RIGHT ? GFX_MAUER_RIGHT :
3940 MovDir[x][y] == MV_UP ? GFX_MAUER_UP :
3941 GFX_MAUER_DOWN ) + phase);
3943 if (!MovDelay[x][y])
3945 if (MovDir[x][y] == MV_LEFT)
3947 if (IN_LEV_FIELD(x-1, y) && IS_MAUER(Feld[x-1][y]))
3948 DrawLevelField(x-1, y);
3950 else if (MovDir[x][y] == MV_RIGHT)
3952 if (IN_LEV_FIELD(x+1, y) && IS_MAUER(Feld[x+1][y]))
3953 DrawLevelField(x+1, y);
3955 else if (MovDir[x][y] == MV_UP)
3957 if (IN_LEV_FIELD(x, y-1) && IS_MAUER(Feld[x][y-1]))
3958 DrawLevelField(x, y-1);
3962 if (IN_LEV_FIELD(x, y+1) && IS_MAUER(Feld[x][y+1]))
3963 DrawLevelField(x, y+1);
3966 Feld[x][y] = Store[x][y];
3968 MovDir[x][y] = MV_NO_MOVING;
3969 DrawLevelField(x, y);
3974 void MauerAbleger(int ax, int ay)
3976 int element = Feld[ax][ay];
3977 boolean oben_frei = FALSE, unten_frei = FALSE;
3978 boolean links_frei = FALSE, rechts_frei = FALSE;
3979 boolean oben_massiv = FALSE, unten_massiv = FALSE;
3980 boolean links_massiv = FALSE, rechts_massiv = FALSE;
3982 if (!MovDelay[ax][ay]) /* start building new wall */
3983 MovDelay[ax][ay] = 6;
3985 if (MovDelay[ax][ay]) /* wait some time before building new wall */
3988 if (MovDelay[ax][ay])
3992 if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
3994 if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
3996 if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
3998 if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
4001 if (element == EL_MAUER_Y || element == EL_MAUER_XY)
4005 Feld[ax][ay-1] = EL_MAUERND;
4006 Store[ax][ay-1] = element;
4007 MovDir[ax][ay-1] = MV_UP;
4008 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
4009 DrawGraphic(SCREENX(ax), SCREENY(ay-1), GFX_MAUER_UP);
4013 Feld[ax][ay+1] = EL_MAUERND;
4014 Store[ax][ay+1] = element;
4015 MovDir[ax][ay+1] = MV_DOWN;
4016 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
4017 DrawGraphic(SCREENX(ax), SCREENY(ay+1), GFX_MAUER_DOWN);
4021 if (element == EL_MAUER_X || element == EL_MAUER_XY ||
4022 element == EL_MAUER_LEBT)
4026 Feld[ax-1][ay] = EL_MAUERND;
4027 Store[ax-1][ay] = element;
4028 MovDir[ax-1][ay] = MV_LEFT;
4029 if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
4030 DrawGraphic(SCREENX(ax-1), SCREENY(ay), GFX_MAUER_LEFT);
4034 Feld[ax+1][ay] = EL_MAUERND;
4035 Store[ax+1][ay] = element;
4036 MovDir[ax+1][ay] = MV_RIGHT;
4037 if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
4038 DrawGraphic(SCREENX(ax+1), SCREENY(ay), GFX_MAUER_RIGHT);
4042 if (element == EL_MAUER_LEBT && (links_frei || rechts_frei))
4043 DrawLevelField(ax, ay);
4045 if (!IN_LEV_FIELD(ax, ay-1) || IS_MAUER(Feld[ax][ay-1]))
4047 if (!IN_LEV_FIELD(ax, ay+1) || IS_MAUER(Feld[ax][ay+1]))
4048 unten_massiv = TRUE;
4049 if (!IN_LEV_FIELD(ax-1, ay) || IS_MAUER(Feld[ax-1][ay]))
4050 links_massiv = TRUE;
4051 if (!IN_LEV_FIELD(ax+1, ay) || IS_MAUER(Feld[ax+1][ay]))
4052 rechts_massiv = TRUE;
4054 if (((oben_massiv && unten_massiv) ||
4055 element == EL_MAUER_X || element == EL_MAUER_LEBT) &&
4056 ((links_massiv && rechts_massiv) ||
4057 element == EL_MAUER_Y))
4058 Feld[ax][ay] = EL_MAUERWERK;
4061 void CheckForDragon(int x, int y)
4064 boolean dragon_found = FALSE;
4065 static int xy[4][2] =
4077 int xx = x + j*xy[i][0], yy = y + j*xy[i][1];
4079 if (IN_LEV_FIELD(xx, yy) &&
4080 (Feld[xx][yy] == EL_BURNING || Feld[xx][yy] == EL_DRACHE))
4082 if (Feld[xx][yy] == EL_DRACHE)
4083 dragon_found = TRUE;
4096 int xx = x + j*xy[i][0], yy = y + j*xy[i][1];
4098 if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_BURNING)
4100 Feld[xx][yy] = EL_LEERRAUM;
4101 DrawLevelField(xx, yy);
4110 static void CheckBuggyBase(int x, int y)
4112 int element = Feld[x][y];
4114 if (element == EL_SP_BUG)
4116 if (!MovDelay[x][y]) /* wait some time before activating base */
4117 MovDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
4122 if (MovDelay[x][y] < 5 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
4123 DrawGraphic(SCREENX(x), SCREENY(y), GFX_SP_BUG_WARNING);
4127 Feld[x][y] = EL_SP_BUG_ACTIVE;
4130 else if (element == EL_SP_BUG_ACTIVE)
4132 if (!MovDelay[x][y]) /* start activating buggy base */
4133 MovDelay[x][y] = 1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND);
4141 static int xy[4][2] =
4149 if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
4150 DrawGraphic(SCREENX(x),SCREENY(y), GFX_SP_BUG_ACTIVE + SimpleRND(4));
4154 int xx = x + xy[i][0], yy = y + xy[i][1];
4156 if (IS_PLAYER(xx, yy))
4158 PlaySoundLevel(x, y, SND_SP_BUG);
4166 Feld[x][y] = EL_SP_BUG;
4167 DrawLevelField(x, y);
4172 static void CheckTrap(int x, int y)
4174 int element = Feld[x][y];
4176 if (element == EL_TRAP_INACTIVE)
4178 if (!MovDelay[x][y]) /* wait some time before activating trap */
4179 MovDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
4187 Feld[x][y] = EL_TRAP_ACTIVE;
4190 else if (element == EL_TRAP_ACTIVE)
4195 if (!MovDelay[x][y]) /* start activating trap */
4196 MovDelay[x][y] = num_frames * delay;
4204 if (!(MovDelay[x][y] % delay))
4206 int phase = MovDelay[x][y]/delay;
4208 if (phase >= num_frames/2)
4209 phase = num_frames - phase;
4211 if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
4213 DrawGraphic(SCREENX(x),SCREENY(y), GFX_TRAP_INACTIVE + phase - 1);
4214 ErdreichAnbroeckeln(SCREENX(x), SCREENY(y));
4221 Feld[x][y] = EL_TRAP_INACTIVE;
4222 DrawLevelField(x, y);
4227 static void DrawBeltAnimation(int x, int y, int element)
4229 int belt_nr = getBeltNrFromElement(element);
4230 int belt_dir = game.belt_dir[belt_nr];
4232 if (belt_dir != MV_NO_MOVING)
4235 int mode = (belt_dir == MV_LEFT ? ANIM_NORMAL : ANIM_REVERSE);
4236 int graphic = el2gfx(element) + (belt_dir == MV_LEFT ? 0 : 7);
4238 DrawGraphicAnimation(x, y, graphic, 8, delay, mode);
4242 static void PlayerActions(struct PlayerInfo *player, byte player_action)
4244 static byte stored_player_action[MAX_PLAYERS];
4245 static int num_stored_actions = 0;
4247 static boolean save_tape_entry = FALSE;
4249 boolean moved = FALSE, snapped = FALSE, bombed = FALSE;
4250 int left = player_action & JOY_LEFT;
4251 int right = player_action & JOY_RIGHT;
4252 int up = player_action & JOY_UP;
4253 int down = player_action & JOY_DOWN;
4254 int button1 = player_action & JOY_BUTTON_1;
4255 int button2 = player_action & JOY_BUTTON_2;
4256 int dx = (left ? -1 : right ? 1 : 0);
4257 int dy = (up ? -1 : down ? 1 : 0);
4259 stored_player_action[player->index_nr] = 0;
4260 num_stored_actions++;
4262 if (!player->active || tape.pausing)
4268 save_tape_entry = TRUE;
4270 player->frame_reset_delay = 0;
4273 snapped = SnapField(player, dx, dy);
4277 bombed = PlaceBomb(player);
4278 moved = MoveFigure(player, dx, dy);
4281 if (tape.single_step && tape.recording && !tape.pausing)
4283 if (button1 || (bombed && !moved))
4285 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
4286 SnapField(player, 0, 0); /* stop snapping */
4291 if (tape.recording && (moved || snapped || bombed))
4293 if (bombed && !moved)
4294 player_action &= JOY_BUTTON;
4296 stored_player_action[player->index_nr] = player_action;
4297 save_tape_entry = TRUE;
4299 else if (tape.playing && snapped)
4300 SnapField(player, 0, 0); /* stop snapping */
4302 stored_player_action[player->index_nr] = player_action;
4307 /* no actions for this player (no input at player's configured device) */
4309 DigField(player, 0, 0, 0, 0, DF_NO_PUSH);
4310 SnapField(player, 0, 0);
4311 CheckGravityMovement(player);
4314 if (player->MovPos == 0) /* needed for tape.playing */
4315 player->is_moving = FALSE;
4318 if (player->MovPos == 0) /* needed for tape.playing */
4319 player->last_move_dir = MV_NO_MOVING;
4321 /* !!! CHECK THIS AGAIN !!!
4322 (Seems to be needed for some EL_ROBOT stuff, but breaks
4323 tapes when walking through pipes!)
4326 /* it seems that "player->last_move_dir" is misused as some sort of
4327 "player->is_just_moving_in_this_moment", which is needed for the
4328 robot stuff (robots don't kill players when they are moving)
4332 if (++player->frame_reset_delay > player->move_delay_value)
4337 if (tape.recording && num_stored_actions >= MAX_PLAYERS && save_tape_entry)
4339 TapeRecordAction(stored_player_action);
4340 num_stored_actions = 0;
4341 save_tape_entry = FALSE;
4344 if (tape.recording && num_stored_actions >= MAX_PLAYERS)
4346 TapeRecordAction(stored_player_action);
4347 num_stored_actions = 0;
4352 if (tape.playing && !tape.pausing && !player_action &&
4353 tape.counter < tape.length)
4355 int jx = player->jx, jy = player->jy;
4357 tape.pos[tape.counter].action[player->index_nr] & (JOY_LEFT|JOY_RIGHT);
4359 if ((next_joy == JOY_LEFT || next_joy == JOY_RIGHT) &&
4360 (player->MovDir != JOY_UP && player->MovDir != JOY_DOWN))
4362 int dx = (next_joy == JOY_LEFT ? -1 : +1);
4364 if (IN_LEV_FIELD(jx+dx, jy) && IS_PUSHABLE(Feld[jx+dx][jy]))
4366 int el = Feld[jx+dx][jy];
4367 int push_delay = (IS_SB_ELEMENT(el) || el == EL_SONDE ? 2 :
4368 (el == EL_BALLOON || el == EL_SPRING) ? 0 : 10);
4370 if (tape.delay_played + push_delay >= tape.pos[tape.counter].delay)
4372 player->MovDir = next_joy;
4373 player->Frame = FrameCounter % 4;
4374 player->Pushing = TRUE;
4384 static unsigned long action_delay = 0;
4385 unsigned long action_delay_value;
4386 int sieb_x = 0, sieb_y = 0;
4387 int i, x, y, element;
4388 byte *recorded_player_action;
4389 byte summarized_player_action = 0;
4391 if (game_status != PLAYING)
4394 action_delay_value =
4395 (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
4397 if (tape.playing && tape.index_search && !tape.pausing)
4398 action_delay_value = 0;
4400 /* ---------- main game synchronization point ---------- */
4402 WaitUntilDelayReached(&action_delay, action_delay_value);
4404 if (network_playing && !network_player_action_received)
4408 printf("DEBUG: try to get network player actions in time\n");
4412 #if defined(PLATFORM_UNIX)
4413 /* last chance to get network player actions without main loop delay */
4417 if (game_status != PLAYING)
4420 if (!network_player_action_received)
4424 printf("DEBUG: failed to get network player actions in time\n");
4434 recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
4436 for (i=0; i<MAX_PLAYERS; i++)
4438 summarized_player_action |= stored_player[i].action;
4440 if (!network_playing)
4441 stored_player[i].effective_action = stored_player[i].action;
4444 #if defined(PLATFORM_UNIX)
4445 if (network_playing)
4446 SendToServer_MovePlayer(summarized_player_action);
4449 if (!options.network && !setup.team_mode)
4450 local_player->effective_action = summarized_player_action;
4452 for (i=0; i<MAX_PLAYERS; i++)
4454 int actual_player_action = stored_player[i].effective_action;
4456 if (stored_player[i].programmed_action)
4457 actual_player_action = stored_player[i].programmed_action;
4459 if (recorded_player_action)
4460 actual_player_action = recorded_player_action[i];
4462 PlayerActions(&stored_player[i], actual_player_action);
4463 ScrollFigure(&stored_player[i], SCROLL_GO_ON);
4466 network_player_action_received = FALSE;
4468 ScrollScreen(NULL, SCROLL_GO_ON);
4474 if (TimeFrames == 0 && local_player->active)
4476 extern unsigned int last_RND();
4478 printf("DEBUG: %03d last RND was %d \t [state checksum is %d]\n",
4479 TimePlayed, last_RND(), getStateCheckSum(TimePlayed));
4486 if (GameFrameDelay >= 500)
4487 printf("FrameCounter == %d\n", FrameCounter);
4496 for (y=0; y<lev_fieldy; y++) for (x=0; x<lev_fieldx; x++)
4499 if (JustStopped[x][y] > 0)
4500 JustStopped[x][y]--;
4503 if (IS_BLOCKED(x, y))
4507 Blocked2Moving(x, y, &oldx, &oldy);
4508 if (!IS_MOVING(oldx, oldy))
4510 printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
4511 printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
4512 printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
4513 printf("GameActions(): This should never happen!\n");
4519 for (y=0; y<lev_fieldy; y++) for (x=0; x<lev_fieldx; x++)
4521 element = Feld[x][y];
4523 if (IS_INACTIVE(element))
4526 if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
4530 if (IS_GEM(element) || element == EL_SP_INFOTRON)
4531 EdelsteinFunkeln(x, y);
4533 else if (IS_MOVING(x, y))
4534 ContinueMoving(x, y);
4535 else if (IS_ACTIVE_BOMB(element))
4536 CheckDynamite(x, y);
4538 else if (element == EL_EXPLODING && !game.explosions_delayed)
4539 Explode(x, y, Frame[x][y], EX_NORMAL);
4541 else if (element == EL_AMOEBING)
4542 AmoebeWaechst(x, y);
4543 else if (element == EL_DEAMOEBING)
4544 AmoebeSchrumpft(x, y);
4546 #if !USE_NEW_AMOEBA_CODE
4547 else if (IS_AMOEBALIVE(element))
4548 AmoebeAbleger(x, y);
4551 else if (element == EL_LIFE || element == EL_LIFE_ASYNC)
4553 else if (element == EL_ABLENK_EIN)
4555 else if (element == EL_TIMEGATE_SWITCH_ON)
4556 TimegateWheel(x, y);
4557 else if (element == EL_SALZSAEURE)
4559 else if (element == EL_BLURB_LEFT || element == EL_BLURB_RIGHT)
4561 else if (element == EL_CRACKINGNUT)
4563 else if (element == EL_PEARL_BREAKING)
4564 BreakingPearl(x, y);
4565 else if (element == EL_AUSGANG_ZU)
4566 AusgangstuerPruefen(x, y);
4567 else if (element == EL_AUSGANG_ACT)
4568 AusgangstuerOeffnen(x, y);
4569 else if (element == EL_AUSGANG_AUF)
4570 AusgangstuerBlinken(x, y);
4571 else if (element == EL_MAUERND)
4573 else if (element == EL_MAUER_LEBT ||
4574 element == EL_MAUER_X ||
4575 element == EL_MAUER_Y ||
4576 element == EL_MAUER_XY)
4578 else if (element == EL_BURNING)
4579 CheckForDragon(x, y);
4580 else if (element == EL_SP_BUG || element == EL_SP_BUG_ACTIVE)
4581 CheckBuggyBase(x, y);
4582 else if (element == EL_TRAP_INACTIVE || element == EL_TRAP_ACTIVE)
4584 else if (element == EL_SP_TERMINAL)
4585 DrawGraphicAnimation(x, y, GFX2_SP_TERMINAL, 7, 12, ANIM_NORMAL);
4586 else if (element == EL_SP_TERMINAL_ACTIVE)
4587 DrawGraphicAnimation(x, y, GFX2_SP_TERMINAL_ACTIVE, 7, 4, ANIM_NORMAL);
4588 else if (IS_BELT(element))
4589 DrawBeltAnimation(x, y, element);
4590 else if (element == EL_SWITCHGATE_OPENING)
4591 OpenSwitchgate(x, y);
4592 else if (element == EL_SWITCHGATE_CLOSING)
4593 CloseSwitchgate(x, y);
4594 else if (element == EL_TIMEGATE_OPENING)
4596 else if (element == EL_TIMEGATE_CLOSING)
4597 CloseTimegate(x, y);
4598 else if (element == EL_EXTRA_TIME)
4599 DrawGraphicAnimation(x, y, GFX_EXTRA_TIME, 6, 4, ANIM_NORMAL);
4600 else if (element == EL_SHIELD_PASSIVE)
4601 DrawGraphicAnimation(x, y, GFX_SHIELD_PASSIVE, 6, 4, ANIM_NORMAL);
4602 else if (element == EL_SHIELD_ACTIVE)
4603 DrawGraphicAnimation(x, y, GFX_SHIELD_ACTIVE, 6, 4, ANIM_NORMAL);
4605 if (game.magic_wall_active)
4607 boolean sieb = FALSE;
4608 int jx = local_player->jx, jy = local_player->jy;
4610 if (element == EL_MAGIC_WALL_FULL ||
4611 element == EL_MAGIC_WALL_EMPTY ||
4612 element == EL_MAGIC_WALL_EMPTYING)
4614 SiebAktivieren(x, y, 1);
4617 else if (element == EL_MAGIC_WALL_BD_FULL ||
4618 element == EL_MAGIC_WALL_BD_EMPTY ||
4619 element == EL_MAGIC_WALL_BD_EMPTYING)
4621 SiebAktivieren(x, y, 2);
4625 /* play the element sound at the position nearest to the player */
4626 if (sieb && ABS(x-jx)+ABS(y-jy) < ABS(sieb_x-jx)+ABS(sieb_y-jy))
4634 #if USE_NEW_AMOEBA_CODE
4635 /* new experimental amoeba growth stuff */
4637 if (!(FrameCounter % 8))
4640 static unsigned long random = 1684108901;
4642 for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
4645 x = (random >> 10) % lev_fieldx;
4646 y = (random >> 20) % lev_fieldy;
4648 x = RND(lev_fieldx);
4649 y = RND(lev_fieldy);
4651 element = Feld[x][y];
4653 if (!IS_PLAYER(x,y) &&
4654 (element == EL_LEERRAUM ||
4655 element == EL_ERDREICH ||
4656 element == EL_MORAST_LEER ||
4657 element == EL_BLURB_LEFT ||
4658 element == EL_BLURB_RIGHT))
4660 if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBE_NASS) ||
4661 (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBE_NASS) ||
4662 (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBE_NASS) ||
4663 (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBE_NASS))
4664 Feld[x][y] = EL_TROPFEN;
4667 random = random * 129 + 1;
4673 if (game.explosions_delayed)
4676 game.explosions_delayed = FALSE;
4678 for (y=0; y<lev_fieldy; y++) for (x=0; x<lev_fieldx; x++)
4680 element = Feld[x][y];
4682 if (ExplodeField[x][y])
4683 Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
4684 else if (element == EL_EXPLODING)
4685 Explode(x, y, Frame[x][y], EX_NORMAL);
4687 ExplodeField[x][y] = EX_NO_EXPLOSION;
4690 game.explosions_delayed = TRUE;
4693 if (game.magic_wall_active)
4695 if (!(game.magic_wall_time_left % 4))
4696 PlaySoundLevel(sieb_x, sieb_y, SND_MIEP);
4698 if (game.magic_wall_time_left > 0)
4700 game.magic_wall_time_left--;
4701 if (!game.magic_wall_time_left)
4703 for (y=0; y<lev_fieldy; y++) for (x=0; x<lev_fieldx; x++)
4705 element = Feld[x][y];
4707 if (element == EL_MAGIC_WALL_EMPTY ||
4708 element == EL_MAGIC_WALL_FULL)
4710 Feld[x][y] = EL_MAGIC_WALL_DEAD;
4711 DrawLevelField(x, y);
4713 else if (element == EL_MAGIC_WALL_BD_EMPTY ||
4714 element == EL_MAGIC_WALL_BD_FULL)
4716 Feld[x][y] = EL_MAGIC_WALL_BD_DEAD;
4717 DrawLevelField(x, y);
4721 game.magic_wall_active = FALSE;
4726 if (game.light_time_left > 0)
4728 game.light_time_left--;
4730 if (game.light_time_left == 0)
4732 for (y=0; y<lev_fieldy; y++) for (x=0; x<lev_fieldx; x++)
4734 element = Feld[x][y];
4736 if (element == EL_LIGHT_SWITCH_ON)
4738 Feld[x][y] = EL_LIGHT_SWITCH_OFF;
4739 DrawLevelField(x, y);
4741 else if (element == EL_INVISIBLE_STEEL ||
4742 element == EL_UNSICHTBAR ||
4743 element == EL_SAND_INVISIBLE)
4744 DrawLevelField(x, y);
4749 if (game.timegate_time_left > 0)
4751 game.timegate_time_left--;
4753 if (game.timegate_time_left == 0)
4754 CloseAllOpenTimegates();
4757 if (TimeFrames >= (1000 / GameFrameDelay))
4762 for (i=0; i<MAX_PLAYERS; i++)
4764 if (SHIELD_ON(&stored_player[i]))
4766 stored_player[i].shield_passive_time_left--;
4768 if (stored_player[i].shield_active_time_left > 0)
4769 stored_player[i].shield_active_time_left--;
4773 if (tape.recording || tape.playing)
4774 DrawVideoDisplay(VIDEO_STATE_TIME_ON, TimePlayed);
4780 if (TimeLeft <= 10 && setup.time_limit)
4781 PlaySoundStereo(SND_GONG, PSND_MAX_RIGHT);
4783 DrawText(DX_TIME, DY_TIME, int2str(TimeLeft, 3), FS_SMALL, FC_YELLOW);
4785 if (!TimeLeft && setup.time_limit)
4786 for (i=0; i<MAX_PLAYERS; i++)
4787 KillHero(&stored_player[i]);
4789 else if (level.time == 0) /* level without time limit */
4790 DrawText(DX_TIME, DY_TIME, int2str(TimePlayed, 3), FS_SMALL, FC_YELLOW);
4795 if (options.debug) /* calculate frames per second */
4797 static unsigned long fps_counter = 0;
4798 static int fps_frames = 0;
4799 unsigned long fps_delay_ms = Counter() - fps_counter;
4803 if (fps_delay_ms >= 500) /* calculate fps every 0.5 seconds */
4805 global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
4808 fps_counter = Counter();
4811 redraw_mask |= REDRAW_FPS;
4815 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
4817 int min_x = x, min_y = y, max_x = x, max_y = y;
4820 for (i=0; i<MAX_PLAYERS; i++)
4822 int jx = stored_player[i].jx, jy = stored_player[i].jy;
4824 if (!stored_player[i].active || &stored_player[i] == player)
4827 min_x = MIN(min_x, jx);
4828 min_y = MIN(min_y, jy);
4829 max_x = MAX(max_x, jx);
4830 max_y = MAX(max_y, jy);
4833 return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
4836 static boolean AllPlayersInVisibleScreen()
4840 for (i=0; i<MAX_PLAYERS; i++)
4842 int jx = stored_player[i].jx, jy = stored_player[i].jy;
4844 if (!stored_player[i].active)
4847 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
4854 void ScrollLevel(int dx, int dy)
4856 int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
4859 BlitBitmap(drawto_field, drawto_field,
4860 FX + TILEX*(dx == -1) - softscroll_offset,
4861 FY + TILEY*(dy == -1) - softscroll_offset,
4862 SXSIZE - TILEX*(dx!=0) + 2*softscroll_offset,
4863 SYSIZE - TILEY*(dy!=0) + 2*softscroll_offset,
4864 FX + TILEX*(dx == 1) - softscroll_offset,
4865 FY + TILEY*(dy == 1) - softscroll_offset);
4869 x = (dx == 1 ? BX1 : BX2);
4870 for (y=BY1; y<=BY2; y++)
4871 DrawScreenField(x, y);
4875 y = (dy == 1 ? BY1 : BY2);
4876 for (x=BX1; x<=BX2; x++)
4877 DrawScreenField(x, y);
4880 redraw_mask |= REDRAW_FIELD;
4883 static void CheckGravityMovement(struct PlayerInfo *player)
4885 if (level.gravity && !player->programmed_action)
4887 int move_dir_vertical = player->action & (MV_UP | MV_DOWN);
4888 int move_dir_horizontal = player->action & (MV_LEFT | MV_RIGHT);
4890 (player->last_move_dir & (MV_LEFT | MV_RIGHT) ?
4891 (move_dir_vertical ? move_dir_vertical : move_dir_horizontal) :
4892 (move_dir_horizontal ? move_dir_horizontal : move_dir_vertical));
4893 int jx = player->jx, jy = player->jy;
4894 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
4895 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
4896 int new_jx = jx + dx, new_jy = jy + dy;
4897 boolean field_under_player_is_free =
4898 (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
4899 boolean player_is_moving_to_valid_field =
4900 (IN_LEV_FIELD(new_jx, new_jy) &&
4901 (Feld[new_jx][new_jy] == EL_SP_BASE ||
4902 Feld[new_jx][new_jy] == EL_ERDREICH));
4904 if (field_under_player_is_free && !player_is_moving_to_valid_field)
4905 player->programmed_action = MV_DOWN;
4909 boolean MoveFigureOneStep(struct PlayerInfo *player,
4910 int dx, int dy, int real_dx, int real_dy)
4912 int jx = player->jx, jy = player->jy;
4913 int new_jx = jx+dx, new_jy = jy+dy;
4917 if (!player->active || (!dx && !dy))
4918 return MF_NO_ACTION;
4920 player->MovDir = (dx < 0 ? MV_LEFT :
4923 dy > 0 ? MV_DOWN : MV_NO_MOVING);
4925 if (!IN_LEV_FIELD(new_jx, new_jy))
4926 return MF_NO_ACTION;
4928 if (!options.network && !AllPlayersInSight(player, new_jx, new_jy))
4929 return MF_NO_ACTION;
4932 element = MovingOrBlocked2Element(new_jx, new_jy);
4934 element = MovingOrBlocked2ElementIfNotLeaving(new_jx, new_jy);
4937 if (DONT_GO_TO(element))
4939 if (element == EL_SALZSAEURE && dx == 0 && dy == 1)
4942 Feld[jx][jy] = EL_SPIELFIGUR;
4943 InitMovingField(jx, jy, MV_DOWN);
4944 Store[jx][jy] = EL_SALZSAEURE;
4945 ContinueMoving(jx, jy);
4949 TestIfHeroRunsIntoBadThing(jx, jy, player->MovDir);
4954 can_move = DigField(player, new_jx, new_jy, real_dx, real_dy, DF_DIG);
4955 if (can_move != MF_MOVING)
4958 StorePlayer[jx][jy] = 0;
4959 player->last_jx = jx;
4960 player->last_jy = jy;
4961 jx = player->jx = new_jx;
4962 jy = player->jy = new_jy;
4963 StorePlayer[jx][jy] = player->element_nr;
4966 (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
4968 ScrollFigure(player, SCROLL_INIT);
4973 boolean MoveFigure(struct PlayerInfo *player, int dx, int dy)
4975 int jx = player->jx, jy = player->jy;
4976 int old_jx = jx, old_jy = jy;
4977 int moved = MF_NO_ACTION;
4979 if (!player->active || (!dx && !dy))
4983 if (!FrameReached(&player->move_delay, player->move_delay_value) &&
4987 if (!FrameReached(&player->move_delay, player->move_delay_value) &&
4988 !(tape.playing && tape.game_version < GAME_VERSION_2_0))
4992 /* remove the last programmed player action */
4993 player->programmed_action = 0;
4997 /* should only happen if pre-1.2 tape recordings are played */
4998 /* this is only for backward compatibility */
5000 int original_move_delay_value = player->move_delay_value;
5003 printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES.\n");
5006 /* scroll remaining steps with finest movement resolution */
5007 player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
5009 while (player->MovPos)
5011 ScrollFigure(player, SCROLL_GO_ON);
5012 ScrollScreen(NULL, SCROLL_GO_ON);
5018 player->move_delay_value = original_move_delay_value;
5021 if (player->last_move_dir & (MV_LEFT | MV_RIGHT))
5023 if (!(moved |= MoveFigureOneStep(player, 0, dy, dx, dy)))
5024 moved |= MoveFigureOneStep(player, dx, 0, dx, dy);
5028 if (!(moved |= MoveFigureOneStep(player, dx, 0, dx, dy)))
5029 moved |= MoveFigureOneStep(player, 0, dy, dx, dy);
5035 if (moved & MF_MOVING && !ScreenMovPos &&
5036 (player == local_player || !options.network))
5038 int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
5039 int offset = (setup.scroll_delay ? 3 : 0);
5041 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
5043 /* actual player has left the screen -- scroll in that direction */
5044 if (jx != old_jx) /* player has moved horizontally */
5045 scroll_x += (jx - old_jx);
5046 else /* player has moved vertically */
5047 scroll_y += (jy - old_jy);
5051 if (jx != old_jx) /* player has moved horizontally */
5053 if ((player->MovDir == MV_LEFT && scroll_x > jx - MIDPOSX + offset) ||
5054 (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
5055 scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
5057 /* don't scroll over playfield boundaries */
5058 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
5059 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
5061 /* don't scroll more than one field at a time */
5062 scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
5064 /* don't scroll against the player's moving direction */
5065 if ((player->MovDir == MV_LEFT && scroll_x > old_scroll_x) ||
5066 (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
5067 scroll_x = old_scroll_x;
5069 else /* player has moved vertically */
5071 if ((player->MovDir == MV_UP && scroll_y > jy - MIDPOSY + offset) ||
5072 (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
5073 scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
5075 /* don't scroll over playfield boundaries */
5076 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
5077 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
5079 /* don't scroll more than one field at a time */
5080 scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
5082 /* don't scroll against the player's moving direction */
5083 if ((player->MovDir == MV_UP && scroll_y > old_scroll_y) ||
5084 (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
5085 scroll_y = old_scroll_y;
5089 if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
5091 if (!options.network && !AllPlayersInVisibleScreen())
5093 scroll_x = old_scroll_x;
5094 scroll_y = old_scroll_y;
5098 ScrollScreen(player, SCROLL_INIT);
5099 ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
5104 if (!(moved & MF_MOVING) && !player->Pushing)
5107 player->Frame = (player->Frame + 1) % 4;
5109 if (moved & MF_MOVING)
5111 if (old_jx != jx && old_jy == jy)
5112 player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
5113 else if (old_jx == jx && old_jy != jy)
5114 player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
5116 DrawLevelField(jx, jy); /* for "ErdreichAnbroeckeln()" */
5118 player->last_move_dir = player->MovDir;
5119 player->is_moving = TRUE;
5123 CheckGravityMovement(player);
5126 player->last_move_dir = MV_NO_MOVING;
5128 player->is_moving = FALSE;
5131 TestIfHeroTouchesBadThing(jx, jy);
5133 if (!player->active)
5139 void ScrollFigure(struct PlayerInfo *player, int mode)
5141 int jx = player->jx, jy = player->jy;
5142 int last_jx = player->last_jx, last_jy = player->last_jy;
5143 int move_stepsize = TILEX / player->move_delay_value;
5145 if (!player->active || !player->MovPos)
5148 if (mode == SCROLL_INIT)
5150 player->actual_frame_counter = FrameCounter;
5151 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
5153 if (Feld[last_jx][last_jy] == EL_LEERRAUM)
5154 Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
5159 else if (!FrameReached(&player->actual_frame_counter, 1))
5162 player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
5163 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
5165 if (Feld[last_jx][last_jy] == EL_PLAYER_IS_LEAVING)
5166 Feld[last_jx][last_jy] = EL_LEERRAUM;
5168 /* before DrawPlayer() to draw correct player graphic for this case */
5169 if (player->MovPos == 0)
5170 CheckGravityMovement(player);
5174 if (player->MovPos == 0)
5176 if (IS_QUICK_GATE(Feld[last_jx][last_jy]))
5178 /* continue with normal speed after quickly moving through gate */
5179 HALVE_PLAYER_SPEED(player);
5181 /* be able to make the next move without delay */
5182 player->move_delay = 0;
5185 player->last_jx = jx;
5186 player->last_jy = jy;
5188 if (Feld[jx][jy] == EL_AUSGANG_AUF)
5192 if (!local_player->friends_still_needed)
5193 player->LevelSolved = player->GameOver = TRUE;
5196 if (tape.single_step && tape.recording && !tape.pausing &&
5197 !player->programmed_action)
5198 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
5202 void ScrollScreen(struct PlayerInfo *player, int mode)
5204 static unsigned long screen_frame_counter = 0;
5206 if (mode == SCROLL_INIT)
5208 /* set scrolling step size according to actual player's moving speed */
5209 ScrollStepSize = TILEX / player->move_delay_value;
5211 screen_frame_counter = FrameCounter;
5212 ScreenMovDir = player->MovDir;
5213 ScreenMovPos = player->MovPos;
5214 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
5217 else if (!FrameReached(&screen_frame_counter, 1))
5222 ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
5223 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
5224 redraw_mask |= REDRAW_FIELD;
5227 ScreenMovDir = MV_NO_MOVING;
5230 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
5232 int i, kill_x = -1, kill_y = -1;
5233 static int test_xy[4][2] =
5240 static int test_dir[4] =
5250 int test_x, test_y, test_move_dir, test_element;
5252 test_x = good_x + test_xy[i][0];
5253 test_y = good_y + test_xy[i][1];
5254 if (!IN_LEV_FIELD(test_x, test_y))
5258 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NO_MOVING);
5261 test_element = Feld[test_x][test_y];
5263 test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
5266 /* 1st case: good thing is moving towards DONT_GO_TO style bad thing;
5267 2nd case: DONT_TOUCH style bad thing does not move away from good thing
5269 if ((DONT_GO_TO(test_element) && good_move_dir == test_dir[i]) ||
5270 (DONT_TOUCH(test_element) && test_move_dir != test_dir[i]))
5278 if (kill_x != -1 || kill_y != -1)
5280 if (IS_PLAYER(good_x, good_y))
5282 struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
5284 if (player->shield_active_time_left > 0)
5285 Bang(kill_x, kill_y);
5286 else if (!PLAYER_PROTECTED(good_x, good_y))
5290 Bang(good_x, good_y);
5294 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
5296 int i, kill_x = -1, kill_y = -1;
5297 int bad_element = Feld[bad_x][bad_y];
5298 static int test_xy[4][2] =
5305 static int test_dir[4] =
5313 if (bad_element == EL_EXPLODING) /* skip just exploding bad things */
5318 int test_x, test_y, test_move_dir, test_element;
5320 test_x = bad_x + test_xy[i][0];
5321 test_y = bad_y + test_xy[i][1];
5322 if (!IN_LEV_FIELD(test_x, test_y))
5326 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NO_MOVING);
5328 test_element = Feld[test_x][test_y];
5330 /* 1st case: good thing is moving towards DONT_GO_TO style bad thing;
5331 2nd case: DONT_TOUCH style bad thing does not move away from good thing
5333 if ((DONT_GO_TO(bad_element) && bad_move_dir == test_dir[i]) ||
5334 (DONT_TOUCH(bad_element) && test_move_dir != test_dir[i]))
5336 /* good thing is player or penguin that does not move away */
5337 if (IS_PLAYER(test_x, test_y))
5339 struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
5341 if (bad_element == EL_ROBOT && player->is_moving)
5342 continue; /* robot does not kill player if he is moving */
5348 else if (test_element == EL_PINGUIN)
5357 if (kill_x != -1 || kill_y != -1)
5359 if (IS_PLAYER(kill_x, kill_y))
5361 struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
5364 int dir = player->MovDir;
5365 int newx = player->jx + (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
5366 int newy = player->jy + (dir == MV_UP ? -1 : dir == MV_DOWN ? +1 : 0);
5368 if (Feld[bad_x][bad_y] == EL_ROBOT && player->is_moving &&
5369 newx != bad_x && newy != bad_y)
5370 ; /* robot does not kill player if he is moving */
5372 printf("-> %d\n", player->MovDir);
5374 if (Feld[bad_x][bad_y] == EL_ROBOT && player->is_moving &&
5375 newx != bad_x && newy != bad_y)
5376 ; /* robot does not kill player if he is moving */
5381 if (player->shield_active_time_left > 0)
5383 else if (!PLAYER_PROTECTED(kill_x, kill_y))
5387 Bang(kill_x, kill_y);
5391 void TestIfHeroTouchesBadThing(int x, int y)
5393 TestIfGoodThingHitsBadThing(x, y, MV_NO_MOVING);
5396 void TestIfHeroRunsIntoBadThing(int x, int y, int move_dir)
5398 TestIfGoodThingHitsBadThing(x, y, move_dir);
5401 void TestIfBadThingTouchesHero(int x, int y)
5403 TestIfBadThingHitsGoodThing(x, y, MV_NO_MOVING);
5406 void TestIfBadThingRunsIntoHero(int x, int y, int move_dir)
5408 TestIfBadThingHitsGoodThing(x, y, move_dir);
5411 void TestIfFriendTouchesBadThing(int x, int y)
5413 TestIfGoodThingHitsBadThing(x, y, MV_NO_MOVING);
5416 void TestIfBadThingTouchesFriend(int x, int y)
5418 TestIfBadThingHitsGoodThing(x, y, MV_NO_MOVING);
5421 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
5423 int i, kill_x = bad_x, kill_y = bad_y;
5424 static int xy[4][2] =
5436 x = bad_x + xy[i][0];
5437 y = bad_y + xy[i][1];
5438 if (!IN_LEV_FIELD(x, y))
5441 element = Feld[x][y];
5442 if (IS_AMOEBOID(element) || element == EL_LIFE ||
5443 element == EL_AMOEBING || element == EL_TROPFEN)
5451 if (kill_x != bad_x || kill_y != bad_y)
5455 void KillHero(struct PlayerInfo *player)
5457 int jx = player->jx, jy = player->jy;
5459 if (!player->active)
5462 if (IS_PFORTE(Feld[jx][jy]))
5463 Feld[jx][jy] = EL_LEERRAUM;
5465 /* deactivate shield (else Bang()/Explode() would not work right) */
5466 player->shield_passive_time_left = 0;
5467 player->shield_active_time_left = 0;
5473 static void KillHeroUnlessProtected(int x, int y)
5475 if (!PLAYER_PROTECTED(x, y))
5476 KillHero(PLAYERINFO(x, y));
5479 void BuryHero(struct PlayerInfo *player)
5481 int jx = player->jx, jy = player->jy;
5483 if (!player->active)
5486 PlaySoundLevel(jx, jy, SND_AUTSCH);
5487 PlaySoundLevel(jx, jy, SND_LACHEN);
5489 player->GameOver = TRUE;
5493 void RemoveHero(struct PlayerInfo *player)
5495 int jx = player->jx, jy = player->jy;
5496 int i, found = FALSE;
5498 player->present = FALSE;
5499 player->active = FALSE;
5501 if (!ExplodeField[jx][jy])
5502 StorePlayer[jx][jy] = 0;
5504 for (i=0; i<MAX_PLAYERS; i++)
5505 if (stored_player[i].active)
5509 AllPlayersGone = TRUE;
5515 int DigField(struct PlayerInfo *player,
5516 int x, int y, int real_dx, int real_dy, int mode)
5518 int jx = player->jx, jy = player->jy;
5519 int dx = x - jx, dy = y - jy;
5520 int move_direction = (dx == -1 ? MV_LEFT :
5521 dx == +1 ? MV_RIGHT :
5523 dy == +1 ? MV_DOWN : MV_NO_MOVING);
5526 if (!player->MovPos)
5527 player->Pushing = FALSE;
5529 if (mode == DF_NO_PUSH)
5531 player->Switching = FALSE;
5532 player->push_delay = 0;
5533 return MF_NO_ACTION;
5536 if (IS_MOVING(x, y) || IS_PLAYER(x, y))
5537 return MF_NO_ACTION;
5539 if (IS_TUBE(Feld[jx][jy]))
5542 int tube_leave_directions[][2] =
5544 { EL_TUBE_CROSS, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
5545 { EL_TUBE_VERTICAL, MV_UP | MV_DOWN },
5546 { EL_TUBE_HORIZONTAL, MV_LEFT | MV_RIGHT },
5547 { EL_TUBE_VERT_LEFT, MV_LEFT | MV_UP | MV_DOWN },
5548 { EL_TUBE_VERT_RIGHT, MV_RIGHT | MV_UP | MV_DOWN },
5549 { EL_TUBE_HORIZ_UP, MV_LEFT | MV_RIGHT | MV_UP },
5550 { EL_TUBE_HORIZ_DOWN, MV_LEFT | MV_RIGHT | MV_DOWN },
5551 { EL_TUBE_LEFT_UP, MV_LEFT | MV_UP },
5552 { EL_TUBE_LEFT_DOWN, MV_LEFT | MV_DOWN },
5553 { EL_TUBE_RIGHT_UP, MV_RIGHT | MV_UP },
5554 { EL_TUBE_RIGHT_DOWN, MV_RIGHT | MV_DOWN },
5555 { -1, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN }
5558 while (tube_leave_directions[i][0] != Feld[jx][jy])
5561 if (tube_leave_directions[i][0] == -1) /* should not happen */
5565 if (!(tube_leave_directions[i][1] & move_direction))
5566 return MF_NO_ACTION; /* tube has no opening in this direction */
5569 element = Feld[x][y];
5574 PlaySoundLevel(x, y, SND_EMPTY);
5578 case EL_SAND_INVISIBLE:
5579 case EL_TRAP_INACTIVE:
5580 Feld[x][y] = EL_LEERRAUM;
5581 PlaySoundLevel(x, y, SND_SCHLURF);
5586 Feld[x][y] = EL_LEERRAUM;
5587 PlaySoundLevel(x, y, SND_SP_BASE);
5591 case EL_EDELSTEIN_BD:
5592 case EL_EDELSTEIN_GELB:
5593 case EL_EDELSTEIN_ROT:
5594 case EL_EDELSTEIN_LILA:
5596 case EL_SP_INFOTRON:
5600 local_player->gems_still_needed -= (element == EL_DIAMANT ? 3 :
5601 element == EL_PEARL ? 5 :
5602 element == EL_CRYSTAL ? 8 : 1);
5603 if (local_player->gems_still_needed < 0)
5604 local_player->gems_still_needed = 0;
5605 RaiseScoreElement(element);
5606 DrawText(DX_EMERALDS, DY_EMERALDS,
5607 int2str(local_player->gems_still_needed, 3),
5608 FS_SMALL, FC_YELLOW);
5609 if (element == EL_SP_INFOTRON)
5610 PlaySoundLevel(x, y, SND_SP_INFOTRON);
5612 PlaySoundLevel(x, y, SND_PONG);
5617 player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
5618 PlaySoundLevel(x, y, SND_PONG);
5622 Feld[x][y] = EL_LEERRAUM;
5623 PlaySoundLevel(x, y, SND_PONG);
5631 DrawText(DX_TIME, DY_TIME, int2str(TimeLeft, 3), FS_SMALL, FC_YELLOW);
5633 PlaySoundStereo(SND_GONG, PSND_MAX_RIGHT);
5636 case EL_SHIELD_PASSIVE:
5638 player->shield_passive_time_left += 10;
5639 PlaySoundLevel(x, y, SND_PONG);
5642 case EL_SHIELD_ACTIVE:
5644 player->shield_passive_time_left += 10;
5645 player->shield_active_time_left += 10;
5646 PlaySoundLevel(x, y, SND_PONG);
5649 case EL_DYNAMITE_INACTIVE:
5650 case EL_SP_DISK_RED:
5653 RaiseScoreElement(EL_DYNAMITE_INACTIVE);
5654 DrawText(DX_DYNAMITE, DY_DYNAMITE,
5655 int2str(local_player->dynamite, 3),
5656 FS_SMALL, FC_YELLOW);
5657 if (element == EL_SP_DISK_RED)
5658 PlaySoundLevel(x, y, SND_SP_INFOTRON);
5660 PlaySoundLevel(x, y, SND_PONG);
5663 case EL_DYNABOMB_NR:
5665 player->dynabomb_count++;
5666 player->dynabombs_left++;
5667 RaiseScoreElement(EL_DYNAMITE_INACTIVE);
5668 PlaySoundLevel(x, y, SND_PONG);
5671 case EL_DYNABOMB_SZ:
5673 player->dynabomb_size++;
5674 RaiseScoreElement(EL_DYNAMITE_INACTIVE);
5675 PlaySoundLevel(x, y, SND_PONG);
5678 case EL_DYNABOMB_XL:
5680 player->dynabomb_xl = TRUE;
5681 RaiseScoreElement(EL_DYNAMITE_INACTIVE);
5682 PlaySoundLevel(x, y, SND_PONG);
5685 case EL_SCHLUESSEL1:
5686 case EL_SCHLUESSEL2:
5687 case EL_SCHLUESSEL3:
5688 case EL_SCHLUESSEL4:
5690 int key_nr = element - EL_SCHLUESSEL1;
5693 player->key[key_nr] = TRUE;
5694 RaiseScoreElement(EL_SCHLUESSEL);
5695 DrawMiniGraphicExt(drawto, DX_KEYS + key_nr * MINI_TILEX, DY_KEYS,
5696 GFX_SCHLUESSEL1 + key_nr);
5697 DrawMiniGraphicExt(window, DX_KEYS + key_nr * MINI_TILEX, DY_KEYS,
5698 GFX_SCHLUESSEL1 + key_nr);
5699 PlaySoundLevel(x, y, SND_PONG);
5708 int key_nr = element - EL_EM_KEY_1;
5711 player->key[key_nr] = TRUE;
5712 RaiseScoreElement(EL_SCHLUESSEL);
5713 DrawMiniGraphicExt(drawto, DX_KEYS + key_nr * MINI_TILEX, DY_KEYS,
5714 GFX_SCHLUESSEL1 + key_nr);
5715 DrawMiniGraphicExt(window, DX_KEYS + key_nr * MINI_TILEX, DY_KEYS,
5716 GFX_SCHLUESSEL1 + key_nr);
5717 PlaySoundLevel(x, y, SND_PONG);
5722 Feld[x][y] = EL_ABLENK_EIN;
5725 DrawLevelField(x, y);
5729 case EL_SP_TERMINAL:
5733 for (yy=0; yy<lev_fieldy; yy++)
5735 for (xx=0; xx<lev_fieldx; xx++)
5737 if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
5739 else if (Feld[xx][yy] == EL_SP_TERMINAL)
5740 Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
5748 case EL_BELT1_SWITCH_LEFT:
5749 case EL_BELT1_SWITCH_MIDDLE:
5750 case EL_BELT1_SWITCH_RIGHT:
5751 case EL_BELT2_SWITCH_LEFT:
5752 case EL_BELT2_SWITCH_MIDDLE:
5753 case EL_BELT2_SWITCH_RIGHT:
5754 case EL_BELT3_SWITCH_LEFT:
5755 case EL_BELT3_SWITCH_MIDDLE:
5756 case EL_BELT3_SWITCH_RIGHT:
5757 case EL_BELT4_SWITCH_LEFT:
5758 case EL_BELT4_SWITCH_MIDDLE:
5759 case EL_BELT4_SWITCH_RIGHT:
5760 if (!player->Switching)
5762 player->Switching = TRUE;
5763 ToggleBeltSwitch(x, y);
5768 case EL_SWITCHGATE_SWITCH_1:
5769 case EL_SWITCHGATE_SWITCH_2:
5770 if (!player->Switching)
5772 player->Switching = TRUE;
5773 ToggleSwitchgateSwitch(x, y);
5778 case EL_LIGHT_SWITCH_OFF:
5779 case EL_LIGHT_SWITCH_ON:
5780 if (!player->Switching)
5782 player->Switching = TRUE;
5783 ToggleLightSwitch(x, y);
5788 case EL_TIMEGATE_SWITCH_OFF:
5789 ActivateTimegateSwitch(x, y);
5794 case EL_BALLOON_SEND_LEFT:
5795 case EL_BALLOON_SEND_RIGHT:
5796 case EL_BALLOON_SEND_UP:
5797 case EL_BALLOON_SEND_DOWN:
5798 case EL_BALLOON_SEND_ANY:
5799 if (element == EL_BALLOON_SEND_ANY)
5800 game.balloon_dir = move_direction;
5802 game.balloon_dir = (element == EL_BALLOON_SEND_LEFT ? MV_LEFT :
5803 element == EL_BALLOON_SEND_RIGHT ? MV_RIGHT :
5804 element == EL_BALLOON_SEND_UP ? MV_UP :
5805 element == EL_BALLOON_SEND_DOWN ? MV_DOWN :
5812 if (local_player->gems_still_needed > 0)
5813 return MF_NO_ACTION;
5815 player->LevelSolved = player->GameOver = TRUE;
5816 PlaySoundStereo(SND_SP_EXIT, PSND_MAX_RIGHT);
5819 case EL_FELSBROCKEN:
5822 case EL_DX_SUPABOMB:
5826 case EL_SP_DISK_ORANGE:
5828 if (dy || mode == DF_SNAP)
5829 return MF_NO_ACTION;
5831 player->Pushing = TRUE;
5833 if (!IN_LEV_FIELD(x+dx, y+dy) || !IS_FREE(x+dx, y+dy))
5834 return MF_NO_ACTION;
5838 if (IN_LEV_FIELD(jx, jy+real_dy) && !IS_SOLID(Feld[jx][jy+real_dy]))
5839 return MF_NO_ACTION;
5842 if (player->push_delay == 0)
5843 player->push_delay = FrameCounter;
5845 if (!FrameReached(&player->push_delay, player->push_delay_value) &&
5846 !tape.playing && element != EL_SPRING)
5847 return MF_NO_ACTION;
5849 if (!FrameReached(&player->push_delay, player->push_delay_value) &&
5850 !(tape.playing && tape.game_version < GAME_VERSION_2_0) &&
5851 element != EL_SPRING)
5852 return MF_NO_ACTION;
5856 Feld[x+dx][y+dy] = element;
5858 if (element == EL_SPRING)
5860 Feld[x+dx][y+dy] = EL_SPRING_MOVING;
5861 MovDir[x+dx][y+dy] = move_direction;
5864 player->push_delay_value = (element == EL_SPRING ? 0 : 2 + RND(8));
5866 DrawLevelField(x+dx, y+dy);
5867 if (element == EL_FELSBROCKEN || element == EL_BD_ROCK)
5868 PlaySoundLevel(x+dx, y+dy, SND_PUSCH);
5869 else if (element == EL_KOKOSNUSS)
5870 PlaySoundLevel(x+dx, y+dy, SND_KNURK);
5871 else if (IS_SP_ELEMENT(element))
5872 PlaySoundLevel(x+dx, y+dy, SND_SP_ZONKPUSH);
5874 PlaySoundLevel(x+dx, y+dy, SND_PUSCH); /* better than "SND_KLOPF" */
5881 if (!player->key[element - EL_PFORTE1])
5882 return MF_NO_ACTION;
5889 if (!player->key[element - EL_PFORTE1X])
5890 return MF_NO_ACTION;
5897 if (!player->key[element - EL_EM_GATE_1])
5898 return MF_NO_ACTION;
5899 if (!IN_LEV_FIELD(x + dx, y + dy) || !IS_FREE(x + dx, y + dy))
5900 return MF_NO_ACTION;
5902 /* automatically move to the next field with double speed */
5903 player->programmed_action = move_direction;
5904 DOUBLE_PLAYER_SPEED(player);
5906 PlaySoundLevel(x, y, SND_GATE);
5914 if (!player->key[element - EL_EM_GATE_1X])
5915 return MF_NO_ACTION;
5916 if (!IN_LEV_FIELD(x + dx, y + dy) || !IS_FREE(x + dx, y + dy))
5917 return MF_NO_ACTION;
5919 /* automatically move to the next field with double speed */
5920 player->programmed_action = move_direction;
5921 DOUBLE_PLAYER_SPEED(player);
5923 PlaySoundLevel(x, y, SND_GATE);
5927 case EL_SWITCHGATE_OPEN:
5928 case EL_TIMEGATE_OPEN:
5929 if (!IN_LEV_FIELD(x + dx, y + dy) || !IS_FREE(x + dx, y + dy))
5930 return MF_NO_ACTION;
5932 /* automatically move to the next field with double speed */
5933 player->programmed_action = move_direction;
5934 DOUBLE_PLAYER_SPEED(player);
5936 PlaySoundLevel(x, y, SND_GATE);
5940 case EL_SP_PORT1_LEFT:
5941 case EL_SP_PORT2_LEFT:
5942 case EL_SP_PORT1_RIGHT:
5943 case EL_SP_PORT2_RIGHT:
5944 case EL_SP_PORT1_UP:
5945 case EL_SP_PORT2_UP:
5946 case EL_SP_PORT1_DOWN:
5947 case EL_SP_PORT2_DOWN:
5952 element != EL_SP_PORT1_LEFT &&
5953 element != EL_SP_PORT2_LEFT &&
5954 element != EL_SP_PORT_X &&
5955 element != EL_SP_PORT_XY) ||
5957 element != EL_SP_PORT1_RIGHT &&
5958 element != EL_SP_PORT2_RIGHT &&
5959 element != EL_SP_PORT_X &&
5960 element != EL_SP_PORT_XY) ||
5962 element != EL_SP_PORT1_UP &&
5963 element != EL_SP_PORT2_UP &&
5964 element != EL_SP_PORT_Y &&
5965 element != EL_SP_PORT_XY) ||
5967 element != EL_SP_PORT1_DOWN &&
5968 element != EL_SP_PORT2_DOWN &&
5969 element != EL_SP_PORT_Y &&
5970 element != EL_SP_PORT_XY) ||
5971 !IN_LEV_FIELD(x + dx, y + dy) ||
5972 !IS_FREE(x + dx, y + dy))
5973 return MF_NO_ACTION;
5975 /* automatically move to the next field with double speed */
5976 player->programmed_action = move_direction;
5977 DOUBLE_PLAYER_SPEED(player);
5979 PlaySoundLevel(x, y, SND_GATE);
5983 case EL_TUBE_VERTICAL:
5984 case EL_TUBE_HORIZONTAL:
5985 case EL_TUBE_VERT_LEFT:
5986 case EL_TUBE_VERT_RIGHT:
5987 case EL_TUBE_HORIZ_UP:
5988 case EL_TUBE_HORIZ_DOWN:
5989 case EL_TUBE_LEFT_UP:
5990 case EL_TUBE_LEFT_DOWN:
5991 case EL_TUBE_RIGHT_UP:
5992 case EL_TUBE_RIGHT_DOWN:
5995 int tube_enter_directions[][2] =
5997 { EL_TUBE_CROSS, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
5998 { EL_TUBE_VERTICAL, MV_UP | MV_DOWN },
5999 { EL_TUBE_HORIZONTAL, MV_LEFT | MV_RIGHT },
6000 { EL_TUBE_VERT_LEFT, MV_RIGHT | MV_UP | MV_DOWN },
6001 { EL_TUBE_VERT_RIGHT, MV_LEFT | MV_UP | MV_DOWN },
6002 { EL_TUBE_HORIZ_UP, MV_LEFT | MV_RIGHT | MV_DOWN },
6003 { EL_TUBE_HORIZ_DOWN, MV_LEFT | MV_RIGHT | MV_UP },
6004 { EL_TUBE_LEFT_UP, MV_RIGHT | MV_DOWN },
6005 { EL_TUBE_LEFT_DOWN, MV_RIGHT | MV_UP },
6006 { EL_TUBE_RIGHT_UP, MV_LEFT | MV_DOWN },
6007 { EL_TUBE_RIGHT_DOWN, MV_LEFT | MV_UP },
6008 { -1, MV_NO_MOVING }
6011 while (tube_enter_directions[i][0] != element)
6014 if (tube_enter_directions[i][0] == -1) /* should not happen */
6018 if (!(tube_enter_directions[i][1] & move_direction))
6019 return MF_NO_ACTION; /* tube has no opening in this direction */
6024 case EL_AUSGANG_ACT:
6025 /* door is not (yet) open */
6026 return MF_NO_ACTION;
6029 case EL_AUSGANG_AUF:
6030 if (mode == DF_SNAP)
6031 return MF_NO_ACTION;
6033 PlaySoundLevel(x, y, SND_BUING);
6038 Feld[x][y] = EL_BIRNE_EIN;
6039 local_player->lights_still_needed--;
6040 DrawLevelField(x, y);
6041 PlaySoundLevel(x, y, SND_DENG);
6046 Feld[x][y] = EL_ZEIT_LEER;
6048 DrawText(DX_TIME, DY_TIME, int2str(TimeLeft, 3), FS_SMALL, FC_YELLOW);
6049 DrawLevelField(x, y);
6050 PlaySoundStereo(SND_GONG, PSND_MAX_RIGHT);
6054 case EL_SOKOBAN_FELD_LEER:
6057 case EL_SOKOBAN_FELD_VOLL:
6058 case EL_SOKOBAN_OBJEKT:
6060 case EL_SP_DISK_YELLOW:
6062 if (mode == DF_SNAP)
6063 return MF_NO_ACTION;
6065 player->Pushing = TRUE;
6067 if (!IN_LEV_FIELD(x+dx, y+dy)
6068 || (!IS_FREE(x+dx, y+dy)
6069 && (Feld[x+dx][y+dy] != EL_SOKOBAN_FELD_LEER
6070 || !IS_SB_ELEMENT(element))))
6071 return MF_NO_ACTION;
6075 if (IN_LEV_FIELD(jx, jy+real_dy) && !IS_SOLID(Feld[jx][jy+real_dy]))
6076 return MF_NO_ACTION;
6078 else if (dy && real_dx)
6080 if (IN_LEV_FIELD(jx+real_dx, jy) && !IS_SOLID(Feld[jx+real_dx][jy]))
6081 return MF_NO_ACTION;
6084 if (player->push_delay == 0)
6085 player->push_delay = FrameCounter;
6087 if (!FrameReached(&player->push_delay, player->push_delay_value) &&
6088 !tape.playing && element != EL_BALLOON)
6089 return MF_NO_ACTION;
6091 if (!FrameReached(&player->push_delay, player->push_delay_value) &&
6092 !(tape.playing && tape.game_version < GAME_VERSION_2_0) &&
6093 element != EL_BALLOON)
6094 return MF_NO_ACTION;
6097 if (IS_SB_ELEMENT(element))
6099 if (element == EL_SOKOBAN_FELD_VOLL)
6101 Feld[x][y] = EL_SOKOBAN_FELD_LEER;
6102 local_player->sokobanfields_still_needed++;
6107 if (Feld[x+dx][y+dy] == EL_SOKOBAN_FELD_LEER)
6109 Feld[x+dx][y+dy] = EL_SOKOBAN_FELD_VOLL;
6110 local_player->sokobanfields_still_needed--;
6111 if (element == EL_SOKOBAN_OBJEKT)
6112 PlaySoundLevel(x, y, SND_DENG);
6115 Feld[x+dx][y+dy] = EL_SOKOBAN_OBJEKT;
6120 Feld[x+dx][y+dy] = element;
6123 player->push_delay_value = (element == EL_BALLOON ? 0 : 2);
6125 DrawLevelField(x, y);
6126 DrawLevelField(x+dx, y+dy);
6127 if (element == EL_BALLOON)
6128 PlaySoundLevel(x+dx, y+dy, SND_SCHLURF);
6130 PlaySoundLevel(x+dx, y+dy, SND_PUSCH);
6132 if (IS_SB_ELEMENT(element) &&
6133 local_player->sokobanfields_still_needed == 0 &&
6134 game.emulation == EMU_SOKOBAN)
6136 player->LevelSolved = player->GameOver = TRUE;
6137 PlaySoundLevel(x, y, SND_BUING);
6148 return MF_NO_ACTION;
6151 player->push_delay = 0;
6156 boolean SnapField(struct PlayerInfo *player, int dx, int dy)
6158 int jx = player->jx, jy = player->jy;
6159 int x = jx + dx, y = jy + dy;
6161 if (!player->active || !IN_LEV_FIELD(x, y))
6169 player->snapped = FALSE;
6173 if (player->snapped)
6176 player->MovDir = (dx < 0 ? MV_LEFT :
6179 dy > 0 ? MV_DOWN : MV_NO_MOVING);
6181 if (!DigField(player, x, y, 0, 0, DF_SNAP))
6184 player->snapped = TRUE;
6185 DrawLevelField(x, y);
6191 boolean PlaceBomb(struct PlayerInfo *player)
6193 int jx = player->jx, jy = player->jy;
6196 if (!player->active || player->MovPos)
6199 element = Feld[jx][jy];
6201 if ((player->dynamite == 0 && player->dynabombs_left == 0) ||
6202 IS_ACTIVE_BOMB(element) || element == EL_EXPLODING)
6205 if (element != EL_LEERRAUM)
6206 Store[jx][jy] = element;
6208 if (player->dynamite)
6210 Feld[jx][jy] = EL_DYNAMITE_ACTIVE;
6211 MovDelay[jx][jy] = 96;
6213 DrawText(DX_DYNAMITE, DY_DYNAMITE, int2str(local_player->dynamite, 3),
6214 FS_SMALL, FC_YELLOW);
6215 if (IN_SCR_FIELD(SCREENX(jx), SCREENY(jy)))
6217 if (game.emulation == EMU_SUPAPLEX)
6218 DrawGraphic(SCREENX(jx), SCREENY(jy), GFX_SP_DISK_RED);
6220 DrawGraphicThruMask(SCREENX(jx), SCREENY(jy), GFX_DYNAMIT);
6225 Feld[jx][jy] = EL_DYNABOMB_ACTIVE_1 + (player->element_nr - EL_SPIELER1);
6226 MovDelay[jx][jy] = 96;
6227 player->dynabombs_left--;
6228 if (IN_SCR_FIELD(SCREENX(jx), SCREENY(jy)))
6229 DrawGraphicThruMask(SCREENX(jx), SCREENY(jy), GFX_DYNABOMB);
6235 void PlaySoundLevel(int x, int y, int sound_nr)
6237 int sx = SCREENX(x), sy = SCREENY(y);
6239 int silence_distance = 8;
6241 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound_nr)) ||
6242 (!setup.sound_loops && IS_LOOP_SOUND(sound_nr)))
6245 if (!IN_LEV_FIELD(x, y) ||
6246 sx < -silence_distance || sx >= SCR_FIELDX+silence_distance ||
6247 sy < -silence_distance || sy >= SCR_FIELDY+silence_distance)
6250 volume = PSND_MAX_VOLUME;
6252 #if !defined(PLATFORM_MSDOS)
6253 stereo = (sx - SCR_FIELDX/2) * 12;
6255 stereo = PSND_MIDDLE + (2 * sx - (SCR_FIELDX - 1)) * 5;
6256 if (stereo > PSND_MAX_RIGHT)
6257 stereo = PSND_MAX_RIGHT;
6258 if (stereo < PSND_MAX_LEFT)
6259 stereo = PSND_MAX_LEFT;
6262 if (!IN_SCR_FIELD(sx, sy))
6264 int dx = ABS(sx - SCR_FIELDX/2) - SCR_FIELDX/2;
6265 int dy = ABS(sy - SCR_FIELDY/2) - SCR_FIELDY/2;
6267 volume -= volume * (dx > dy ? dx : dy) / silence_distance;
6270 PlaySoundExt(sound_nr, volume, stereo, PSND_NO_LOOP);
6273 void RaiseScore(int value)
6275 local_player->score += value;
6276 DrawText(DX_SCORE, DY_SCORE, int2str(local_player->score, 5),
6277 FS_SMALL, FC_YELLOW);
6280 void RaiseScoreElement(int element)
6285 case EL_EDELSTEIN_BD:
6286 case EL_EDELSTEIN_GELB:
6287 case EL_EDELSTEIN_ROT:
6288 case EL_EDELSTEIN_LILA:
6289 RaiseScore(level.score[SC_EDELSTEIN]);
6292 RaiseScore(level.score[SC_DIAMANT]);
6296 RaiseScore(level.score[SC_KAEFER]);
6300 RaiseScore(level.score[SC_FLIEGER]);
6304 RaiseScore(level.score[SC_MAMPFER]);
6307 RaiseScore(level.score[SC_ROBOT]);
6310 RaiseScore(level.score[SC_PACMAN]);
6313 RaiseScore(level.score[SC_KOKOSNUSS]);
6315 case EL_DYNAMITE_INACTIVE:
6316 RaiseScore(level.score[SC_DYNAMIT]);
6319 RaiseScore(level.score[SC_SCHLUESSEL]);
6326 void RequestQuitGame(boolean ask_if_really_quit)
6328 if (AllPlayersGone ||
6329 !ask_if_really_quit ||
6330 level_editor_test_game ||
6331 Request("Do you really want to quit the game ?",
6332 REQ_ASK | REQ_STAY_CLOSED))
6334 #if defined(PLATFORM_UNIX)
6335 if (options.network)
6336 SendToServer_StopPlaying();
6340 game_status = MAINMENU;
6346 OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
6351 /* ---------- new game button stuff ---------------------------------------- */
6353 /* graphic position values for game buttons */
6354 #define GAME_BUTTON_XSIZE 30
6355 #define GAME_BUTTON_YSIZE 30
6356 #define GAME_BUTTON_XPOS 5
6357 #define GAME_BUTTON_YPOS 215
6358 #define SOUND_BUTTON_XPOS 5
6359 #define SOUND_BUTTON_YPOS (GAME_BUTTON_YPOS + GAME_BUTTON_YSIZE)
6361 #define GAME_BUTTON_STOP_XPOS (GAME_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
6362 #define GAME_BUTTON_PAUSE_XPOS (GAME_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
6363 #define GAME_BUTTON_PLAY_XPOS (GAME_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
6364 #define SOUND_BUTTON_MUSIC_XPOS (SOUND_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
6365 #define SOUND_BUTTON_LOOPS_XPOS (SOUND_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
6366 #define SOUND_BUTTON_SIMPLE_XPOS (SOUND_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
6373 } gamebutton_info[NUM_GAME_BUTTONS] =
6376 GAME_BUTTON_STOP_XPOS, GAME_BUTTON_YPOS,
6381 GAME_BUTTON_PAUSE_XPOS, GAME_BUTTON_YPOS,
6386 GAME_BUTTON_PLAY_XPOS, GAME_BUTTON_YPOS,
6391 SOUND_BUTTON_MUSIC_XPOS, SOUND_BUTTON_YPOS,
6392 SOUND_CTRL_ID_MUSIC,
6393 "background music on/off"
6396 SOUND_BUTTON_LOOPS_XPOS, SOUND_BUTTON_YPOS,
6397 SOUND_CTRL_ID_LOOPS,
6398 "sound loops on/off"
6401 SOUND_BUTTON_SIMPLE_XPOS, SOUND_BUTTON_YPOS,
6402 SOUND_CTRL_ID_SIMPLE,
6403 "normal sounds on/off"
6407 void CreateGameButtons()
6411 for (i=0; i<NUM_GAME_BUTTONS; i++)
6413 Bitmap *gd_bitmap = pix[PIX_DOOR];
6414 struct GadgetInfo *gi;
6417 unsigned long event_mask;
6418 int gd_xoffset, gd_yoffset;
6419 int gd_x1, gd_x2, gd_y1, gd_y2;
6422 gd_xoffset = gamebutton_info[i].x;
6423 gd_yoffset = gamebutton_info[i].y;
6424 gd_x1 = DOOR_GFX_PAGEX4 + gd_xoffset;
6425 gd_x2 = DOOR_GFX_PAGEX3 + gd_xoffset;
6427 if (id == GAME_CTRL_ID_STOP ||
6428 id == GAME_CTRL_ID_PAUSE ||
6429 id == GAME_CTRL_ID_PLAY)
6431 button_type = GD_TYPE_NORMAL_BUTTON;
6433 event_mask = GD_EVENT_RELEASED;
6434 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
6435 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
6439 button_type = GD_TYPE_CHECK_BUTTON;
6441 ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
6442 (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
6443 (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
6444 event_mask = GD_EVENT_PRESSED;
6445 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset;
6446 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
6449 gi = CreateGadget(GDI_CUSTOM_ID, id,
6450 GDI_INFO_TEXT, gamebutton_info[i].infotext,
6451 GDI_X, DX + gd_xoffset,
6452 GDI_Y, DY + gd_yoffset,
6453 GDI_WIDTH, GAME_BUTTON_XSIZE,
6454 GDI_HEIGHT, GAME_BUTTON_YSIZE,
6455 GDI_TYPE, button_type,
6456 GDI_STATE, GD_BUTTON_UNPRESSED,
6457 GDI_CHECKED, checked,
6458 GDI_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y1,
6459 GDI_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y1,
6460 GDI_ALT_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y2,
6461 GDI_ALT_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y2,
6462 GDI_EVENT_MASK, event_mask,
6463 GDI_CALLBACK_ACTION, HandleGameButtons,
6467 Error(ERR_EXIT, "cannot create gadget");
6469 game_gadget[id] = gi;
6473 static void MapGameButtons()
6477 for (i=0; i<NUM_GAME_BUTTONS; i++)
6478 MapGadget(game_gadget[i]);
6481 void UnmapGameButtons()
6485 for (i=0; i<NUM_GAME_BUTTONS; i++)
6486 UnmapGadget(game_gadget[i]);
6489 static void HandleGameButtons(struct GadgetInfo *gi)
6491 int id = gi->custom_id;
6493 if (game_status != PLAYING)
6498 case GAME_CTRL_ID_STOP:
6499 RequestQuitGame(TRUE);
6502 case GAME_CTRL_ID_PAUSE:
6503 if (options.network)
6505 #if defined(PLATFORM_UNIX)
6507 SendToServer_ContinuePlaying();
6509 SendToServer_PausePlaying();
6513 TapeTogglePause(TAPE_TOGGLE_MANUAL);
6516 case GAME_CTRL_ID_PLAY:
6519 #if defined(PLATFORM_UNIX)
6520 if (options.network)
6521 SendToServer_ContinuePlaying();
6525 tape.pausing = FALSE;
6526 DrawVideoDisplay(VIDEO_STATE_PAUSE_OFF,0);
6531 case SOUND_CTRL_ID_MUSIC:
6532 if (setup.sound_music)
6534 setup.sound_music = FALSE;
6537 else if (audio.music_available)
6539 setup.sound = setup.sound_music = TRUE;
6540 PlayMusic(level_nr);
6544 case SOUND_CTRL_ID_LOOPS:
6545 if (setup.sound_loops)
6546 setup.sound_loops = FALSE;
6547 else if (audio.loops_available)
6548 setup.sound = setup.sound_loops = TRUE;
6551 case SOUND_CTRL_ID_SIMPLE:
6552 if (setup.sound_simple)
6553 setup.sound_simple = FALSE;
6554 else if (audio.sound_available)
6555 setup.sound = setup.sound_simple = TRUE;