1 /***********************************************************
2 * Rocks'n'Diamonds -- McDuffin Strikes Back! *
3 *----------------------------------------------------------*
4 * (c) 1995-98 Artsoft Entertainment *
8 * phone: ++49 +521 290471 *
9 * email: aeglos@valinor.owl.de *
10 *----------------------------------------------------------*
12 ***********************************************************/
31 /* for MoveFigure() */
32 #define MF_NO_ACTION 0
36 /* for ScrollFigure() */
38 #define SCROLL_GO_ON 1
41 #define EX_PHASE_START 0
46 /* special positions in the game control window (relative to control window) */
49 #define XX_EMERALDS 29
50 #define YY_EMERALDS 54
51 #define XX_DYNAMITE 29
52 #define YY_DYNAMITE 89
60 /* special positions in the game control window (relative to main window) */
61 #define DX_LEVEL (DX + XX_LEVEL)
62 #define DY_LEVEL (DY + YY_LEVEL)
63 #define DX_EMERALDS (DX + XX_EMERALDS)
64 #define DY_EMERALDS (DY + YY_EMERALDS)
65 #define DX_DYNAMITE (DX + XX_DYNAMITE)
66 #define DY_DYNAMITE (DY + YY_DYNAMITE)
67 #define DX_KEYS (DX + XX_KEYS)
68 #define DY_KEYS (DY + YY_KEYS)
69 #define DX_SCORE (DX + XX_SCORE)
70 #define DY_SCORE (DY + YY_SCORE)
71 #define DX_TIME (DX + XX_TIME)
72 #define DY_TIME (DY + YY_TIME)
74 #define IS_LOOP_SOUND(s) ((s)==SND_KLAPPER || (s)==SND_ROEHR || \
75 (s)==SND_NJAM || (s)==SND_MIEP)
76 #define IS_MUSIC_SOUND(s) ((s)==SND_ALCHEMY || (s)==SND_CHASE || \
77 (s)==SND_NETWORK || (s)==SND_CZARDASZ || \
78 (s)==SND_TYGER || (s)==SND_VOYAGER || \
81 /* values for player movement speed (which is in fact a delay value) */
82 #define MOVE_DELAY_NORMAL_SPEED 8
83 #define MOVE_DELAY_HIGH_SPEED 4
85 #define DOUBLE_MOVE_DELAY(x) (x = (x <= MOVE_DELAY_HIGH_SPEED ? x * 2 : x))
86 #define HALVE_MOVE_DELAY(x) (x = (x >= MOVE_DELAY_HIGH_SPEED ? x / 2 : x))
87 #define DOUBLE_PLAYER_SPEED(p) (HALVE_MOVE_DELAY((p)->move_delay_value))
88 #define HALVE_PLAYER_SPEED(p) (DOUBLE_MOVE_DELAY((p)->move_delay_value))
90 /* game button identifiers */
91 #define GAME_CTRL_ID_STOP 0
92 #define GAME_CTRL_ID_PAUSE 1
93 #define GAME_CTRL_ID_PLAY 2
94 #define SOUND_CTRL_ID_MUSIC 3
95 #define SOUND_CTRL_ID_LOOPS 4
96 #define SOUND_CTRL_ID_SIMPLE 5
98 #define NUM_GAME_BUTTONS 6
100 /* forward declaration for internal use */
101 static void CheckGravityMovement(struct PlayerInfo *);
103 static void MapGameButtons();
104 static void HandleGameButtons(struct GadgetInfo *);
106 static struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS];
112 static unsigned int getStateCheckSum(int counter)
115 unsigned int mult = 1;
116 unsigned int checksum = 0;
118 static short lastFeld[MAX_LEV_FIELDX][MAX_LEV_FIELDY];
120 static boolean first_game = TRUE;
122 for (y=0; y<lev_fieldy; y++) for(x=0; x<lev_fieldx; x++)
128 lastFeld[x][y] = Feld[x][y];
129 else if (lastFeld[x][y] != Feld[x][y])
130 printf("DIFF: [%d][%d]: lastFeld == %d != %d == Feld\n",
131 x, y, lastFeld[x][y], Feld[x][y]);
135 checksum += mult++ * Ur[x][y];
136 checksum += mult++ * Feld[x][y];
139 checksum += mult++ * MovPos[x][y];
140 checksum += mult++ * MovDir[x][y];
141 checksum += mult++ * MovDelay[x][y];
142 checksum += mult++ * Store[x][y];
143 checksum += mult++ * Store2[x][y];
144 checksum += mult++ * StorePlayer[x][y];
145 checksum += mult++ * Frame[x][y];
146 checksum += mult++ * AmoebaNr[x][y];
147 checksum += mult++ * JustHit[x][y];
148 checksum += mult++ * Stop[x][y];
152 if (counter == 3 && first_game)
163 void GetPlayerConfig()
165 if (sound_status == SOUND_OFF)
168 if (!sound_loops_allowed)
170 setup.sound_loops = FALSE;
171 setup.sound_music = FALSE;
174 setup.sound_simple = setup.sound;
179 static void InitField(int x, int y, boolean init_game)
186 Feld[x][y] = EL_SPIELER1;
194 struct PlayerInfo *player = &stored_player[Feld[x][y] - EL_SPIELER1];
195 int jx = player->jx, jy = player->jy;
197 player->present = TRUE;
199 if (!options.network || player->connected)
201 player->active = TRUE;
203 /* remove potentially duplicate players */
204 if (StorePlayer[jx][jy] == Feld[x][y])
205 StorePlayer[jx][jy] = 0;
207 StorePlayer[x][y] = Feld[x][y];
211 printf("Player %d activated.\n", player->element_nr);
212 printf("[Local player is %d and currently %s.]\n",
213 local_player->element_nr,
214 local_player->active ? "active" : "not active");
218 Feld[x][y] = EL_LEERRAUM;
219 player->jx = player->last_jx = x;
220 player->jy = player->last_jy = y;
225 if (x < lev_fieldx-1 && Feld[x+1][y] == EL_SALZSAEURE)
226 Feld[x][y] = EL_BADEWANNE1;
227 else if (x > 0 && Feld[x-1][y] == EL_SALZSAEURE)
228 Feld[x][y] = EL_BADEWANNE2;
229 else if (y > 0 && Feld[x][y-1] == EL_BADEWANNE1)
230 Feld[x][y] = EL_BADEWANNE3;
231 else if (y > 0 && Feld[x][y-1] == EL_SALZSAEURE)
232 Feld[x][y] = EL_BADEWANNE4;
233 else if (y > 0 && Feld[x][y-1] == EL_BADEWANNE2)
234 Feld[x][y] = EL_BADEWANNE5;
276 if (y == lev_fieldy - 1)
278 Feld[x][y] = EL_AMOEBING;
279 Store[x][y] = EL_AMOEBE_NASS;
288 local_player->lights_still_needed++;
291 case EL_SOKOBAN_FELD_LEER:
292 local_player->sokobanfields_still_needed++;
297 local_player->friends_still_needed++;
302 MovDir[x][y] = 1 << RND(4);
306 Feld[x][y] = EL_LEERRAUM;
309 case EL_EM_KEY_1_FILE:
310 Feld[x][y] = EL_EM_KEY_1;
312 case EL_EM_KEY_2_FILE:
313 Feld[x][y] = EL_EM_KEY_2;
315 case EL_EM_KEY_3_FILE:
316 Feld[x][y] = EL_EM_KEY_3;
318 case EL_EM_KEY_4_FILE:
319 Feld[x][y] = EL_EM_KEY_4;
330 boolean emulate_bd = TRUE; /* unless non-BOULDERDASH elements found */
331 boolean emulate_sb = TRUE; /* unless non-SOKOBAN elements found */
332 boolean emulate_sp = TRUE; /* unless non-SUPAPLEX elements found */
334 /* don't play tapes over network */
335 network_playing = (options.network && !tape.playing);
337 for (i=0; i<MAX_PLAYERS; i++)
339 struct PlayerInfo *player = &stored_player[i];
341 player->index_nr = i;
342 player->element_nr = EL_SPIELER1 + i;
344 player->present = FALSE;
345 player->active = FALSE;
348 player->effective_action = 0;
349 player->programmed_action = 0;
352 player->gems_still_needed = level.gems_needed;
353 player->sokobanfields_still_needed = 0;
354 player->lights_still_needed = 0;
355 player->friends_still_needed = 0;
358 player->key[j] = FALSE;
360 player->dynamite = 0;
361 player->dynabomb_count = 0;
362 player->dynabomb_size = 0;
363 player->dynabombs_left = 0;
364 player->dynabomb_xl = FALSE;
366 player->MovDir = MV_NO_MOVING;
368 player->Pushing = FALSE;
372 player->actual_frame_counter = 0;
374 player->frame_reset_delay = 0;
376 player->push_delay = 0;
377 player->push_delay_value = 5;
379 player->move_delay = 0;
380 player->last_move_dir = MV_NO_MOVING;
382 player->move_delay_value =
383 (level.double_speed ? MOVE_DELAY_HIGH_SPEED : MOVE_DELAY_NORMAL_SPEED);
385 player->snapped = FALSE;
387 player->last_jx = player->last_jy = 0;
388 player->jx = player->jy = 0;
390 DigField(player, 0, 0, 0, 0, DF_NO_PUSH);
391 SnapField(player, 0, 0);
393 player->LevelSolved = FALSE;
394 player->GameOver = FALSE;
397 network_player_action_received = FALSE;
400 /* initial null action */
402 SendToServer_MovePlayer(MV_NO_MOVING);
407 game.yam_content_nr = 0;
411 TimeLeft = level.time;
413 ScreenMovDir = MV_NO_MOVING;
417 ScrollStepSize = 0; /* will be correctly initialized by ScrollScreen() */
419 AllPlayersGone = FALSE;
420 game.magic_wall_active = FALSE;
421 game.magic_wall_time_left = 0;
423 for (i=0; i<MAX_NUM_AMOEBA; i++)
424 AmoebaCnt[i] = AmoebaCnt2[i] = 0;
426 for (x=0; x<lev_fieldx; x++)
428 for (y=0; y<lev_fieldy; y++)
430 Feld[x][y] = Ur[x][y];
431 MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
432 Store[x][y] = Store2[x][y] = StorePlayer[x][y] = 0;
440 for(y=0; y<lev_fieldy; y++)
442 for(x=0; x<lev_fieldx; x++)
444 if (emulate_bd && !IS_BD_ELEMENT(Feld[x][y]))
446 if (emulate_sb && !IS_SB_ELEMENT(Feld[x][y]))
448 if (emulate_sp && !IS_SP_ELEMENT(Feld[x][y]))
451 InitField(x, y, TRUE);
455 /* check if any connected player was not found in playfield */
456 for (i=0; i<MAX_PLAYERS; i++)
458 struct PlayerInfo *player = &stored_player[i];
460 if (player->connected && !player->present)
462 for (j=0; j<MAX_PLAYERS; j++)
464 struct PlayerInfo *some_player = &stored_player[j];
465 int jx = some_player->jx, jy = some_player->jy;
467 /* assign first free player found that is present in the playfield */
468 if (some_player->present && !some_player->connected)
470 player->present = TRUE;
471 player->active = TRUE;
472 some_player->present = FALSE;
474 StorePlayer[jx][jy] = player->element_nr;
475 player->jx = player->last_jx = jx;
476 player->jy = player->last_jy = jy;
486 /* when playing a tape, eliminate all players who do not participate */
488 for (i=0; i<MAX_PLAYERS; i++)
490 if (stored_player[i].active && !tape.player_participates[i])
492 struct PlayerInfo *player = &stored_player[i];
493 int jx = player->jx, jy = player->jy;
495 player->active = FALSE;
496 StorePlayer[jx][jy] = 0;
497 Feld[jx][jy] = EL_LEERRAUM;
501 else if (!options.network && !setup.team_mode) /* && !tape.playing */
503 /* when in single player mode, eliminate all but the first active player */
505 for (i=0; i<MAX_PLAYERS; i++)
507 if (stored_player[i].active)
509 for (j=i+1; j<MAX_PLAYERS; j++)
511 if (stored_player[j].active)
513 struct PlayerInfo *player = &stored_player[j];
514 int jx = player->jx, jy = player->jy;
516 player->active = FALSE;
517 StorePlayer[jx][jy] = 0;
518 Feld[jx][jy] = EL_LEERRAUM;
525 /* when recording the game, store which players take part in the game */
528 for (i=0; i<MAX_PLAYERS; i++)
529 if (stored_player[i].active)
530 tape.player_participates[i] = TRUE;
535 for (i=0; i<MAX_PLAYERS; i++)
537 struct PlayerInfo *player = &stored_player[i];
539 printf("Player %d: present == %d, connected == %d, active == %d.\n",
544 if (local_player == player)
545 printf("Player %d is local player.\n", i+1);
549 game.emulation = (emulate_bd ? EMU_BOULDERDASH :
550 emulate_sb ? EMU_SOKOBAN :
551 emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
553 /* determine border element for this level */
556 if (BorderElement == EL_LEERRAUM)
559 SBX_Right = lev_fieldx - SCR_FIELDX;
561 SBY_Lower = lev_fieldy - SCR_FIELDY;
566 SBX_Right = lev_fieldx - SCR_FIELDX + 1;
568 SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
571 if (lev_fieldx + (SBX_Left == -1 ? 2 : 0) <= SCR_FIELDX)
572 SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
574 if (lev_fieldy + (SBY_Upper == -1 ? 2 : 0) <= SCR_FIELDY)
575 SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
578 scroll_y = SBY_Upper;
579 if (local_player->jx >= SBX_Left + MIDPOSX)
580 scroll_x = (local_player->jx <= SBX_Right + MIDPOSX ?
581 local_player->jx - MIDPOSX :
583 if (local_player->jy >= SBY_Upper + MIDPOSY)
584 scroll_y = (local_player->jy <= SBY_Lower + MIDPOSY ?
585 local_player->jy - MIDPOSY :
588 CloseDoor(DOOR_CLOSE_1);
597 if (setup.soft_scrolling)
598 XCopyArea(display, fieldbuffer, backbuffer, gc,
599 FX, FY, SXSIZE, SYSIZE, SX, SY);
601 redraw_mask |= REDRAW_FROM_BACKBUFFER;
606 /* copy default game door content to main double buffer */
607 XCopyArea(display, pix[PIX_DOOR], drawto, gc,
608 DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
610 DrawText(DX + XX_LEVEL, DY + YY_LEVEL,
611 int2str(level_nr, 2), FS_SMALL, FC_YELLOW);
612 DrawText(DX + XX_EMERALDS, DY + YY_EMERALDS,
613 int2str(local_player->gems_still_needed,3), FS_SMALL, FC_YELLOW);
614 DrawText(DX + XX_DYNAMITE, DY + YY_DYNAMITE,
615 int2str(local_player->dynamite, 3), FS_SMALL, FC_YELLOW);
616 DrawText(DX + XX_SCORE, DY + YY_SCORE,
617 int2str(local_player->score, 5), FS_SMALL, FC_YELLOW);
618 DrawText(DX + XX_TIME, DY + YY_TIME,
619 int2str(TimeLeft, 3), FS_SMALL, FC_YELLOW);
622 game_gadget[SOUND_CTRL_ID_MUSIC]->checked = setup.sound_music;
623 game_gadget[SOUND_CTRL_ID_LOOPS]->checked = setup.sound_loops;
624 game_gadget[SOUND_CTRL_ID_SIMPLE]->checked = setup.sound_simple;
628 /* copy actual game door content to door double buffer for OpenDoor() */
629 XCopyArea(display, drawto, pix[PIX_DB_DOOR], gc,
630 DX, DY, DXSIZE, DYSIZE, DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1);
632 OpenDoor(DOOR_OPEN_ALL);
634 if (setup.sound_music)
635 PlaySoundLoop(background_loop[level_nr % num_bg_loops]);
637 XAutoRepeatOff(display);
642 printf("Player %d %sactive.\n",
643 i + 1, (stored_player[i].active ? "" : "not "));
647 void InitMovDir(int x, int y)
649 int i, element = Feld[x][y];
650 static int xy[4][2] =
657 static int direction[2][4] =
659 { MV_RIGHT, MV_UP, MV_LEFT, MV_DOWN },
660 { MV_LEFT, MV_DOWN, MV_RIGHT, MV_UP }
669 Feld[x][y] = EL_KAEFER;
670 MovDir[x][y] = direction[0][element - EL_KAEFER_R];
677 Feld[x][y] = EL_FLIEGER;
678 MovDir[x][y] = direction[0][element - EL_FLIEGER_R];
685 Feld[x][y] = EL_BUTTERFLY;
686 MovDir[x][y] = direction[0][element - EL_BUTTERFLY_R];
693 Feld[x][y] = EL_FIREFLY;
694 MovDir[x][y] = direction[0][element - EL_FIREFLY_R];
701 Feld[x][y] = EL_PACMAN;
702 MovDir[x][y] = direction[0][element - EL_PACMAN_R];
706 MovDir[x][y] = MV_UP;
710 MovDir[x][y] = MV_LEFT;
714 MovDir[x][y] = 1 << RND(4);
715 if (element != EL_KAEFER &&
716 element != EL_FLIEGER &&
717 element != EL_BUTTERFLY &&
718 element != EL_FIREFLY)
723 int x1 = x + xy[i][0];
724 int y1 = y + xy[i][1];
726 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
728 if (element == EL_KAEFER || element == EL_BUTTERFLY)
730 MovDir[x][y] = direction[0][i];
733 else if (element == EL_FLIEGER || element == EL_FIREFLY ||
734 element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
736 MovDir[x][y] = direction[1][i];
745 void InitAmoebaNr(int x, int y)
748 int group_nr = AmoebeNachbarNr(x, y);
752 for (i=1; i<MAX_NUM_AMOEBA; i++)
754 if (AmoebaCnt[i] == 0)
762 AmoebaNr[x][y] = group_nr;
763 AmoebaCnt[group_nr]++;
764 AmoebaCnt2[group_nr]++;
770 int bumplevel = FALSE;
772 if (local_player->MovPos)
775 local_player->LevelSolved = FALSE;
779 if (setup.sound_loops)
780 PlaySoundExt(SND_SIRR, PSND_MAX_VOLUME, PSND_MAX_RIGHT, PSND_LOOP);
784 if (!setup.sound_loops)
785 PlaySoundStereo(SND_SIRR, PSND_MAX_RIGHT);
786 if (TimeLeft > 0 && !(TimeLeft % 10))
787 RaiseScore(level.score[SC_ZEITBONUS]);
788 if (TimeLeft > 100 && !(TimeLeft % 10))
792 DrawText(DX_TIME, DY_TIME, int2str(TimeLeft, 3), FS_SMALL, FC_YELLOW);
797 if (setup.sound_loops)
800 else if (level.time == 0) /* level without time limit */
802 if (setup.sound_loops)
803 PlaySoundExt(SND_SIRR, PSND_MAX_VOLUME, PSND_MAX_RIGHT, PSND_LOOP);
805 while(TimePlayed < 999)
807 if (!setup.sound_loops)
808 PlaySoundStereo(SND_SIRR, PSND_MAX_RIGHT);
809 if (TimePlayed < 999 && !(TimePlayed % 10))
810 RaiseScore(level.score[SC_ZEITBONUS]);
811 if (TimePlayed < 900 && !(TimePlayed % 10))
815 DrawText(DX_TIME, DY_TIME, int2str(TimePlayed, 3), FS_SMALL, FC_YELLOW);
820 if (setup.sound_loops)
826 /* Hero disappears */
827 DrawLevelField(ExitX, ExitY);
833 CloseDoor(DOOR_CLOSE_1);
838 SaveTape(tape.level_nr); /* Ask to save tape */
841 if ((hi_pos = NewHiScore()) >= 0)
843 game_status = HALLOFFAME;
844 DrawHallOfFame(hi_pos);
845 if (bumplevel && TAPE_IS_EMPTY(tape))
850 game_status = MAINMENU;
851 if (bumplevel && TAPE_IS_EMPTY(tape))
866 if (strcmp(setup.player_name, EMPTY_PLAYER_NAME) == 0 ||
867 local_player->score < highscore[MAX_SCORE_ENTRIES - 1].Score)
870 for (k=0; k<MAX_SCORE_ENTRIES; k++)
872 if (local_player->score > highscore[k].Score)
874 /* player has made it to the hall of fame */
876 if (k < MAX_SCORE_ENTRIES - 1)
878 int m = MAX_SCORE_ENTRIES - 1;
881 for (l=k; l<MAX_SCORE_ENTRIES; l++)
882 if (!strcmp(setup.player_name, highscore[l].Name))
884 if (m == k) /* player's new highscore overwrites his old one */
890 strcpy(highscore[l].Name, highscore[l - 1].Name);
891 highscore[l].Score = highscore[l - 1].Score;
898 strncpy(highscore[k].Name, setup.player_name, MAX_NAMELEN - 1);
899 highscore[k].Name[MAX_NAMELEN - 1] = '\0';
900 highscore[k].Score = local_player->score;
906 else if (!strncmp(setup.player_name, highscore[k].Name, MAX_NAMELEN - 1))
907 break; /* player already there with a higher score */
918 void InitMovingField(int x, int y, int direction)
920 int newx = x + (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
921 int newy = y + (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
923 MovDir[x][y] = direction;
924 MovDir[newx][newy] = direction;
925 if (Feld[newx][newy] == EL_LEERRAUM)
926 Feld[newx][newy] = EL_BLOCKED;
929 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
931 int direction = MovDir[x][y];
932 int newx = x + (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
933 int newy = y + (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
939 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
941 int oldx = x, oldy = y;
942 int direction = MovDir[x][y];
944 if (direction == MV_LEFT)
946 else if (direction == MV_RIGHT)
948 else if (direction == MV_UP)
950 else if (direction == MV_DOWN)
953 *comes_from_x = oldx;
954 *comes_from_y = oldy;
957 int MovingOrBlocked2Element(int x, int y)
959 int element = Feld[x][y];
961 if (element == EL_BLOCKED)
965 Blocked2Moving(x, y, &oldx, &oldy);
966 return Feld[oldx][oldy];
972 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
974 /* like MovingOrBlocked2Element(), but if element is moving
975 and (x,y) is the field the moving element is just leaving,
976 return EL_BLOCKED instead of the element value */
977 int element = Feld[x][y];
981 if (element == EL_BLOCKED)
985 Blocked2Moving(x, y, &oldx, &oldy);
986 return Feld[oldx][oldy];
995 static void RemoveField(int x, int y)
997 Feld[x][y] = EL_LEERRAUM;
1003 void RemoveMovingField(int x, int y)
1005 int oldx = x, oldy = y, newx = x, newy = y;
1007 if (Feld[x][y] != EL_BLOCKED && !IS_MOVING(x, y))
1010 if (IS_MOVING(x, y))
1012 Moving2Blocked(x, y, &newx, &newy);
1013 if (Feld[newx][newy] != EL_BLOCKED)
1016 else if (Feld[x][y] == EL_BLOCKED)
1018 Blocked2Moving(x, y, &oldx, &oldy);
1019 if (!IS_MOVING(oldx, oldy))
1023 if (Feld[x][y] == EL_BLOCKED &&
1024 (Store[oldx][oldy] == EL_MORAST_LEER ||
1025 Store[oldx][oldy] == EL_SIEB_LEER ||
1026 Store[oldx][oldy] == EL_SIEB2_LEER ||
1027 Store[oldx][oldy] == EL_AMOEBE_NASS))
1029 Feld[oldx][oldy] = Store[oldx][oldy];
1030 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
1033 Feld[oldx][oldy] = EL_LEERRAUM;
1035 Feld[newx][newy] = EL_LEERRAUM;
1036 MovPos[oldx][oldy] = MovDir[oldx][oldy] = MovDelay[oldx][oldy] = 0;
1037 MovPos[newx][newy] = MovDir[newx][newy] = MovDelay[newx][newy] = 0;
1039 DrawLevelField(oldx, oldy);
1040 DrawLevelField(newx, newy);
1043 void DrawDynamite(int x, int y)
1045 int sx = SCREENX(x), sy = SCREENY(y);
1046 int graphic = el2gfx(Feld[x][y]);
1049 if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
1053 DrawGraphic(sx, sy, el2gfx(Store[x][y]));
1055 if (Feld[x][y] == EL_DYNAMIT)
1057 if ((phase = (96 - MovDelay[x][y]) / 12) > 6)
1062 if ((phase = ((96 - MovDelay[x][y]) / 6) % 8) > 3)
1066 if (game.emulation == EMU_SUPAPLEX)
1067 DrawGraphic(sx, sy, GFX_SP_DISK_RED);
1068 else if (Store[x][y])
1069 DrawGraphicThruMask(sx, sy, graphic + phase);
1071 DrawGraphic(sx, sy, graphic + phase);
1074 void CheckDynamite(int x, int y)
1076 if (MovDelay[x][y]) /* dynamite is still waiting to explode */
1081 if (!(MovDelay[x][y] % 12))
1082 PlaySoundLevel(x, y, SND_ZISCH);
1084 if (Feld[x][y] == EL_DYNAMIT && !(MovDelay[x][y] % 12))
1086 else if (Feld[x][y] == EL_DYNABOMB && !(MovDelay[x][y] % 6))
1093 StopSound(SND_ZISCH);
1097 void Explode(int ex, int ey, int phase, int mode)
1100 int num_phase = 9, delay = 2;
1101 int last_phase = num_phase * delay;
1102 int half_phase = (num_phase / 2) * delay;
1103 int first_phase_after_start = EX_PHASE_START + 1;
1105 if (phase == EX_PHASE_START) /* initialize 'Store[][]' field */
1107 int center_element = Feld[ex][ey];
1109 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
1111 center_element = MovingOrBlocked2Element(ex, ey);
1112 RemoveMovingField(ex, ey);
1115 for (y=ey-1; y<ey+2; y++) for(x=ex-1; x<ex+2; x++)
1117 int element = Feld[x][y];
1119 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
1121 element = MovingOrBlocked2Element(x, y);
1122 RemoveMovingField(x, y);
1125 if (!IN_LEV_FIELD(x, y) || IS_MASSIVE(element) || element == EL_BURNING)
1128 if ((mode!=EX_NORMAL || center_element == EL_AMOEBA2DIAM) &&
1132 if (element == EL_EXPLODING)
1133 element = Store2[x][y];
1135 if (IS_PLAYER(ex, ey))
1137 switch(StorePlayer[ex][ey])
1140 Store[x][y] = EL_EDELSTEIN_ROT;
1143 Store[x][y] = EL_EDELSTEIN;
1146 Store[x][y] = EL_EDELSTEIN_LILA;
1150 Store[x][y] = EL_EDELSTEIN_GELB;
1154 if (game.emulation == EMU_SUPAPLEX)
1155 Store[x][y] = EL_LEERRAUM;
1157 else if (center_element == EL_MAULWURF)
1158 Store[x][y] = EL_EDELSTEIN_ROT;
1159 else if (center_element == EL_PINGUIN)
1160 Store[x][y] = EL_EDELSTEIN_LILA;
1161 else if (center_element == EL_KAEFER)
1162 Store[x][y] = ((x == ex && y == ey) ? EL_DIAMANT : EL_EDELSTEIN);
1163 else if (center_element == EL_BUTTERFLY)
1164 Store[x][y] = EL_EDELSTEIN_BD;
1165 else if (center_element == EL_SP_ELECTRON)
1166 Store[x][y] = EL_SP_INFOTRON;
1167 else if (center_element == EL_MAMPFER)
1168 Store[x][y] = level.yam_content[game.yam_content_nr][x-ex+1][y-ey+1];
1169 else if (center_element == EL_AMOEBA2DIAM)
1170 Store[x][y] = level.amoeba_content;
1171 else if (element == EL_ERZ_EDEL)
1172 Store[x][y] = EL_EDELSTEIN;
1173 else if (element == EL_ERZ_DIAM)
1174 Store[x][y] = EL_DIAMANT;
1175 else if (element == EL_ERZ_EDEL_BD)
1176 Store[x][y] = EL_EDELSTEIN_BD;
1177 else if (element == EL_ERZ_EDEL_GELB)
1178 Store[x][y] = EL_EDELSTEIN_GELB;
1179 else if (element == EL_ERZ_EDEL_ROT)
1180 Store[x][y] = EL_EDELSTEIN_ROT;
1181 else if (element == EL_ERZ_EDEL_LILA)
1182 Store[x][y] = EL_EDELSTEIN_LILA;
1183 else if (!IS_PFORTE(Store[x][y]))
1184 Store[x][y] = EL_LEERRAUM;
1186 if (x != ex || y != ey ||
1187 center_element == EL_AMOEBA2DIAM || mode == EX_BORDER)
1188 Store2[x][y] = element;
1190 if (AmoebaNr[x][y] &&
1191 (element == EL_AMOEBE_VOLL ||
1192 element == EL_AMOEBE_BD ||
1193 element == EL_AMOEBING))
1195 AmoebaCnt[AmoebaNr[x][y]]--;
1196 AmoebaCnt2[AmoebaNr[x][y]]--;
1199 Feld[x][y] = EL_EXPLODING;
1200 MovDir[x][y] = MovPos[x][y] = 0;
1206 if (center_element == EL_MAMPFER)
1207 game.yam_content_nr = (game.yam_content_nr + 1) % level.num_yam_contents;
1218 Frame[x][y] = (phase < last_phase ? phase + 1 : 0);
1220 if (phase == first_phase_after_start)
1222 int element = Store2[x][y];
1224 if (element == EL_BLACK_ORB)
1226 Feld[x][y] = Store2[x][y];
1231 else if (phase == half_phase)
1233 int element = Store2[x][y];
1235 if (IS_PLAYER(x, y))
1236 KillHero(PLAYERINFO(x, y));
1237 else if (IS_EXPLOSIVE(element))
1239 Feld[x][y] = Store2[x][y];
1243 else if (element == EL_AMOEBA2DIAM)
1244 AmoebeUmwandeln(x, y);
1247 if (phase == last_phase)
1251 element = Feld[x][y] = Store[x][y];
1252 Store[x][y] = Store2[x][y] = 0;
1253 MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
1254 InitField(x, y, FALSE);
1255 if (CAN_MOVE(element) || COULD_MOVE(element))
1257 DrawLevelField(x, y);
1259 else if (!(phase % delay) && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
1261 int graphic = GFX_EXPLOSION;
1263 if (game.emulation == EMU_SUPAPLEX)
1264 graphic = (Store[x][y] == EL_SP_INFOTRON ?
1265 GFX_SP_EXPLODE_INFOTRON :
1266 GFX_SP_EXPLODE_EMPTY);
1269 ErdreichAnbroeckeln(SCREENX(x), SCREENY(y));
1271 DrawGraphic(SCREENX(x), SCREENY(y), graphic + (phase / delay - 1));
1275 void DynaExplode(int ex, int ey)
1278 struct PlayerInfo *player = &stored_player[Store2[ex][ey] - EL_SPIELER1];
1279 static int xy[4][2] =
1287 Store2[ex][ey] = 0; /* delete player information */
1289 Explode(ex, ey, EX_PHASE_START, EX_CENTER);
1293 for (j=1; j<=player->dynabomb_size; j++)
1295 int x = ex+j*xy[i%4][0];
1296 int y = ey+j*xy[i%4][1];
1299 if (!IN_LEV_FIELD(x, y) || IS_MASSIVE(Feld[x][y]))
1302 element = Feld[x][y];
1303 Explode(x, y, EX_PHASE_START, EX_BORDER);
1305 if (element != EL_LEERRAUM &&
1306 element != EL_ERDREICH &&
1307 element != EL_EXPLODING &&
1308 !player->dynabomb_xl)
1313 player->dynabombs_left++;
1316 void Bang(int x, int y)
1318 int element = Feld[x][y];
1320 if (game.emulation == EMU_SUPAPLEX)
1321 PlaySoundLevel(x, y, SND_SP_BOOOM);
1323 PlaySoundLevel(x, y, SND_ROAAAR);
1325 if (IS_PLAYER(x, y)) /* remove objects that might cause smaller explosion */
1326 element = EL_LEERRAUM;
1338 RaiseScoreElement(element);
1339 Explode(x, y, EX_PHASE_START, EX_NORMAL);
1342 case EL_DYNABOMB_NR:
1343 case EL_DYNABOMB_SZ:
1344 case EL_DYNABOMB_XL:
1351 Explode(x, y, EX_PHASE_START, EX_CENTER);
1354 Explode(x, y, EX_PHASE_START, EX_NORMAL);
1359 void Blurb(int x, int y)
1361 int element = Feld[x][y];
1363 if (element != EL_BLURB_LEFT && element != EL_BLURB_RIGHT) /* start */
1365 PlaySoundLevel(x, y, SND_BLURB);
1366 if (IN_LEV_FIELD(x-1, y) && IS_FREE(x-1, y) &&
1367 (!IN_LEV_FIELD(x-1, y-1) ||
1368 !CAN_FALL(MovingOrBlocked2Element(x-1, y-1))))
1370 Feld[x-1][y] = EL_BLURB_LEFT;
1372 if (IN_LEV_FIELD(x+1, y) && IS_FREE(x+1, y) &&
1373 (!IN_LEV_FIELD(x+1, y-1) ||
1374 !CAN_FALL(MovingOrBlocked2Element(x+1, y-1))))
1376 Feld[x+1][y] = EL_BLURB_RIGHT;
1381 int graphic = (element==EL_BLURB_LEFT ? GFX_BLURB_LEFT : GFX_BLURB_RIGHT);
1383 if (!MovDelay[x][y]) /* initialize animation counter */
1386 if (MovDelay[x][y]) /* continue animation */
1389 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
1390 DrawGraphic(SCREENX(x), SCREENY(y), graphic+4-MovDelay[x][y]/2);
1392 if (!MovDelay[x][y])
1394 Feld[x][y] = EL_LEERRAUM;
1395 DrawLevelField(x, y);
1401 void Impact(int x, int y)
1403 boolean lastline = (y == lev_fieldy-1);
1404 boolean object_hit = FALSE;
1405 int element = Feld[x][y];
1408 if (!lastline) /* check if element below was hit */
1410 if (Feld[x][y+1] == EL_PLAYER_IS_LEAVING)
1413 object_hit = (!IS_FREE(x, y+1) && (!IS_MOVING(x, y+1) ||
1414 MovDir[x][y+1]!=MV_DOWN ||
1415 MovPos[x][y+1]<=TILEY/2));
1417 smashed = MovingOrBlocked2Element(x, y+1);
1420 if (!lastline && smashed == EL_SALZSAEURE) /* element falls into acid */
1426 if ((element == EL_BOMBE || element == EL_SP_DISK_ORANGE) &&
1427 (lastline || object_hit)) /* element is bomb */
1433 if (element == EL_TROPFEN && (lastline || object_hit)) /* acid drop */
1435 if (object_hit && IS_PLAYER(x, y+1))
1436 KillHero(PLAYERINFO(x, y+1));
1437 else if (object_hit && (smashed == EL_MAULWURF || smashed == EL_PINGUIN))
1441 Feld[x][y] = EL_AMOEBING;
1442 Store[x][y] = EL_AMOEBE_NASS;
1447 if (!lastline && object_hit) /* check which object was hit */
1449 if (CAN_CHANGE(element) &&
1450 (smashed == EL_SIEB_INAKTIV || smashed == EL_SIEB2_INAKTIV))
1453 int activated_magic_wall =
1454 (smashed == EL_SIEB_INAKTIV ? EL_SIEB_LEER : EL_SIEB2_LEER);
1456 /* activate magic wall / mill */
1458 for (y=0; y<lev_fieldy; y++)
1459 for (x=0; x<lev_fieldx; x++)
1460 if (Feld[x][y] == smashed)
1461 Feld[x][y] = activated_magic_wall;
1463 game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
1464 game.magic_wall_active = TRUE;
1467 if (IS_PLAYER(x, y+1))
1469 KillHero(PLAYERINFO(x, y+1));
1472 else if (smashed == EL_MAULWURF || smashed == EL_PINGUIN)
1477 else if (element == EL_EDELSTEIN_BD)
1479 if (IS_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
1485 else if (element == EL_FELSBROCKEN || element == EL_SP_ZONK)
1487 if (IS_ENEMY(smashed) ||
1488 smashed == EL_BOMBE || smashed == EL_SP_DISK_ORANGE ||
1489 smashed == EL_SONDE || smashed == EL_SCHWEIN || smashed == EL_DRACHE)
1494 else if (!IS_MOVING(x, y+1))
1496 if (smashed == EL_BIRNE_AUS || smashed == EL_BIRNE_EIN)
1501 else if (smashed == EL_KOKOSNUSS)
1503 Feld[x][y+1] = EL_CRACKINGNUT;
1504 PlaySoundLevel(x, y, SND_KNACK);
1505 RaiseScoreElement(EL_KOKOSNUSS);
1508 else if (smashed == EL_DIAMANT)
1510 Feld[x][y+1] = EL_LEERRAUM;
1511 PlaySoundLevel(x, y, SND_QUIRK);
1518 /* play sound of magic wall / mill */
1520 (Feld[x][y+1] == EL_SIEB_LEER || Feld[x][y+1] == EL_SIEB2_LEER))
1522 PlaySoundLevel(x, y, SND_QUIRK);
1526 /* play sound of object that hits the ground */
1527 if (lastline || object_hit)
1534 case EL_EDELSTEIN_BD:
1535 case EL_EDELSTEIN_GELB:
1536 case EL_EDELSTEIN_ROT:
1537 case EL_EDELSTEIN_LILA:
1539 case EL_SP_INFOTRON:
1545 case EL_FELSBROCKEN:
1549 sound = SND_SP_ZONKDOWN;
1552 case EL_SCHLUESSEL1:
1553 case EL_SCHLUESSEL2:
1554 case EL_SCHLUESSEL3:
1555 case EL_SCHLUESSEL4:
1572 PlaySoundLevel(x, y, sound);
1576 void TurnRound(int x, int y)
1588 { 0, 0 }, { 0, 0 }, { 0, 0 },
1593 int left, right, back;
1597 { MV_DOWN, MV_UP, MV_RIGHT },
1598 { MV_UP, MV_DOWN, MV_LEFT },
1600 { MV_LEFT, MV_RIGHT, MV_DOWN },
1601 { 0,0,0 }, { 0,0,0 }, { 0,0,0 },
1602 { MV_RIGHT, MV_LEFT, MV_UP }
1605 int element = Feld[x][y];
1606 int old_move_dir = MovDir[x][y];
1607 int left_dir = turn[old_move_dir].left;
1608 int right_dir = turn[old_move_dir].right;
1609 int back_dir = turn[old_move_dir].back;
1611 int left_dx = move_xy[left_dir].x, left_dy = move_xy[left_dir].y;
1612 int right_dx = move_xy[right_dir].x, right_dy = move_xy[right_dir].y;
1613 int move_dx = move_xy[old_move_dir].x, move_dy = move_xy[old_move_dir].y;
1614 int back_dx = move_xy[back_dir].x, back_dy = move_xy[back_dir].y;
1616 int left_x = x+left_dx, left_y = y+left_dy;
1617 int right_x = x+right_dx, right_y = y+right_dy;
1618 int move_x = x+move_dx, move_y = y+move_dy;
1620 if (element == EL_KAEFER || element == EL_BUTTERFLY)
1622 TestIfBadThingHitsOtherBadThing(x, y);
1624 if (IN_LEV_FIELD(right_x, right_y) &&
1625 IS_FREE_OR_PLAYER(right_x, right_y))
1626 MovDir[x][y] = right_dir;
1627 else if (!IN_LEV_FIELD(move_x, move_y) ||
1628 !IS_FREE_OR_PLAYER(move_x, move_y))
1629 MovDir[x][y] = left_dir;
1631 if (element == EL_KAEFER && MovDir[x][y] != old_move_dir)
1633 else if (element == EL_BUTTERFLY) /* && MovDir[x][y] == left_dir) */
1636 else if (element == EL_FLIEGER || element == EL_FIREFLY ||
1637 element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
1639 TestIfBadThingHitsOtherBadThing(x, y);
1641 if (IN_LEV_FIELD(left_x, left_y) &&
1642 IS_FREE_OR_PLAYER(left_x, left_y))
1643 MovDir[x][y] = left_dir;
1644 else if (!IN_LEV_FIELD(move_x, move_y) ||
1645 !IS_FREE_OR_PLAYER(move_x, move_y))
1646 MovDir[x][y] = right_dir;
1648 if ((element == EL_FLIEGER ||
1649 element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
1650 && MovDir[x][y] != old_move_dir)
1652 else if (element == EL_FIREFLY) /* && MovDir[x][y] == right_dir) */
1655 else if (element == EL_MAMPFER)
1657 boolean can_turn_left = FALSE, can_turn_right = FALSE;
1659 if (IN_LEV_FIELD(left_x, left_y) &&
1660 (IS_FREE_OR_PLAYER(left_x, left_y) ||
1661 Feld[left_x][left_y] == EL_DIAMANT))
1662 can_turn_left = TRUE;
1663 if (IN_LEV_FIELD(right_x, right_y) &&
1664 (IS_FREE_OR_PLAYER(right_x, right_y) ||
1665 Feld[right_x][right_y] == EL_DIAMANT))
1666 can_turn_right = TRUE;
1668 if (can_turn_left && can_turn_right)
1669 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
1670 else if (can_turn_left)
1671 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
1672 else if (can_turn_right)
1673 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
1675 MovDir[x][y] = back_dir;
1677 MovDelay[x][y] = 16+16*RND(3);
1679 else if (element == EL_MAMPFER2)
1681 boolean can_turn_left = FALSE, can_turn_right = FALSE;
1683 if (IN_LEV_FIELD(left_x, left_y) &&
1684 (IS_FREE_OR_PLAYER(left_x, left_y) ||
1685 IS_MAMPF2(Feld[left_x][left_y])))
1686 can_turn_left = TRUE;
1687 if (IN_LEV_FIELD(right_x, right_y) &&
1688 (IS_FREE_OR_PLAYER(right_x, right_y) ||
1689 IS_MAMPF2(Feld[right_x][right_y])))
1690 can_turn_right = TRUE;
1692 if (can_turn_left && can_turn_right)
1693 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
1694 else if (can_turn_left)
1695 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
1696 else if (can_turn_right)
1697 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
1699 MovDir[x][y] = back_dir;
1701 MovDelay[x][y] = 16+16*RND(3);
1703 else if (element == EL_PACMAN)
1705 boolean can_turn_left = FALSE, can_turn_right = FALSE;
1707 if (IN_LEV_FIELD(left_x, left_y) &&
1708 (IS_FREE_OR_PLAYER(left_x, left_y) ||
1709 IS_AMOEBOID(Feld[left_x][left_y])))
1710 can_turn_left = TRUE;
1711 if (IN_LEV_FIELD(right_x, right_y) &&
1712 (IS_FREE_OR_PLAYER(right_x, right_y) ||
1713 IS_AMOEBOID(Feld[right_x][right_y])))
1714 can_turn_right = TRUE;
1716 if (can_turn_left && can_turn_right)
1717 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
1718 else if (can_turn_left)
1719 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
1720 else if (can_turn_right)
1721 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
1723 MovDir[x][y] = back_dir;
1725 MovDelay[x][y] = 6+RND(40);
1727 else if (element == EL_SCHWEIN)
1729 boolean can_turn_left = FALSE, can_turn_right = FALSE, can_move_on = FALSE;
1730 boolean should_turn_left = FALSE, should_turn_right = FALSE;
1731 boolean should_move_on = FALSE;
1733 int rnd = RND(rnd_value);
1735 if (IN_LEV_FIELD(left_x, left_y) &&
1736 (IS_FREE(left_x, left_y) || IS_GEM(Feld[left_x][left_y])))
1737 can_turn_left = TRUE;
1738 if (IN_LEV_FIELD(right_x, right_y) &&
1739 (IS_FREE(right_x, right_y) || IS_GEM(Feld[right_x][right_y])))
1740 can_turn_right = TRUE;
1741 if (IN_LEV_FIELD(move_x, move_y) &&
1742 (IS_FREE(move_x, move_y) || IS_GEM(Feld[move_x][move_y])))
1745 if (can_turn_left &&
1747 (IN_LEV_FIELD(x+back_dx+left_dx, y+back_dy+left_dy) &&
1748 !IS_FREE(x+back_dx+left_dx, y+back_dy+left_dy))))
1749 should_turn_left = TRUE;
1750 if (can_turn_right &&
1752 (IN_LEV_FIELD(x+back_dx+right_dx, y+back_dy+right_dy) &&
1753 !IS_FREE(x+back_dx+right_dx, y+back_dy+right_dy))))
1754 should_turn_right = TRUE;
1756 (!can_turn_left || !can_turn_right ||
1757 (IN_LEV_FIELD(x+move_dx+left_dx, y+move_dy+left_dy) &&
1758 !IS_FREE(x+move_dx+left_dx, y+move_dy+left_dy)) ||
1759 (IN_LEV_FIELD(x+move_dx+right_dx, y+move_dy+right_dy) &&
1760 !IS_FREE(x+move_dx+right_dx, y+move_dy+right_dy))))
1761 should_move_on = TRUE;
1763 if (should_turn_left || should_turn_right || should_move_on)
1765 if (should_turn_left && should_turn_right && should_move_on)
1766 MovDir[x][y] = (rnd < rnd_value/3 ? left_dir :
1767 rnd < 2*rnd_value/3 ? right_dir :
1769 else if (should_turn_left && should_turn_right)
1770 MovDir[x][y] = (rnd < rnd_value/2 ? left_dir : right_dir);
1771 else if (should_turn_left && should_move_on)
1772 MovDir[x][y] = (rnd < rnd_value/2 ? left_dir : old_move_dir);
1773 else if (should_turn_right && should_move_on)
1774 MovDir[x][y] = (rnd < rnd_value/2 ? right_dir : old_move_dir);
1775 else if (should_turn_left)
1776 MovDir[x][y] = left_dir;
1777 else if (should_turn_right)
1778 MovDir[x][y] = right_dir;
1779 else if (should_move_on)
1780 MovDir[x][y] = old_move_dir;
1782 else if (can_move_on && rnd > rnd_value/8)
1783 MovDir[x][y] = old_move_dir;
1784 else if (can_turn_left && can_turn_right)
1785 MovDir[x][y] = (rnd < rnd_value/2 ? left_dir : right_dir);
1786 else if (can_turn_left && rnd > rnd_value/8)
1787 MovDir[x][y] = left_dir;
1788 else if (can_turn_right && rnd > rnd_value/8)
1789 MovDir[x][y] = right_dir;
1791 MovDir[x][y] = back_dir;
1793 if (!IS_FREE(x+move_xy[MovDir[x][y]].x, y+move_xy[MovDir[x][y]].y) &&
1794 !IS_GEM(Feld[x+move_xy[MovDir[x][y]].x][y+move_xy[MovDir[x][y]].y]))
1795 MovDir[x][y] = old_move_dir;
1799 else if (element == EL_DRACHE)
1801 boolean can_turn_left = FALSE, can_turn_right = FALSE, can_move_on = FALSE;
1803 int rnd = RND(rnd_value);
1805 if (IN_LEV_FIELD(left_x, left_y) && IS_FREE(left_x, left_y))
1806 can_turn_left = TRUE;
1807 if (IN_LEV_FIELD(right_x, right_y) && IS_FREE(right_x, right_y))
1808 can_turn_right = TRUE;
1809 if (IN_LEV_FIELD(move_x, move_y) && IS_FREE(move_x, move_y))
1812 if (can_move_on && rnd > rnd_value/8)
1813 MovDir[x][y] = old_move_dir;
1814 else if (can_turn_left && can_turn_right)
1815 MovDir[x][y] = (rnd < rnd_value/2 ? left_dir : right_dir);
1816 else if (can_turn_left && rnd > rnd_value/8)
1817 MovDir[x][y] = left_dir;
1818 else if (can_turn_right && rnd > rnd_value/8)
1819 MovDir[x][y] = right_dir;
1821 MovDir[x][y] = back_dir;
1823 if (!IS_FREE(x+move_xy[MovDir[x][y]].x, y+move_xy[MovDir[x][y]].y))
1824 MovDir[x][y] = old_move_dir;
1828 else if (element == EL_ROBOT || element == EL_SONDE ||
1829 element == EL_MAULWURF || element == EL_PINGUIN)
1831 int attr_x = -1, attr_y = -1;
1842 for (i=0; i<MAX_PLAYERS; i++)
1844 struct PlayerInfo *player = &stored_player[i];
1845 int jx = player->jx, jy = player->jy;
1847 if (!player->active)
1850 if (attr_x == -1 || ABS(jx-x)+ABS(jy-y) < ABS(attr_x-x)+ABS(attr_y-y))
1858 if (element == EL_ROBOT && ZX>=0 && ZY>=0)
1864 if (element == EL_MAULWURF || element == EL_PINGUIN)
1867 static int xy[4][2] =
1877 int ex = x + xy[i%4][0];
1878 int ey = y + xy[i%4][1];
1880 if (IN_LEV_FIELD(ex, ey) && Feld[ex][ey] == EL_AUSGANG_AUF)
1889 MovDir[x][y] = MV_NO_MOVING;
1891 MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
1893 MovDir[x][y] |= (AllPlayersGone ? MV_LEFT : MV_RIGHT);
1895 MovDir[x][y] |= (AllPlayersGone ? MV_DOWN : MV_UP);
1897 MovDir[x][y] |= (AllPlayersGone ? MV_UP : MV_DOWN);
1899 if (element == EL_ROBOT)
1903 if ((MovDir[x][y]&(MV_LEFT|MV_RIGHT)) && (MovDir[x][y]&(MV_UP|MV_DOWN)))
1904 MovDir[x][y] &= (RND(2) ? (MV_LEFT|MV_RIGHT) : (MV_UP|MV_DOWN));
1905 Moving2Blocked(x, y, &newx, &newy);
1907 if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
1908 MovDelay[x][y] = 8+8*!RND(3);
1910 MovDelay[x][y] = 16;
1918 if ((MovDir[x][y]&(MV_LEFT|MV_RIGHT)) && (MovDir[x][y]&(MV_UP|MV_DOWN)))
1920 boolean first_horiz = RND(2);
1921 int new_move_dir = MovDir[x][y];
1924 new_move_dir & (first_horiz ? (MV_LEFT|MV_RIGHT) : (MV_UP|MV_DOWN));
1925 Moving2Blocked(x, y, &newx, &newy);
1927 if (IN_LEV_FIELD(newx, newy) &&
1928 (IS_FREE(newx, newy) ||
1929 Feld[newx][newy] == EL_SALZSAEURE ||
1930 ((element == EL_MAULWURF || element == EL_PINGUIN) &&
1931 (Feld[newx][newy] == EL_AUSGANG_AUF ||
1932 IS_MAMPF3(Feld[newx][newy])))))
1936 new_move_dir & (!first_horiz ? (MV_LEFT|MV_RIGHT) : (MV_UP|MV_DOWN));
1937 Moving2Blocked(x, y, &newx, &newy);
1939 if (IN_LEV_FIELD(newx, newy) &&
1940 (IS_FREE(newx, newy) ||
1941 Feld[newx][newy] == EL_SALZSAEURE ||
1942 ((element == EL_MAULWURF || element == EL_PINGUIN) &&
1943 (Feld[newx][newy] == EL_AUSGANG_AUF ||
1944 IS_MAMPF3(Feld[newx][newy])))))
1947 MovDir[x][y] = old_move_dir;
1954 static boolean JustBeingPushed(int x, int y)
1958 for (i=0; i<MAX_PLAYERS; i++)
1960 struct PlayerInfo *player = &stored_player[i];
1962 if (player->active && player->Pushing && player->MovPos)
1964 int next_jx = player->jx + (player->jx - player->last_jx);
1965 int next_jy = player->jy + (player->jy - player->last_jy);
1967 if (x == next_jx && y == next_jy)
1975 void StartMoving(int x, int y)
1977 int element = Feld[x][y];
1982 if (CAN_FALL(element) && y<lev_fieldy-1)
1984 if ((x>0 && IS_PLAYER(x-1, y)) || (x<lev_fieldx-1 && IS_PLAYER(x+1, y)))
1985 if (JustBeingPushed(x, y))
1988 if (element == EL_MORAST_VOLL)
1990 if (IS_FREE(x, y+1))
1992 InitMovingField(x, y, MV_DOWN);
1993 Feld[x][y] = EL_FELSBROCKEN;
1994 Store[x][y] = EL_MORAST_LEER;
1996 else if (Feld[x][y+1] == EL_MORAST_LEER)
1998 if (!MovDelay[x][y])
1999 MovDelay[x][y] = TILEY + 1;
2008 Feld[x][y] = EL_MORAST_LEER;
2009 Feld[x][y+1] = EL_MORAST_VOLL;
2012 else if (element == EL_FELSBROCKEN && Feld[x][y+1] == EL_MORAST_LEER)
2014 InitMovingField(x, y, MV_DOWN);
2015 Store[x][y] = EL_MORAST_VOLL;
2017 else if (element == EL_SIEB_VOLL)
2019 if (IS_FREE(x, y+1))
2021 InitMovingField(x, y, MV_DOWN);
2022 Feld[x][y] = EL_CHANGED(Store2[x][y]);
2023 Store[x][y] = EL_SIEB_LEER;
2025 else if (Feld[x][y+1] == EL_SIEB_LEER)
2027 if (!MovDelay[x][y])
2028 MovDelay[x][y] = TILEY/4 + 1;
2037 Feld[x][y] = EL_SIEB_LEER;
2038 Feld[x][y+1] = EL_SIEB_VOLL;
2039 Store2[x][y+1] = EL_CHANGED(Store2[x][y]);
2043 else if (element == EL_SIEB2_VOLL)
2045 if (IS_FREE(x, y+1))
2047 InitMovingField(x, y, MV_DOWN);
2048 Feld[x][y] = EL_CHANGED2(Store2[x][y]);
2049 Store[x][y] = EL_SIEB2_LEER;
2051 else if (Feld[x][y+1] == EL_SIEB2_LEER)
2053 if (!MovDelay[x][y])
2054 MovDelay[x][y] = TILEY/4 + 1;
2063 Feld[x][y] = EL_SIEB2_LEER;
2064 Feld[x][y+1] = EL_SIEB2_VOLL;
2065 Store2[x][y+1] = EL_CHANGED2(Store2[x][y]);
2069 else if (CAN_CHANGE(element) &&
2070 (Feld[x][y+1] == EL_SIEB_LEER || Feld[x][y+1] == EL_SIEB2_LEER))
2072 InitMovingField(x, y, MV_DOWN);
2074 (Feld[x][y+1] == EL_SIEB_LEER ? EL_SIEB_VOLL : EL_SIEB2_VOLL);
2075 Store2[x][y+1] = element;
2077 else if (CAN_SMASH(element) && Feld[x][y+1] == EL_SALZSAEURE)
2080 InitMovingField(x, y, MV_DOWN);
2081 Store[x][y] = EL_SALZSAEURE;
2083 else if (CAN_SMASH(element) && Feld[x][y+1] == EL_BLOCKED && JustHit[x][y])
2087 else if (IS_FREE(x, y+1))
2089 InitMovingField(x, y, MV_DOWN);
2091 else if (element == EL_TROPFEN)
2093 Feld[x][y] = EL_AMOEBING;
2094 Store[x][y] = EL_AMOEBE_NASS;
2096 else if (IS_SLIPPERY(Feld[x][y+1]) && !Store[x][y+1])
2098 boolean left = (x>0 && IS_FREE(x-1, y) &&
2099 (IS_FREE(x-1, y+1) || Feld[x-1][y+1] == EL_SALZSAEURE));
2100 boolean right = (x<lev_fieldx-1 && IS_FREE(x+1, y) &&
2101 (IS_FREE(x+1, y+1) || Feld[x+1][y+1] == EL_SALZSAEURE));
2105 if (left && right && game.emulation != EMU_BOULDERDASH)
2106 left = !(right = RND(2));
2108 InitMovingField(x, y, left ? MV_LEFT : MV_RIGHT);
2112 else if (CAN_MOVE(element))
2116 if (element == EL_SONDE && JustBeingPushed(x, y))
2119 if (!MovDelay[x][y]) /* start new movement phase */
2121 /* all objects that can change their move direction after each step */
2122 /* (MAMPFER, MAMPFER2 and PACMAN go straight until they hit a wall */
2124 if (element!=EL_MAMPFER && element!=EL_MAMPFER2 && element!=EL_PACMAN)
2127 if (MovDelay[x][y] && (element == EL_KAEFER || element == EL_FLIEGER ||
2128 element == EL_SP_SNIKSNAK ||
2129 element == EL_SP_ELECTRON))
2130 DrawLevelField(x, y);
2134 if (MovDelay[x][y]) /* wait some time before next movement */
2138 if (element == EL_ROBOT ||
2139 element == EL_MAMPFER || element == EL_MAMPFER2)
2141 int phase = MovDelay[x][y] % 8;
2146 if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
2147 DrawGraphic(SCREENX(x), SCREENY(y), el2gfx(element)+phase);
2149 if ((element == EL_MAMPFER || element == EL_MAMPFER2)
2150 && MovDelay[x][y]%4 == 3)
2151 PlaySoundLevel(x, y, SND_NJAM);
2153 else if (element == EL_SP_ELECTRON)
2154 DrawGraphicAnimation(x, y, GFX2_SP_ELECTRON, 8, 2, ANIM_NORMAL);
2155 else if (element == EL_DRACHE)
2158 int dir = MovDir[x][y];
2159 int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
2160 int dy = (dir == MV_UP ? -1 : dir == MV_DOWN ? +1 : 0);
2161 int graphic = (dir == MV_LEFT ? GFX_FLAMMEN_LEFT :
2162 dir == MV_RIGHT ? GFX_FLAMMEN_RIGHT :
2163 dir == MV_UP ? GFX_FLAMMEN_UP :
2164 dir == MV_DOWN ? GFX_FLAMMEN_DOWN : GFX_LEERRAUM);
2165 int phase = FrameCounter % 2;
2167 for (i=1; i<=3; i++)
2169 int xx = x + i*dx, yy = y + i*dy;
2170 int sx = SCREENX(xx), sy = SCREENY(yy);
2172 if (!IN_LEV_FIELD(xx, yy) ||
2173 IS_SOLID(Feld[xx][yy]) || Feld[xx][yy] == EL_EXPLODING)
2178 int flamed = MovingOrBlocked2Element(xx, yy);
2180 if (IS_ENEMY(flamed) || IS_EXPLOSIVE(flamed))
2183 RemoveMovingField(xx, yy);
2185 Feld[xx][yy] = EL_BURNING;
2186 if (IN_SCR_FIELD(sx, sy))
2187 DrawGraphic(sx, sy, graphic + phase*3 + i-1);
2191 if (Feld[xx][yy] == EL_BURNING)
2192 Feld[xx][yy] = EL_LEERRAUM;
2193 DrawLevelField(xx, yy);
2202 if (element == EL_KAEFER || element == EL_BUTTERFLY)
2204 PlaySoundLevel(x, y, SND_KLAPPER);
2206 else if (element == EL_FLIEGER || element == EL_FIREFLY)
2208 PlaySoundLevel(x, y, SND_ROEHR);
2211 /* now make next step */
2213 Moving2Blocked(x, y, &newx, &newy); /* get next screen position */
2215 if (IS_ENEMY(element) && IS_PLAYER(newx, newy))
2217 /* enemy got the player */
2219 KillHero(PLAYERINFO(newx, newy));
2222 else if ((element == EL_MAULWURF || element == EL_PINGUIN ||
2223 element == EL_ROBOT || element == EL_SONDE) &&
2224 IN_LEV_FIELD(newx, newy) &&
2225 MovDir[x][y] == MV_DOWN && Feld[newx][newy] == EL_SALZSAEURE)
2228 Store[x][y] = EL_SALZSAEURE;
2230 else if ((element == EL_MAULWURF || element == EL_PINGUIN) &&
2231 IN_LEV_FIELD(newx, newy))
2233 if (Feld[newx][newy] == EL_AUSGANG_AUF)
2235 Feld[x][y] = EL_LEERRAUM;
2236 DrawLevelField(x, y);
2238 PlaySoundLevel(newx, newy, SND_BUING);
2239 if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
2240 DrawGraphicThruMask(SCREENX(newx), SCREENY(newy), el2gfx(element));
2242 local_player->friends_still_needed--;
2243 if (!local_player->friends_still_needed &&
2244 !local_player->GameOver && AllPlayersGone)
2245 local_player->LevelSolved = local_player->GameOver = TRUE;
2249 else if (IS_MAMPF3(Feld[newx][newy]))
2251 if (DigField(local_player, newx, newy, 0, 0, DF_DIG) == MF_MOVING)
2252 DrawLevelField(newx, newy);
2254 MovDir[x][y] = MV_NO_MOVING;
2256 else if (!IS_FREE(newx, newy))
2258 if (IS_PLAYER(x, y))
2259 DrawPlayerField(x, y);
2261 DrawLevelField(x, y);
2265 else if (element == EL_SCHWEIN && IN_LEV_FIELD(newx, newy))
2267 if (IS_GEM(Feld[newx][newy]))
2269 if (IS_MOVING(newx, newy))
2270 RemoveMovingField(newx, newy);
2273 Feld[newx][newy] = EL_LEERRAUM;
2274 DrawLevelField(newx, newy);
2277 else if (!IS_FREE(newx, newy))
2279 if (IS_PLAYER(x, y))
2280 DrawPlayerField(x, y);
2282 DrawLevelField(x, y);
2286 else if (element == EL_DRACHE && IN_LEV_FIELD(newx, newy))
2288 if (!IS_FREE(newx, newy))
2290 if (IS_PLAYER(x, y))
2291 DrawPlayerField(x, y);
2293 DrawLevelField(x, y);
2298 boolean wanna_flame = !RND(10);
2299 int dx = newx - x, dy = newy - y;
2300 int newx1 = newx+1*dx, newy1 = newy+1*dy;
2301 int newx2 = newx+2*dx, newy2 = newy+2*dy;
2302 int element1 = (IN_LEV_FIELD(newx1, newy1) ?
2303 MovingOrBlocked2Element(newx1, newy1) : EL_BETON);
2304 int element2 = (IN_LEV_FIELD(newx2, newy2) ?
2305 MovingOrBlocked2Element(newx2, newy2) : EL_BETON);
2307 if ((wanna_flame || IS_ENEMY(element1) || IS_ENEMY(element2)) &&
2308 element1 != EL_DRACHE && element2 != EL_DRACHE &&
2309 element1 != EL_BURNING && element2 != EL_BURNING)
2311 if (IS_PLAYER(x, y))
2312 DrawPlayerField(x, y);
2314 DrawLevelField(x, y);
2316 MovDelay[x][y] = 50;
2317 Feld[newx][newy] = EL_BURNING;
2318 if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_LEERRAUM)
2319 Feld[newx1][newy1] = EL_BURNING;
2320 if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_LEERRAUM)
2321 Feld[newx2][newy2] = EL_BURNING;
2326 else if (element == EL_MAMPFER && IN_LEV_FIELD(newx, newy) &&
2327 Feld[newx][newy] == EL_DIAMANT)
2329 if (IS_MOVING(newx, newy))
2330 RemoveMovingField(newx, newy);
2333 Feld[newx][newy] = EL_LEERRAUM;
2334 DrawLevelField(newx, newy);
2337 else if (element == EL_MAMPFER2 && IN_LEV_FIELD(newx, newy) &&
2338 IS_MAMPF2(Feld[newx][newy]))
2340 if (AmoebaNr[newx][newy])
2342 AmoebaCnt2[AmoebaNr[newx][newy]]--;
2343 if (Feld[newx][newy] == EL_AMOEBE_VOLL ||
2344 Feld[newx][newy] == EL_AMOEBE_BD)
2345 AmoebaCnt[AmoebaNr[newx][newy]]--;
2348 if (IS_MOVING(newx, newy))
2349 RemoveMovingField(newx, newy);
2352 Feld[newx][newy] = EL_LEERRAUM;
2353 DrawLevelField(newx, newy);
2356 else if (element == EL_PACMAN && IN_LEV_FIELD(newx, newy) &&
2357 IS_AMOEBOID(Feld[newx][newy]))
2359 if (AmoebaNr[newx][newy])
2361 AmoebaCnt2[AmoebaNr[newx][newy]]--;
2362 if (Feld[newx][newy] == EL_AMOEBE_VOLL ||
2363 Feld[newx][newy] == EL_AMOEBE_BD)
2364 AmoebaCnt[AmoebaNr[newx][newy]]--;
2367 Feld[newx][newy] = EL_LEERRAUM;
2368 DrawLevelField(newx, newy);
2370 else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
2372 /* object was running against a wall */
2376 if (element == EL_KAEFER || element == EL_FLIEGER ||
2377 element == EL_SP_SNIKSNAK)
2378 DrawLevelField(x, y);
2379 else if (element == EL_BUTTERFLY || element == EL_FIREFLY)
2380 DrawGraphicAnimation(x, y, el2gfx(element), 2, 4, ANIM_NORMAL);
2381 else if (element == EL_SONDE)
2382 DrawGraphicAnimation(x, y, GFX_SONDE_START, 8, 2, ANIM_NORMAL);
2383 else if (element == EL_SP_ELECTRON)
2384 DrawGraphicAnimation(x, y, GFX2_SP_ELECTRON, 8, 2, ANIM_NORMAL);
2389 if (element == EL_ROBOT && IN_SCR_FIELD(x, y))
2390 PlaySoundLevel(x, y, SND_SCHLURF);
2392 InitMovingField(x, y, MovDir[x][y]);
2396 ContinueMoving(x, y);
2399 void ContinueMoving(int x, int y)
2401 int element = Feld[x][y];
2402 int direction = MovDir[x][y];
2403 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
2404 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
2405 int horiz_move = (dx!=0);
2406 int newx = x + dx, newy = y + dy;
2407 int step = (horiz_move ? dx : dy) * TILEX/8;
2409 if (CAN_FALL(element) && horiz_move && !IS_SP_ELEMENT(element))
2411 else if (element == EL_TROPFEN)
2413 else if (Store[x][y] == EL_MORAST_VOLL || Store[x][y] == EL_MORAST_LEER)
2416 MovPos[x][y] += step;
2418 if (ABS(MovPos[x][y])>=TILEX) /* object reached its destination */
2420 Feld[x][y] = EL_LEERRAUM;
2421 Feld[newx][newy] = element;
2423 if (Store[x][y] == EL_MORAST_VOLL)
2426 Feld[newx][newy] = EL_MORAST_VOLL;
2427 element = EL_MORAST_VOLL;
2429 else if (Store[x][y] == EL_MORAST_LEER)
2432 Feld[x][y] = EL_MORAST_LEER;
2434 else if (Store[x][y] == EL_SIEB_VOLL)
2437 element = Feld[newx][newy] =
2438 (game.magic_wall_active ? EL_SIEB_VOLL : EL_SIEB_TOT);
2440 else if (Store[x][y] == EL_SIEB_LEER)
2442 Store[x][y] = Store2[x][y] = 0;
2443 Feld[x][y] = (game.magic_wall_active ? EL_SIEB_LEER : EL_SIEB_TOT);
2445 else if (Store[x][y] == EL_SIEB2_VOLL)
2448 element = Feld[newx][newy] =
2449 (game.magic_wall_active ? EL_SIEB2_VOLL : EL_SIEB2_TOT);
2451 else if (Store[x][y] == EL_SIEB2_LEER)
2453 Store[x][y] = Store2[x][y] = 0;
2454 Feld[x][y] = (game.magic_wall_active ? EL_SIEB2_LEER : EL_SIEB2_TOT);
2456 else if (Store[x][y] == EL_SALZSAEURE)
2459 Feld[newx][newy] = EL_SALZSAEURE;
2460 element = EL_SALZSAEURE;
2462 else if (Store[x][y] == EL_AMOEBE_NASS)
2465 Feld[x][y] = EL_AMOEBE_NASS;
2468 MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
2469 MovDelay[newx][newy] = 0;
2471 if (!CAN_MOVE(element))
2472 MovDir[newx][newy] = 0;
2474 DrawLevelField(x, y);
2475 DrawLevelField(newx, newy);
2477 Stop[newx][newy] = TRUE;
2478 JustHit[x][newy] = 3;
2480 if (DONT_TOUCH(element)) /* object may be nasty to player or others */
2482 TestIfBadThingHitsHero(newx, newy);
2483 TestIfBadThingHitsFriend(newx, newy);
2484 TestIfBadThingHitsOtherBadThing(newx, newy);
2486 else if (element == EL_PINGUIN)
2487 TestIfFriendHitsBadThing(newx, newy);
2489 if (CAN_SMASH(element) && direction == MV_DOWN &&
2490 (newy == lev_fieldy-1 || !IS_FREE(x, newy+1)))
2493 else /* still moving on */
2494 DrawLevelField(x, y);
2497 int AmoebeNachbarNr(int ax, int ay)
2500 int element = Feld[ax][ay];
2502 static int xy[4][2] =
2512 int x = ax + xy[i][0];
2513 int y = ay + xy[i][1];
2515 if (!IN_LEV_FIELD(x, y))
2518 if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
2519 group_nr = AmoebaNr[x][y];
2525 void AmoebenVereinigen(int ax, int ay)
2527 int i, x, y, xx, yy;
2528 int new_group_nr = AmoebaNr[ax][ay];
2529 static int xy[4][2] =
2537 if (new_group_nr == 0)
2545 if (!IN_LEV_FIELD(x, y))
2548 if ((Feld[x][y] == EL_AMOEBE_VOLL ||
2549 Feld[x][y] == EL_AMOEBE_BD ||
2550 Feld[x][y] == EL_AMOEBE_TOT) &&
2551 AmoebaNr[x][y] != new_group_nr)
2553 int old_group_nr = AmoebaNr[x][y];
2555 if (old_group_nr == 0)
2558 AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
2559 AmoebaCnt[old_group_nr] = 0;
2560 AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
2561 AmoebaCnt2[old_group_nr] = 0;
2563 for (yy=0; yy<lev_fieldy; yy++)
2565 for (xx=0; xx<lev_fieldx; xx++)
2567 if (AmoebaNr[xx][yy] == old_group_nr)
2568 AmoebaNr[xx][yy] = new_group_nr;
2575 void AmoebeUmwandeln(int ax, int ay)
2579 if (Feld[ax][ay] == EL_AMOEBE_TOT)
2581 int group_nr = AmoebaNr[ax][ay];
2586 printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
2587 printf("AmoebeUmwandeln(): This should never happen!\n");
2592 for (y=0; y<lev_fieldy; y++)
2594 for (x=0; x<lev_fieldx; x++)
2596 if (Feld[x][y] == EL_AMOEBE_TOT && AmoebaNr[x][y] == group_nr)
2599 Feld[x][y] = EL_AMOEBA2DIAM;
2607 static int xy[4][2] =
2620 if (!IN_LEV_FIELD(x, y))
2623 if (Feld[x][y] == EL_AMOEBA2DIAM)
2629 void AmoebeUmwandelnBD(int ax, int ay, int new_element)
2632 int group_nr = AmoebaNr[ax][ay];
2633 boolean done = FALSE;
2638 printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
2639 printf("AmoebeUmwandelnBD(): This should never happen!\n");
2644 for (y=0; y<lev_fieldy; y++)
2646 for (x=0; x<lev_fieldx; x++)
2648 if (AmoebaNr[x][y] == group_nr &&
2649 (Feld[x][y] == EL_AMOEBE_TOT ||
2650 Feld[x][y] == EL_AMOEBE_BD ||
2651 Feld[x][y] == EL_AMOEBING))
2654 Feld[x][y] = new_element;
2655 InitField(x, y, FALSE);
2656 DrawLevelField(x, y);
2663 PlaySoundLevel(ax, ay,
2664 (new_element == EL_FELSBROCKEN ? SND_KLOPF : SND_PLING));
2667 void AmoebeWaechst(int x, int y)
2669 static unsigned long sound_delay = 0;
2670 static unsigned long sound_delay_value = 0;
2672 if (!MovDelay[x][y]) /* start new growing cycle */
2676 if (DelayReached(&sound_delay, sound_delay_value))
2678 PlaySoundLevel(x, y, SND_AMOEBE);
2679 sound_delay_value = 30;
2683 if (MovDelay[x][y]) /* wait some time before growing bigger */
2686 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
2687 DrawGraphic(SCREENX(x), SCREENY(y), GFX_AMOEBING + 3 - MovDelay[x][y]/2);
2689 if (!MovDelay[x][y])
2691 Feld[x][y] = Store[x][y];
2693 DrawLevelField(x, y);
2698 void AmoebeAbleger(int ax, int ay)
2701 int element = Feld[ax][ay];
2702 int newax = ax, neway = ay;
2703 static int xy[4][2] =
2711 if (!level.amoeba_speed)
2713 Feld[ax][ay] = EL_AMOEBE_TOT;
2714 DrawLevelField(ax, ay);
2718 if (!MovDelay[ax][ay]) /* start making new amoeba field */
2719 MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
2721 if (MovDelay[ax][ay]) /* wait some time before making new amoeba */
2724 if (MovDelay[ax][ay])
2728 if (element == EL_AMOEBE_NASS) /* object is an acid / amoeba drop */
2731 int x = ax + xy[start][0];
2732 int y = ay + xy[start][1];
2734 if (!IN_LEV_FIELD(x, y))
2737 if (IS_FREE(x, y) ||
2738 Feld[x][y] == EL_ERDREICH || Feld[x][y] == EL_MORAST_LEER)
2744 if (newax == ax && neway == ay)
2747 else /* normal or "filled" (BD style) amoeba */
2750 boolean waiting_for_player = FALSE;
2754 int j = (start + i) % 4;
2755 int x = ax + xy[j][0];
2756 int y = ay + xy[j][1];
2758 if (!IN_LEV_FIELD(x, y))
2761 if (IS_FREE(x, y) ||
2762 Feld[x][y] == EL_ERDREICH || Feld[x][y] == EL_MORAST_LEER)
2768 else if (IS_PLAYER(x, y))
2769 waiting_for_player = TRUE;
2772 if (newax == ax && neway == ay) /* amoeba cannot grow */
2774 if (i == 4 && (!waiting_for_player || game.emulation == EMU_BOULDERDASH))
2776 Feld[ax][ay] = EL_AMOEBE_TOT;
2777 DrawLevelField(ax, ay);
2778 AmoebaCnt[AmoebaNr[ax][ay]]--;
2780 if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0) /* amoeba is completely dead */
2782 if (element == EL_AMOEBE_VOLL)
2783 AmoebeUmwandeln(ax, ay);
2784 else if (element == EL_AMOEBE_BD)
2785 AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
2790 else if (element == EL_AMOEBE_VOLL || element == EL_AMOEBE_BD)
2792 /* amoeba gets larger by growing in some direction */
2794 int new_group_nr = AmoebaNr[ax][ay];
2797 if (new_group_nr == 0)
2799 printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
2800 printf("AmoebeAbleger(): This should never happen!\n");
2805 AmoebaNr[newax][neway] = new_group_nr;
2806 AmoebaCnt[new_group_nr]++;
2807 AmoebaCnt2[new_group_nr]++;
2809 /* if amoeba touches other amoeba(s) after growing, unify them */
2810 AmoebenVereinigen(newax, neway);
2812 if (element == EL_AMOEBE_BD && AmoebaCnt2[new_group_nr] >= 200)
2814 AmoebeUmwandelnBD(newax, neway, EL_FELSBROCKEN);
2820 if (element != EL_AMOEBE_NASS || neway < ay || !IS_FREE(newax, neway) ||
2821 (neway == lev_fieldy - 1 && newax != ax))
2823 Feld[newax][neway] = EL_AMOEBING;
2824 Store[newax][neway] = element;
2826 else if (neway == ay)
2827 Feld[newax][neway] = EL_TROPFEN;
2830 InitMovingField(ax, ay, MV_DOWN);
2831 Feld[ax][ay] = EL_TROPFEN;
2832 Store[ax][ay] = EL_AMOEBE_NASS;
2833 ContinueMoving(ax, ay);
2837 DrawLevelField(newax, neway);
2840 void Life(int ax, int ay)
2843 static int life[4] = { 2, 3, 3, 3 }; /* parameters for "game of life" */
2845 int element = Feld[ax][ay];
2850 if (!MovDelay[ax][ay]) /* start new "game of life" cycle */
2851 MovDelay[ax][ay] = life_time;
2853 if (MovDelay[ax][ay]) /* wait some time before next cycle */
2856 if (MovDelay[ax][ay])
2860 for (y1=-1; y1<2; y1++) for(x1=-1; x1<2; x1++)
2862 int xx = ax+x1, yy = ay+y1;
2865 if (!IN_LEV_FIELD(xx, yy))
2868 for (y2=-1; y2<2; y2++) for (x2=-1; x2<2; x2++)
2870 int x = xx+x2, y = yy+y2;
2872 if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
2875 if (((Feld[x][y] == element ||
2876 (element == EL_LIFE && IS_PLAYER(x, y))) &&
2878 (IS_FREE(x, y) && Stop[x][y]))
2882 if (xx == ax && yy == ay) /* field in the middle */
2884 if (nachbarn<life[0] || nachbarn>life[1])
2886 Feld[xx][yy] = EL_LEERRAUM;
2888 DrawLevelField(xx, yy);
2889 Stop[xx][yy] = TRUE;
2892 else if (IS_FREE(xx, yy) || Feld[xx][yy] == EL_ERDREICH)
2893 { /* free border field */
2894 if (nachbarn>=life[2] && nachbarn<=life[3])
2896 Feld[xx][yy] = element;
2897 MovDelay[xx][yy] = (element == EL_LIFE ? 0 : life_time-1);
2899 DrawLevelField(xx, yy);
2900 Stop[xx][yy] = TRUE;
2906 void Ablenk(int x, int y)
2908 if (!MovDelay[x][y]) /* next animation frame */
2909 MovDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
2911 if (MovDelay[x][y]) /* wait some time before next frame */
2916 if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
2917 DrawGraphic(SCREENX(x), SCREENY(y), GFX_ABLENK+MovDelay[x][y]%4);
2918 if (!(MovDelay[x][y]%4))
2919 PlaySoundLevel(x, y, SND_MIEP);
2924 Feld[x][y] = EL_ABLENK_AUS;
2925 DrawLevelField(x, y);
2926 if (ZX == x && ZY == y)
2930 void Birne(int x, int y)
2932 if (!MovDelay[x][y]) /* next animation frame */
2933 MovDelay[x][y] = 800;
2935 if (MovDelay[x][y]) /* wait some time before next frame */
2940 if (!(MovDelay[x][y]%5))
2942 if (!(MovDelay[x][y]%10))
2943 Feld[x][y]=EL_ABLENK_EIN;
2945 Feld[x][y]=EL_ABLENK_AUS;
2946 DrawLevelField(x, y);
2947 Feld[x][y]=EL_ABLENK_EIN;
2953 Feld[x][y]=EL_ABLENK_AUS;
2954 DrawLevelField(x, y);
2955 if (ZX == x && ZY == y)
2959 void Blubber(int x, int y)
2961 if (y > 0 && IS_MOVING(x, y-1) && MovDir[x][y-1] == MV_DOWN)
2962 DrawLevelField(x, y-1);
2964 DrawGraphicAnimation(x, y, GFX_GEBLUBBER, 4, 10, ANIM_NORMAL);
2967 void NussKnacken(int x, int y)
2969 if (!MovDelay[x][y]) /* next animation frame */
2972 if (MovDelay[x][y]) /* wait some time before next frame */
2975 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
2976 DrawGraphic(SCREENX(x), SCREENY(y), GFX_CRACKINGNUT+3-MovDelay[x][y]/2);
2978 if (!MovDelay[x][y])
2980 Feld[x][y] = EL_EDELSTEIN;
2981 DrawLevelField(x, y);
2986 void SiebAktivieren(int x, int y, int typ)
2988 int graphic = (typ == 1 ? GFX_SIEB_VOLL : GFX_SIEB2_VOLL) + 3;
2990 DrawGraphicAnimation(x, y, graphic, 4, 4, ANIM_REVERSE);
2993 void AusgangstuerPruefen(int x, int y)
2995 if (!local_player->gems_still_needed &&
2996 !local_player->sokobanfields_still_needed &&
2997 !local_player->lights_still_needed)
2999 Feld[x][y] = EL_AUSGANG_ACT;
3001 PlaySoundLevel(x < LEVELX(BX1) ? LEVELX(BX1) :
3002 (x > LEVELX(BX2) ? LEVELX(BX2) : x),
3003 y < LEVELY(BY1) ? LEVELY(BY1) :
3004 (y > LEVELY(BY2) ? LEVELY(BY2) : y),
3009 void AusgangstuerOeffnen(int x, int y)
3013 if (!MovDelay[x][y]) /* next animation frame */
3014 MovDelay[x][y] = 5*delay;
3016 if (MovDelay[x][y]) /* wait some time before next frame */
3021 tuer = MovDelay[x][y]/delay;
3022 if (!(MovDelay[x][y]%delay) && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
3023 DrawGraphic(SCREENX(x), SCREENY(y), GFX_AUSGANG_AUF-tuer);
3025 if (!MovDelay[x][y])
3027 Feld[x][y] = EL_AUSGANG_AUF;
3028 DrawLevelField(x, y);
3033 void AusgangstuerBlinken(int x, int y)
3035 DrawGraphicAnimation(x, y, GFX_AUSGANG_AUF, 4, 4, ANIM_OSCILLATE);
3038 void EdelsteinFunkeln(int x, int y)
3040 if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
3043 if (Feld[x][y] == EL_EDELSTEIN_BD)
3044 DrawGraphicAnimation(x, y, GFX_EDELSTEIN_BD, 4, 4, ANIM_REVERSE);
3047 if (!MovDelay[x][y]) /* next animation frame */
3048 MovDelay[x][y] = 11 * !SimpleRND(500);
3050 if (MovDelay[x][y]) /* wait some time before next frame */
3054 if (setup.direct_draw && MovDelay[x][y])
3055 SetDrawtoField(DRAW_BUFFERED);
3057 DrawGraphic(SCREENX(x), SCREENY(y), el2gfx(Feld[x][y]));
3061 int phase = (MovDelay[x][y]-1)/2;
3066 DrawGraphicThruMask(SCREENX(x), SCREENY(y), GFX_FUNKELN_WEISS + phase);
3068 if (setup.direct_draw)
3072 dest_x = FX + SCREENX(x)*TILEX;
3073 dest_y = FY + SCREENY(y)*TILEY;
3075 XCopyArea(display, drawto_field, window, gc,
3076 dest_x, dest_y, TILEX, TILEY, dest_x, dest_y);
3077 SetDrawtoField(DRAW_DIRECT);
3084 void MauerWaechst(int x, int y)
3088 if (!MovDelay[x][y]) /* next animation frame */
3089 MovDelay[x][y] = 3*delay;
3091 if (MovDelay[x][y]) /* wait some time before next frame */
3096 phase = 2-MovDelay[x][y]/delay;
3097 if (!(MovDelay[x][y]%delay) && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
3098 DrawGraphic(SCREENX(x), SCREENY(y),
3099 (MovDir[x][y] == MV_LEFT ? GFX_MAUER_LEFT :
3100 MovDir[x][y] == MV_RIGHT ? GFX_MAUER_RIGHT :
3101 MovDir[x][y] == MV_UP ? GFX_MAUER_UP :
3102 GFX_MAUER_DOWN ) + phase);
3104 if (!MovDelay[x][y])
3106 if (MovDir[x][y] == MV_LEFT)
3108 if (IN_LEV_FIELD(x-1, y) && IS_MAUER(Feld[x-1][y]))
3109 DrawLevelField(x-1, y);
3111 else if (MovDir[x][y] == MV_RIGHT)
3113 if (IN_LEV_FIELD(x+1, y) && IS_MAUER(Feld[x+1][y]))
3114 DrawLevelField(x+1, y);
3116 else if (MovDir[x][y] == MV_UP)
3118 if (IN_LEV_FIELD(x, y-1) && IS_MAUER(Feld[x][y-1]))
3119 DrawLevelField(x, y-1);
3123 if (IN_LEV_FIELD(x, y+1) && IS_MAUER(Feld[x][y+1]))
3124 DrawLevelField(x, y+1);
3127 Feld[x][y] = Store[x][y];
3129 MovDir[x][y] = MV_NO_MOVING;
3130 DrawLevelField(x, y);
3135 void MauerAbleger(int ax, int ay)
3137 int element = Feld[ax][ay];
3138 boolean oben_frei = FALSE, unten_frei = FALSE;
3139 boolean links_frei = FALSE, rechts_frei = FALSE;
3140 boolean oben_massiv = FALSE, unten_massiv = FALSE;
3141 boolean links_massiv = FALSE, rechts_massiv = FALSE;
3143 if (!MovDelay[ax][ay]) /* start building new wall */
3144 MovDelay[ax][ay] = 6;
3146 if (MovDelay[ax][ay]) /* wait some time before building new wall */
3149 if (MovDelay[ax][ay])
3153 if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
3155 if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
3157 if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
3159 if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
3162 if (element == EL_MAUER_Y || element == EL_MAUER_XY)
3166 Feld[ax][ay-1] = EL_MAUERND;
3167 Store[ax][ay-1] = element;
3168 MovDir[ax][ay-1] = MV_UP;
3169 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
3170 DrawGraphic(SCREENX(ax), SCREENY(ay-1), GFX_MAUER_UP);
3174 Feld[ax][ay+1] = EL_MAUERND;
3175 Store[ax][ay+1] = element;
3176 MovDir[ax][ay+1] = MV_DOWN;
3177 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
3178 DrawGraphic(SCREENX(ax), SCREENY(ay+1), GFX_MAUER_DOWN);
3182 if (element == EL_MAUER_X || element == EL_MAUER_XY ||
3183 element == EL_MAUER_LEBT)
3187 Feld[ax-1][ay] = EL_MAUERND;
3188 Store[ax-1][ay] = element;
3189 MovDir[ax-1][ay] = MV_LEFT;
3190 if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
3191 DrawGraphic(SCREENX(ax-1), SCREENY(ay), GFX_MAUER_LEFT);
3195 Feld[ax+1][ay] = EL_MAUERND;
3196 Store[ax+1][ay] = element;
3197 MovDir[ax+1][ay] = MV_RIGHT;
3198 if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
3199 DrawGraphic(SCREENX(ax+1), SCREENY(ay), GFX_MAUER_RIGHT);
3203 if (element == EL_MAUER_LEBT && (links_frei || rechts_frei))
3204 DrawLevelField(ax, ay);
3206 if (!IN_LEV_FIELD(ax, ay-1) || IS_MAUER(Feld[ax][ay-1]))
3208 if (!IN_LEV_FIELD(ax, ay+1) || IS_MAUER(Feld[ax][ay+1]))
3209 unten_massiv = TRUE;
3210 if (!IN_LEV_FIELD(ax-1, ay) || IS_MAUER(Feld[ax-1][ay]))
3211 links_massiv = TRUE;
3212 if (!IN_LEV_FIELD(ax+1, ay) || IS_MAUER(Feld[ax+1][ay]))
3213 rechts_massiv = TRUE;
3215 if (((oben_massiv && unten_massiv) ||
3216 element == EL_MAUER_X || element == EL_MAUER_LEBT) &&
3217 ((links_massiv && rechts_massiv) ||
3218 element == EL_MAUER_Y))
3219 Feld[ax][ay] = EL_MAUERWERK;
3222 void CheckForDragon(int x, int y)
3225 boolean dragon_found = FALSE;
3226 static int xy[4][2] =
3238 int xx = x + j*xy[i][0], yy = y + j*xy[i][1];
3240 if (IN_LEV_FIELD(xx, yy) &&
3241 (Feld[xx][yy] == EL_BURNING || Feld[xx][yy] == EL_DRACHE))
3243 if (Feld[xx][yy] == EL_DRACHE)
3244 dragon_found = TRUE;
3257 int xx = x + j*xy[i][0], yy = y + j*xy[i][1];
3259 if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_BURNING)
3261 Feld[xx][yy] = EL_LEERRAUM;
3262 DrawLevelField(xx, yy);
3271 static void CheckBuggyBase(int x, int y)
3273 int element = Feld[x][y];
3275 if (element == EL_SP_BUG)
3277 if (!MovDelay[x][y]) /* start activating buggy base */
3278 MovDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
3280 if (MovDelay[x][y]) /* wait some time before activating base */
3283 if (MovDelay[x][y] < 5 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
3284 DrawGraphic(SCREENX(x), SCREENY(y), GFX_SP_BUG_WARNING);
3288 Feld[x][y] = EL_SP_BUG_ACTIVE;
3291 else if (element == EL_SP_BUG_ACTIVE)
3293 if (!MovDelay[x][y]) /* start activating buggy base */
3294 MovDelay[x][y] = 1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND);
3296 if (MovDelay[x][y]) /* wait some time before activating base */
3302 static int xy[4][2] =
3310 if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
3311 DrawGraphic(SCREENX(x),SCREENY(y), GFX_SP_BUG_ACTIVE + SimpleRND(4));
3315 int xx = x + xy[i][0], yy = y + xy[i][1];
3317 if (IS_PLAYER(xx, yy))
3319 PlaySoundLevel(x, y, SND_SP_BUG);
3327 Feld[x][y] = EL_SP_BUG;
3328 DrawLevelField(x, y);
3333 static void PlayerActions(struct PlayerInfo *player, byte player_action)
3335 static byte stored_player_action[MAX_PLAYERS];
3336 static int num_stored_actions = 0;
3337 static boolean save_tape_entry = FALSE;
3338 boolean moved = FALSE, snapped = FALSE, bombed = FALSE;
3339 int jx = player->jx, jy = player->jy;
3340 int left = player_action & JOY_LEFT;
3341 int right = player_action & JOY_RIGHT;
3342 int up = player_action & JOY_UP;
3343 int down = player_action & JOY_DOWN;
3344 int button1 = player_action & JOY_BUTTON_1;
3345 int button2 = player_action & JOY_BUTTON_2;
3346 int dx = (left ? -1 : right ? 1 : 0);
3347 int dy = (up ? -1 : down ? 1 : 0);
3349 stored_player_action[player->index_nr] = 0;
3350 num_stored_actions++;
3352 if (!player->active || tape.pausing)
3357 save_tape_entry = TRUE;
3358 player->frame_reset_delay = 0;
3361 snapped = SnapField(player, dx, dy);
3365 bombed = PlaceBomb(player);
3366 moved = MoveFigure(player, dx, dy);
3369 if (tape.recording && (moved || snapped || bombed))
3371 if (bombed && !moved)
3372 player_action &= JOY_BUTTON;
3374 stored_player_action[player->index_nr] = player_action;
3376 else if (tape.playing && snapped)
3377 SnapField(player, 0, 0); /* stop snapping */
3381 /* no actions for this player (no input at player's configured device) */
3383 DigField(player, 0, 0, 0, 0, DF_NO_PUSH);
3384 SnapField(player, 0, 0);
3385 CheckGravityMovement(player);
3387 if (++player->frame_reset_delay > player->move_delay_value)
3391 if (tape.recording && num_stored_actions >= MAX_PLAYERS && save_tape_entry)
3393 TapeRecordAction(stored_player_action);
3394 num_stored_actions = 0;
3395 save_tape_entry = FALSE;
3398 if (tape.playing && !tape.pausing && !player_action &&
3399 tape.counter < tape.length)
3402 tape.pos[tape.counter].action[player->index_nr] & (JOY_LEFT|JOY_RIGHT);
3404 if ((next_joy == JOY_LEFT || next_joy == JOY_RIGHT) &&
3405 (player->MovDir != JOY_UP && player->MovDir != JOY_DOWN))
3407 int dx = (next_joy == JOY_LEFT ? -1 : +1);
3409 if (IN_LEV_FIELD(jx+dx, jy) && IS_PUSHABLE(Feld[jx+dx][jy]))
3411 int el = Feld[jx+dx][jy];
3412 int push_delay = (IS_SB_ELEMENT(el) || el == EL_SONDE ? 2 : 10);
3414 if (tape.delay_played + push_delay >= tape.pos[tape.counter].delay)
3416 player->MovDir = next_joy;
3417 player->Frame = FrameCounter % 4;
3418 player->Pushing = TRUE;
3427 static unsigned long action_delay = 0;
3428 unsigned long action_delay_value;
3429 int sieb_x = 0, sieb_y = 0;
3430 int i, x, y, element;
3431 byte *recorded_player_action;
3432 byte summarized_player_action = 0;
3434 if (game_status != PLAYING)
3437 action_delay_value =
3438 (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
3440 /* ---------- main game synchronization point ---------- */
3442 WaitUntilDelayReached(&action_delay, action_delay_value);
3444 if (network_playing && !network_player_action_received)
3448 printf("DEBUG: try to get network player actions in time\n");
3453 /* last chance to get network player actions without main loop delay */
3457 if (game_status != PLAYING)
3460 if (!network_player_action_received)
3464 printf("DEBUG: failed to get network player actions in time\n");
3476 else if (tape.recording)
3479 recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
3481 for (i=0; i<MAX_PLAYERS; i++)
3483 summarized_player_action |= stored_player[i].action;
3485 if (!network_playing)
3486 stored_player[i].effective_action = stored_player[i].action;
3490 if (network_playing)
3491 SendToServer_MovePlayer(summarized_player_action);
3494 if (!options.network && !setup.team_mode)
3495 local_player->effective_action = summarized_player_action;
3497 for (i=0; i<MAX_PLAYERS; i++)
3499 int actual_player_action = stored_player[i].effective_action;
3501 if (stored_player[i].programmed_action)
3502 actual_player_action = stored_player[i].programmed_action;
3504 if (recorded_player_action)
3505 actual_player_action = recorded_player_action[i];
3507 PlayerActions(&stored_player[i], actual_player_action);
3508 ScrollFigure(&stored_player[i], SCROLL_GO_ON);
3511 network_player_action_received = FALSE;
3513 ScrollScreen(NULL, SCROLL_GO_ON);
3519 if (TimeFrames == 0 && local_player->active)
3521 extern unsigned int last_RND();
3523 printf("DEBUG: %03d last RND was %d \t [state checksum is %d]\n",
3524 TimePlayed, last_RND(), getStateCheckSum(TimePlayed));
3531 if (GameFrameDelay >= 500)
3532 printf("FrameCounter == %d\n", FrameCounter);
3541 for (y=0; y<lev_fieldy; y++) for (x=0; x<lev_fieldx; x++)
3544 if (JustHit[x][y]>0)
3548 if (IS_BLOCKED(x, y))
3552 Blocked2Moving(x, y, &oldx, &oldy);
3553 if (!IS_MOVING(oldx, oldy))
3555 printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
3556 printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
3557 printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
3558 printf("GameActions(): This should never happen!\n");
3564 for (y=0; y<lev_fieldy; y++) for (x=0; x<lev_fieldx; x++)
3566 element = Feld[x][y];
3568 if (IS_INACTIVE(element))
3571 if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
3575 if (IS_GEM(element))
3576 EdelsteinFunkeln(x, y);
3578 else if (IS_MOVING(x, y))
3579 ContinueMoving(x, y);
3580 else if (element == EL_DYNAMIT || element == EL_DYNABOMB)
3581 CheckDynamite(x, y);
3582 else if (element == EL_EXPLODING)
3583 Explode(x, y, Frame[x][y], EX_NORMAL);
3584 else if (element == EL_AMOEBING)
3585 AmoebeWaechst(x, y);
3586 else if (IS_AMOEBALIVE(element))
3587 AmoebeAbleger(x, y);
3588 else if (element == EL_LIFE || element == EL_LIFE_ASYNC)
3590 else if (element == EL_ABLENK_EIN)
3592 else if (element == EL_SALZSAEURE)
3594 else if (element == EL_BLURB_LEFT || element == EL_BLURB_RIGHT)
3596 else if (element == EL_CRACKINGNUT)
3598 else if (element == EL_AUSGANG_ZU)
3599 AusgangstuerPruefen(x, y);
3600 else if (element == EL_AUSGANG_ACT)
3601 AusgangstuerOeffnen(x, y);
3602 else if (element == EL_AUSGANG_AUF)
3603 AusgangstuerBlinken(x, y);
3604 else if (element == EL_MAUERND)
3606 else if (element == EL_MAUER_LEBT ||
3607 element == EL_MAUER_X ||
3608 element == EL_MAUER_Y ||
3609 element == EL_MAUER_XY)
3611 else if (element == EL_BURNING)
3612 CheckForDragon(x, y);
3613 else if (element == EL_SP_BUG || element == EL_SP_BUG_ACTIVE)
3614 CheckBuggyBase(x, y);
3615 else if (element == EL_SP_TERMINAL)
3616 DrawGraphicAnimation(x, y, GFX2_SP_TERMINAL, 7, 12, ANIM_NORMAL);
3617 else if (element == EL_SP_TERMINAL_ACTIVE)
3618 DrawGraphicAnimation(x, y, GFX2_SP_TERMINAL_ACTIVE, 7, 4, ANIM_NORMAL);
3620 if (game.magic_wall_active)
3622 boolean sieb = FALSE;
3623 int jx = local_player->jx, jy = local_player->jy;
3625 if (element == EL_SIEB_LEER || element == EL_SIEB_VOLL ||
3626 Store[x][y] == EL_SIEB_LEER)
3628 SiebAktivieren(x, y, 1);
3631 else if (element == EL_SIEB2_LEER || element == EL_SIEB2_VOLL ||
3632 Store[x][y] == EL_SIEB2_LEER)
3634 SiebAktivieren(x, y, 2);
3638 /* play the element sound at the position nearest to the player */
3639 if (sieb && ABS(x-jx)+ABS(y-jy) < ABS(sieb_x-jx)+ABS(sieb_y-jy))
3647 if (game.magic_wall_active)
3649 if (!(game.magic_wall_time_left % 4))
3650 PlaySoundLevel(sieb_x, sieb_y, SND_MIEP);
3652 if (game.magic_wall_time_left > 0)
3654 game.magic_wall_time_left--;
3655 if (!game.magic_wall_time_left)
3657 for (y=0; y<lev_fieldy; y++) for (x=0; x<lev_fieldx; x++)
3659 element = Feld[x][y];
3660 if (element == EL_SIEB_LEER || element == EL_SIEB_VOLL)
3662 Feld[x][y] = EL_SIEB_TOT;
3663 DrawLevelField(x, y);
3665 else if (element == EL_SIEB2_LEER || element == EL_SIEB2_VOLL)
3667 Feld[x][y] = EL_SIEB2_TOT;
3668 DrawLevelField(x, y);
3672 game.magic_wall_active = FALSE;
3677 if (TimeFrames >= (1000 / GameFrameDelay) && !tape.pausing)
3682 if (tape.recording || tape.playing)
3683 DrawVideoDisplay(VIDEO_STATE_TIME_ON, TimePlayed);
3690 PlaySoundStereo(SND_GONG, PSND_MAX_RIGHT);
3692 DrawText(DX_TIME, DY_TIME, int2str(TimeLeft, 3), FS_SMALL, FC_YELLOW);
3695 for (i=0; i<MAX_PLAYERS; i++)
3696 KillHero(&stored_player[i]);
3698 else if (level.time == 0) /* level without time limit */
3699 DrawText(DX_TIME, DY_TIME, int2str(TimePlayed, 3), FS_SMALL, FC_YELLOW);
3705 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
3707 int min_x = x, min_y = y, max_x = x, max_y = y;
3710 for (i=0; i<MAX_PLAYERS; i++)
3712 int jx = stored_player[i].jx, jy = stored_player[i].jy;
3714 if (!stored_player[i].active || &stored_player[i] == player)
3717 min_x = MIN(min_x, jx);
3718 min_y = MIN(min_y, jy);
3719 max_x = MAX(max_x, jx);
3720 max_y = MAX(max_y, jy);
3723 return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
3726 static boolean AllPlayersInVisibleScreen()
3730 for (i=0; i<MAX_PLAYERS; i++)
3732 int jx = stored_player[i].jx, jy = stored_player[i].jy;
3734 if (!stored_player[i].active)
3737 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
3744 void ScrollLevel(int dx, int dy)
3746 int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
3749 XCopyArea(display, drawto_field, drawto_field, gc,
3750 FX + TILEX*(dx == -1) - softscroll_offset,
3751 FY + TILEY*(dy == -1) - softscroll_offset,
3752 SXSIZE - TILEX*(dx!=0) + 2*softscroll_offset,
3753 SYSIZE - TILEY*(dy!=0) + 2*softscroll_offset,
3754 FX + TILEX*(dx == 1) - softscroll_offset,
3755 FY + TILEY*(dy == 1) - softscroll_offset);
3759 x = (dx == 1 ? BX1 : BX2);
3760 for (y=BY1; y<=BY2; y++)
3761 DrawScreenField(x, y);
3765 y = (dy == 1 ? BY1 : BY2);
3766 for (x=BX1; x<=BX2; x++)
3767 DrawScreenField(x, y);
3770 redraw_mask |= REDRAW_FIELD;
3773 static void CheckGravityMovement(struct PlayerInfo *player)
3775 if (level.gravity && !player->programmed_action)
3777 int move_dir_vertical = player->action & (MV_UP | MV_DOWN);
3778 int move_dir_horizontal = player->action & (MV_LEFT | MV_RIGHT);
3780 (player->last_move_dir & (MV_LEFT | MV_RIGHT) ?
3781 (move_dir_vertical ? move_dir_vertical : move_dir_horizontal) :
3782 (move_dir_horizontal ? move_dir_horizontal : move_dir_vertical));
3783 int jx = player->jx, jy = player->jy;
3784 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
3785 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
3786 int new_jx = jx + dx, new_jy = jy + dy;
3787 boolean field_under_player_is_free =
3788 (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
3789 boolean player_is_moving_to_valid_field =
3790 (IN_LEV_FIELD(new_jx, new_jy) &&
3791 (Feld[new_jx][new_jy] == EL_SP_BASE ||
3792 Feld[new_jx][new_jy] == EL_ERDREICH));
3794 if (field_under_player_is_free && !player_is_moving_to_valid_field)
3795 player->programmed_action = MV_DOWN;
3799 boolean MoveFigureOneStep(struct PlayerInfo *player,
3800 int dx, int dy, int real_dx, int real_dy)
3802 int jx = player->jx, jy = player->jy;
3803 int new_jx = jx+dx, new_jy = jy+dy;
3807 if (!player->active || (!dx && !dy))
3808 return MF_NO_ACTION;
3810 player->MovDir = (dx < 0 ? MV_LEFT :
3813 dy > 0 ? MV_DOWN : MV_NO_MOVING);
3815 if (!IN_LEV_FIELD(new_jx, new_jy))
3816 return MF_NO_ACTION;
3818 if (!options.network && !AllPlayersInSight(player, new_jx, new_jy))
3819 return MF_NO_ACTION;
3822 element = MovingOrBlocked2Element(new_jx, new_jy);
3824 element = MovingOrBlocked2ElementIfNotLeaving(new_jx, new_jy);
3827 if (DONT_GO_TO(element))
3829 if (element == EL_SALZSAEURE && dx == 0 && dy == 1)
3832 Feld[jx][jy] = EL_SPIELFIGUR;
3833 InitMovingField(jx, jy, MV_DOWN);
3834 Store[jx][jy] = EL_SALZSAEURE;
3835 ContinueMoving(jx, jy);
3844 can_move = DigField(player, new_jx, new_jy, real_dx, real_dy, DF_DIG);
3845 if (can_move != MF_MOVING)
3848 StorePlayer[jx][jy] = 0;
3849 player->last_jx = jx;
3850 player->last_jy = jy;
3851 jx = player->jx = new_jx;
3852 jy = player->jy = new_jy;
3853 StorePlayer[jx][jy] = player->element_nr;
3856 (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
3858 ScrollFigure(player, SCROLL_INIT);
3863 boolean MoveFigure(struct PlayerInfo *player, int dx, int dy)
3865 int jx = player->jx, jy = player->jy;
3866 int old_jx = jx, old_jy = jy;
3867 int moved = MF_NO_ACTION;
3869 if (!player->active || (!dx && !dy))
3872 if (!FrameReached(&player->move_delay, player->move_delay_value) &&
3876 /* remove the last programmed player action */
3877 player->programmed_action = 0;
3881 /* should only happen if pre-1.2 tape recordings are played */
3882 /* this is only for backward compatibility */
3884 int original_move_delay_value = player->move_delay_value;
3887 printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES.\n");
3890 /* scroll remaining steps with finest movement resolution */
3891 player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
3893 while (player->MovPos)
3895 ScrollFigure(player, SCROLL_GO_ON);
3896 ScrollScreen(NULL, SCROLL_GO_ON);
3902 player->move_delay_value = original_move_delay_value;
3905 if (player->last_move_dir & (MV_LEFT | MV_RIGHT))
3907 if (!(moved |= MoveFigureOneStep(player, 0, dy, dx, dy)))
3908 moved |= MoveFigureOneStep(player, dx, 0, dx, dy);
3912 if (!(moved |= MoveFigureOneStep(player, dx, 0, dx, dy)))
3913 moved |= MoveFigureOneStep(player, 0, dy, dx, dy);
3919 if (moved & MF_MOVING && !ScreenMovPos &&
3920 (player == local_player || !options.network))
3922 int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
3923 int offset = (setup.scroll_delay ? 3 : 0);
3925 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
3927 /* actual player has left the screen -- scroll in that direction */
3928 if (jx != old_jx) /* player has moved horizontally */
3929 scroll_x += (jx - old_jx);
3930 else /* player has moved vertically */
3931 scroll_y += (jy - old_jy);
3935 if (jx != old_jx) /* player has moved horizontally */
3937 if ((player->MovDir == MV_LEFT && scroll_x > jx - MIDPOSX + offset) ||
3938 (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
3939 scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
3941 /* don't scroll over playfield boundaries */
3942 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
3943 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
3945 /* don't scroll more than one field at a time */
3946 scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
3948 /* don't scroll against the player's moving direction */
3949 if ((player->MovDir == MV_LEFT && scroll_x > old_scroll_x) ||
3950 (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
3951 scroll_x = old_scroll_x;
3953 else /* player has moved vertically */
3955 if ((player->MovDir == MV_UP && scroll_y > jy - MIDPOSY + offset) ||
3956 (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
3957 scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
3959 /* don't scroll over playfield boundaries */
3960 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
3961 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
3963 /* don't scroll more than one field at a time */
3964 scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
3966 /* don't scroll against the player's moving direction */
3967 if ((player->MovDir == MV_UP && scroll_y > old_scroll_y) ||
3968 (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
3969 scroll_y = old_scroll_y;
3973 if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
3975 if (!options.network && !AllPlayersInVisibleScreen())
3977 scroll_x = old_scroll_x;
3978 scroll_y = old_scroll_y;
3982 ScrollScreen(player, SCROLL_INIT);
3983 ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
3988 if (!(moved & MF_MOVING) && !player->Pushing)
3991 player->Frame = (player->Frame + 1) % 4;
3993 if (moved & MF_MOVING)
3995 if (old_jx != jx && old_jy == jy)
3996 player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
3997 else if (old_jx == jx && old_jy != jy)
3998 player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
4000 DrawLevelField(jx, jy); /* for "ErdreichAnbroeckeln()" */
4002 player->last_move_dir = player->MovDir;
4006 CheckGravityMovement(player);
4008 player->last_move_dir = MV_NO_MOVING;
4011 TestIfHeroHitsBadThing(jx, jy);
4013 if (!player->active)
4019 void ScrollFigure(struct PlayerInfo *player, int mode)
4021 int jx = player->jx, jy = player->jy;
4022 int last_jx = player->last_jx, last_jy = player->last_jy;
4023 int move_stepsize = TILEX / player->move_delay_value;
4025 if (!player->active || !player->MovPos)
4028 if (mode == SCROLL_INIT)
4030 player->actual_frame_counter = FrameCounter;
4031 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
4033 if (Feld[last_jx][last_jy] == EL_LEERRAUM)
4034 Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
4039 else if (!FrameReached(&player->actual_frame_counter, 1))
4042 player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
4043 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
4045 if (Feld[last_jx][last_jy] == EL_PLAYER_IS_LEAVING)
4046 Feld[last_jx][last_jy] = EL_LEERRAUM;
4048 /* before DrawPlayer() to draw correct player graphic for this case */
4049 if (player->MovPos == 0)
4050 CheckGravityMovement(player);
4054 if (player->MovPos == 0)
4056 if (IS_QUICK_GATE(Feld[last_jx][last_jy]))
4058 /* continue with normal speed after quickly moving through gate */
4059 HALVE_PLAYER_SPEED(player);
4061 /* be able to make the next move without delay */
4062 player->move_delay = 0;
4065 player->last_jx = jx;
4066 player->last_jy = jy;
4068 if (Feld[jx][jy] == EL_AUSGANG_AUF)
4072 if (!local_player->friends_still_needed)
4073 player->LevelSolved = player->GameOver = TRUE;
4078 void ScrollScreen(struct PlayerInfo *player, int mode)
4080 static unsigned long screen_frame_counter = 0;
4082 if (mode == SCROLL_INIT)
4084 /* set scrolling step size according to actual player's moving speed */
4085 ScrollStepSize = TILEX / player->move_delay_value;
4087 screen_frame_counter = FrameCounter;
4088 ScreenMovDir = player->MovDir;
4089 ScreenMovPos = player->MovPos;
4090 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
4093 else if (!FrameReached(&screen_frame_counter, 1))
4098 ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
4099 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
4100 redraw_mask |= REDRAW_FIELD;
4103 ScreenMovDir = MV_NO_MOVING;
4106 void TestIfGoodThingHitsBadThing(int goodx, int goody)
4108 int i, killx = goodx, killy = goody;
4109 static int xy[4][2] =
4116 static int harmless[4] =
4128 x = goodx + xy[i][0];
4129 y = goody + xy[i][1];
4130 if (!IN_LEV_FIELD(x, y))
4134 element = Feld[x][y];
4136 element = MovingOrBlocked2ElementIfNotLeaving(x, y);
4139 if (DONT_TOUCH(element))
4141 if (MovDir[x][y] == harmless[i])
4150 if (killx != goodx || killy != goody)
4152 if (IS_PLAYER(goodx, goody))
4153 KillHero(PLAYERINFO(goodx, goody));
4159 void TestIfBadThingHitsGoodThing(int badx, int bady)
4161 int i, killx = badx, killy = bady;
4162 static int xy[4][2] =
4169 static int harmless[4] =
4181 x = badx + xy[i][0];
4182 y = bady + xy[i][1];
4183 if (!IN_LEV_FIELD(x, y))
4186 element = Feld[x][y];
4188 if (IS_PLAYER(x, y))
4194 else if (element == EL_PINGUIN)
4196 if (MovDir[x][y] == harmless[i] && IS_MOVING(x, y))
4205 if (killx != badx || killy != bady)
4207 if (IS_PLAYER(killx, killy))
4208 KillHero(PLAYERINFO(killx, killy));
4214 void TestIfHeroHitsBadThing(int x, int y)
4216 TestIfGoodThingHitsBadThing(x, y);
4219 void TestIfBadThingHitsHero(int x, int y)
4221 TestIfBadThingHitsGoodThing(x, y);
4224 void TestIfFriendHitsBadThing(int x, int y)
4226 TestIfGoodThingHitsBadThing(x, y);
4229 void TestIfBadThingHitsFriend(int x, int y)
4231 TestIfBadThingHitsGoodThing(x, y);
4234 void TestIfBadThingHitsOtherBadThing(int badx, int bady)
4236 int i, killx = badx, killy = bady;
4237 static int xy[4][2] =
4251 if (!IN_LEV_FIELD(x, y))
4254 element = Feld[x][y];
4255 if (IS_AMOEBOID(element) || element == EL_LIFE ||
4256 element == EL_AMOEBING || element == EL_TROPFEN)
4264 if (killx != badx || killy != bady)
4268 void KillHero(struct PlayerInfo *player)
4270 int jx = player->jx, jy = player->jy;
4272 if (!player->active)
4275 if (IS_PFORTE(Feld[jx][jy]))
4276 Feld[jx][jy] = EL_LEERRAUM;
4282 void BuryHero(struct PlayerInfo *player)
4284 int jx = player->jx, jy = player->jy;
4286 if (!player->active)
4289 PlaySoundLevel(jx, jy, SND_AUTSCH);
4290 PlaySoundLevel(jx, jy, SND_LACHEN);
4292 player->GameOver = TRUE;
4296 void RemoveHero(struct PlayerInfo *player)
4298 int jx = player->jx, jy = player->jy;
4299 int i, found = FALSE;
4301 player->present = FALSE;
4302 player->active = FALSE;
4304 StorePlayer[jx][jy] = 0;
4306 for (i=0; i<MAX_PLAYERS; i++)
4307 if (stored_player[i].active)
4311 AllPlayersGone = TRUE;
4317 int DigField(struct PlayerInfo *player,
4318 int x, int y, int real_dx, int real_dy, int mode)
4320 int jx = player->jx, jy = player->jy;
4321 int dx = x - jx, dy = y - jy;
4322 int move_direction = (dx == -1 ? MV_LEFT :
4323 dx == +1 ? MV_RIGHT :
4325 dy == +1 ? MV_DOWN : MV_NO_MOVING);
4328 if (!player->MovPos)
4329 player->Pushing = FALSE;
4331 if (mode == DF_NO_PUSH)
4333 player->push_delay = 0;
4334 return MF_NO_ACTION;
4337 if (IS_MOVING(x, y) || IS_PLAYER(x, y))
4338 return MF_NO_ACTION;
4340 element = Feld[x][y];
4345 PlaySoundLevel(x, y, SND_EMPTY);
4349 Feld[x][y] = EL_LEERRAUM;
4350 PlaySoundLevel(x, y, SND_SCHLURF);
4355 Feld[x][y] = EL_LEERRAUM;
4356 PlaySoundLevel(x, y, SND_SP_BASE);
4360 case EL_EDELSTEIN_BD:
4361 case EL_EDELSTEIN_GELB:
4362 case EL_EDELSTEIN_ROT:
4363 case EL_EDELSTEIN_LILA:
4365 case EL_SP_INFOTRON:
4367 local_player->gems_still_needed -= (element == EL_DIAMANT ? 3 : 1);
4368 if (local_player->gems_still_needed < 0)
4369 local_player->gems_still_needed = 0;
4370 RaiseScoreElement(element);
4371 DrawText(DX_EMERALDS, DY_EMERALDS,
4372 int2str(local_player->gems_still_needed, 3),
4373 FS_SMALL, FC_YELLOW);
4374 if (element == EL_SP_INFOTRON)
4375 PlaySoundLevel(x, y, SND_SP_INFOTRON);
4377 PlaySoundLevel(x, y, SND_PONG);
4382 player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
4383 PlaySoundLevel(x, y, SND_PONG);
4386 case EL_DYNAMIT_AUS:
4387 case EL_SP_DISK_RED:
4390 RaiseScoreElement(EL_DYNAMIT);
4391 DrawText(DX_DYNAMITE, DY_DYNAMITE,
4392 int2str(local_player->dynamite, 3),
4393 FS_SMALL, FC_YELLOW);
4394 if (element == EL_SP_DISK_RED)
4395 PlaySoundLevel(x, y, SND_SP_INFOTRON);
4397 PlaySoundLevel(x, y, SND_PONG);
4400 case EL_DYNABOMB_NR:
4402 player->dynabomb_count++;
4403 player->dynabombs_left++;
4404 RaiseScoreElement(EL_DYNAMIT);
4405 PlaySoundLevel(x, y, SND_PONG);
4408 case EL_DYNABOMB_SZ:
4410 player->dynabomb_size++;
4411 RaiseScoreElement(EL_DYNAMIT);
4412 PlaySoundLevel(x, y, SND_PONG);
4415 case EL_DYNABOMB_XL:
4417 player->dynabomb_xl = TRUE;
4418 RaiseScoreElement(EL_DYNAMIT);
4419 PlaySoundLevel(x, y, SND_PONG);
4422 case EL_SCHLUESSEL1:
4423 case EL_SCHLUESSEL2:
4424 case EL_SCHLUESSEL3:
4425 case EL_SCHLUESSEL4:
4427 int key_nr = element - EL_SCHLUESSEL1;
4430 player->key[key_nr] = TRUE;
4431 RaiseScoreElement(EL_SCHLUESSEL);
4432 DrawMiniGraphicExt(drawto, gc,
4433 DX_KEYS+key_nr*MINI_TILEX, DY_KEYS,
4434 GFX_SCHLUESSEL1+key_nr);
4435 DrawMiniGraphicExt(window, gc,
4436 DX_KEYS+key_nr*MINI_TILEX, DY_KEYS,
4437 GFX_SCHLUESSEL1+key_nr);
4438 PlaySoundLevel(x, y, SND_PONG);
4447 int key_nr = element - EL_EM_KEY_1;
4450 player->key[key_nr] = TRUE;
4451 RaiseScoreElement(EL_SCHLUESSEL);
4452 DrawMiniGraphicExt(drawto, gc,
4453 DX_KEYS+key_nr*MINI_TILEX, DY_KEYS,
4454 GFX_SCHLUESSEL1+key_nr);
4455 DrawMiniGraphicExt(window, gc,
4456 DX_KEYS+key_nr*MINI_TILEX, DY_KEYS,
4457 GFX_SCHLUESSEL1+key_nr);
4458 PlaySoundLevel(x, y, SND_PONG);
4463 Feld[x][y] = EL_ABLENK_EIN;
4466 DrawLevelField(x, y);
4470 case EL_SP_TERMINAL:
4474 for (yy=0; yy<lev_fieldy; yy++)
4476 for (xx=0; xx<lev_fieldx; xx++)
4478 if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
4480 else if (Feld[xx][yy] == EL_SP_TERMINAL)
4481 Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
4490 if (local_player->gems_still_needed > 0)
4491 return MF_NO_ACTION;
4493 player->LevelSolved = player->GameOver = TRUE;
4494 PlaySoundStereo(SND_SP_EXIT, PSND_MAX_RIGHT);
4497 case EL_FELSBROCKEN:
4502 case EL_SP_DISK_ORANGE:
4503 if (dy || mode == DF_SNAP)
4504 return MF_NO_ACTION;
4506 player->Pushing = TRUE;
4508 if (!IN_LEV_FIELD(x+dx, y+dy) || !IS_FREE(x+dx, y+dy))
4509 return MF_NO_ACTION;
4513 if (IN_LEV_FIELD(jx, jy+real_dy) && !IS_SOLID(Feld[jx][jy+real_dy]))
4514 return MF_NO_ACTION;
4517 if (player->push_delay == 0)
4518 player->push_delay = FrameCounter;
4519 if (!FrameReached(&player->push_delay, player->push_delay_value) &&
4521 return MF_NO_ACTION;
4524 Feld[x+dx][y+dy] = element;
4526 player->push_delay_value = 2+RND(8);
4528 DrawLevelField(x+dx, y+dy);
4529 if (element == EL_FELSBROCKEN)
4530 PlaySoundLevel(x+dx, y+dy, SND_PUSCH);
4531 else if (element == EL_KOKOSNUSS)
4532 PlaySoundLevel(x+dx, y+dy, SND_KNURK);
4533 else if (IS_SP_ELEMENT(element))
4534 PlaySoundLevel(x+dx, y+dy, SND_SP_ZONKPUSH);
4536 PlaySoundLevel(x+dx, y+dy, SND_KLOPF);
4543 if (!player->key[element - EL_PFORTE1])
4544 return MF_NO_ACTION;
4551 if (!player->key[element - EL_PFORTE1X])
4552 return MF_NO_ACTION;
4559 if (!player->key[element - EL_EM_GATE_1])
4560 return MF_NO_ACTION;
4561 if (!IN_LEV_FIELD(x + dx, y + dy) || !IS_FREE(x + dx, y + dy))
4562 return MF_NO_ACTION;
4564 /* automatically move to the next field with double speed */
4565 player->programmed_action = move_direction;
4566 DOUBLE_PLAYER_SPEED(player);
4574 if (!player->key[element - EL_EM_GATE_1X])
4575 return MF_NO_ACTION;
4576 if (!IN_LEV_FIELD(x + dx, y + dy) || !IS_FREE(x + dx, y + dy))
4577 return MF_NO_ACTION;
4579 /* automatically move to the next field with double speed */
4580 player->programmed_action = move_direction;
4581 DOUBLE_PLAYER_SPEED(player);
4585 case EL_SP_PORT1_LEFT:
4586 case EL_SP_PORT2_LEFT:
4587 case EL_SP_PORT1_RIGHT:
4588 case EL_SP_PORT2_RIGHT:
4589 case EL_SP_PORT1_UP:
4590 case EL_SP_PORT2_UP:
4591 case EL_SP_PORT1_DOWN:
4592 case EL_SP_PORT2_DOWN:
4597 element != EL_SP_PORT1_LEFT &&
4598 element != EL_SP_PORT2_LEFT &&
4599 element != EL_SP_PORT_X &&
4600 element != EL_SP_PORT_XY) ||
4602 element != EL_SP_PORT1_RIGHT &&
4603 element != EL_SP_PORT2_RIGHT &&
4604 element != EL_SP_PORT_X &&
4605 element != EL_SP_PORT_XY) ||
4607 element != EL_SP_PORT1_UP &&
4608 element != EL_SP_PORT2_UP &&
4609 element != EL_SP_PORT_Y &&
4610 element != EL_SP_PORT_XY) ||
4612 element != EL_SP_PORT1_DOWN &&
4613 element != EL_SP_PORT2_DOWN &&
4614 element != EL_SP_PORT_Y &&
4615 element != EL_SP_PORT_XY) ||
4616 !IN_LEV_FIELD(x + dx, y + dy) ||
4617 !IS_FREE(x + dx, y + dy))
4618 return MF_NO_ACTION;
4620 /* automatically move to the next field with double speed */
4621 player->programmed_action = move_direction;
4622 DOUBLE_PLAYER_SPEED(player);
4627 case EL_AUSGANG_ACT:
4628 /* door is not (yet) open */
4629 return MF_NO_ACTION;
4632 case EL_AUSGANG_AUF:
4633 if (mode == DF_SNAP)
4634 return MF_NO_ACTION;
4636 PlaySoundLevel(x, y, SND_BUING);
4641 Feld[x][y] = EL_BIRNE_EIN;
4642 local_player->lights_still_needed--;
4643 DrawLevelField(x, y);
4644 PlaySoundLevel(x, y, SND_DENG);
4649 Feld[x][y] = EL_ZEIT_LEER;
4651 DrawText(DX_TIME, DY_TIME, int2str(TimeLeft, 3), FS_SMALL, FC_YELLOW);
4652 DrawLevelField(x, y);
4653 PlaySoundStereo(SND_GONG, PSND_MAX_RIGHT);
4657 case EL_SOKOBAN_FELD_LEER:
4660 case EL_SOKOBAN_FELD_VOLL:
4661 case EL_SOKOBAN_OBJEKT:
4663 case EL_SP_DISK_YELLOW:
4664 if (mode == DF_SNAP)
4665 return MF_NO_ACTION;
4667 player->Pushing = TRUE;
4669 if (!IN_LEV_FIELD(x+dx, y+dy)
4670 || (!IS_FREE(x+dx, y+dy)
4671 && (Feld[x+dx][y+dy] != EL_SOKOBAN_FELD_LEER
4672 || !IS_SB_ELEMENT(element))))
4673 return MF_NO_ACTION;
4677 if (IN_LEV_FIELD(jx, jy+real_dy) && !IS_SOLID(Feld[jx][jy+real_dy]))
4678 return MF_NO_ACTION;
4680 else if (dy && real_dx)
4682 if (IN_LEV_FIELD(jx+real_dx, jy) && !IS_SOLID(Feld[jx+real_dx][jy]))
4683 return MF_NO_ACTION;
4686 if (player->push_delay == 0)
4687 player->push_delay = FrameCounter;
4688 if (!FrameReached(&player->push_delay, player->push_delay_value) &&
4690 return MF_NO_ACTION;
4692 if (IS_SB_ELEMENT(element))
4694 if (element == EL_SOKOBAN_FELD_VOLL)
4696 Feld[x][y] = EL_SOKOBAN_FELD_LEER;
4697 local_player->sokobanfields_still_needed++;
4702 if (Feld[x+dx][y+dy] == EL_SOKOBAN_FELD_LEER)
4704 Feld[x+dx][y+dy] = EL_SOKOBAN_FELD_VOLL;
4705 local_player->sokobanfields_still_needed--;
4706 if (element == EL_SOKOBAN_OBJEKT)
4707 PlaySoundLevel(x, y, SND_DENG);
4710 Feld[x+dx][y+dy] = EL_SOKOBAN_OBJEKT;
4715 Feld[x+dx][y+dy] = element;
4718 player->push_delay_value = 2;
4720 DrawLevelField(x, y);
4721 DrawLevelField(x+dx, y+dy);
4722 PlaySoundLevel(x+dx, y+dy, SND_PUSCH);
4724 if (IS_SB_ELEMENT(element) &&
4725 local_player->sokobanfields_still_needed == 0 &&
4726 game.emulation == EMU_SOKOBAN)
4728 player->LevelSolved = player->GameOver = TRUE;
4729 PlaySoundLevel(x, y, SND_BUING);
4741 return MF_NO_ACTION;
4744 player->push_delay = 0;
4749 boolean SnapField(struct PlayerInfo *player, int dx, int dy)
4751 int jx = player->jx, jy = player->jy;
4752 int x = jx + dx, y = jy + dy;
4754 if (!player->active || !IN_LEV_FIELD(x, y))
4762 player->snapped = FALSE;
4766 if (player->snapped)
4769 player->MovDir = (dx < 0 ? MV_LEFT :
4772 dy > 0 ? MV_DOWN : MV_NO_MOVING);
4774 if (!DigField(player, x, y, 0, 0, DF_SNAP))
4777 player->snapped = TRUE;
4778 DrawLevelField(x, y);
4784 boolean PlaceBomb(struct PlayerInfo *player)
4786 int jx = player->jx, jy = player->jy;
4789 if (!player->active || player->MovPos)
4792 element = Feld[jx][jy];
4794 if ((player->dynamite == 0 && player->dynabombs_left == 0) ||
4795 element == EL_DYNAMIT || element == EL_DYNABOMB ||
4796 element == EL_EXPLODING)
4799 if (element != EL_LEERRAUM)
4800 Store[jx][jy] = element;
4802 if (player->dynamite)
4804 Feld[jx][jy] = EL_DYNAMIT;
4805 MovDelay[jx][jy] = 96;
4807 DrawText(DX_DYNAMITE, DY_DYNAMITE, int2str(local_player->dynamite, 3),
4808 FS_SMALL, FC_YELLOW);
4809 if (IN_SCR_FIELD(SCREENX(jx), SCREENY(jy)))
4811 if (game.emulation == EMU_SUPAPLEX)
4812 DrawGraphic(SCREENX(jx), SCREENY(jy), GFX_SP_DISK_RED);
4814 DrawGraphicThruMask(SCREENX(jx), SCREENY(jy), GFX_DYNAMIT);
4819 Feld[jx][jy] = EL_DYNABOMB;
4820 Store2[jx][jy] = player->element_nr; /* for DynaExplode() */
4821 MovDelay[jx][jy] = 96;
4822 player->dynabombs_left--;
4823 if (IN_SCR_FIELD(SCREENX(jx), SCREENY(jy)))
4824 DrawGraphicThruMask(SCREENX(jx), SCREENY(jy), GFX_DYNABOMB);
4830 void PlaySoundLevel(int x, int y, int sound_nr)
4832 int sx = SCREENX(x), sy = SCREENY(y);
4834 int silence_distance = 8;
4836 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound_nr)) ||
4837 (!setup.sound_loops && IS_LOOP_SOUND(sound_nr)))
4840 if (!IN_LEV_FIELD(x, y) ||
4841 sx < -silence_distance || sx >= SCR_FIELDX+silence_distance ||
4842 sy < -silence_distance || sy >= SCR_FIELDY+silence_distance)
4845 volume = PSND_MAX_VOLUME;
4848 stereo = (sx - SCR_FIELDX/2) * 12;
4850 stereo = PSND_MIDDLE + (2 * sx - (SCR_FIELDX - 1)) * 5;
4851 if (stereo > PSND_MAX_RIGHT)
4852 stereo = PSND_MAX_RIGHT;
4853 if (stereo < PSND_MAX_LEFT)
4854 stereo = PSND_MAX_LEFT;
4857 if (!IN_SCR_FIELD(sx, sy))
4859 int dx = ABS(sx - SCR_FIELDX/2) - SCR_FIELDX/2;
4860 int dy = ABS(sy - SCR_FIELDY/2) - SCR_FIELDY/2;
4862 volume -= volume * (dx > dy ? dx : dy) / silence_distance;
4865 PlaySoundExt(sound_nr, volume, stereo, PSND_NO_LOOP);
4868 void RaiseScore(int value)
4870 local_player->score += value;
4871 DrawText(DX_SCORE, DY_SCORE, int2str(local_player->score, 5),
4872 FS_SMALL, FC_YELLOW);
4875 void RaiseScoreElement(int element)
4880 case EL_EDELSTEIN_BD:
4881 case EL_EDELSTEIN_GELB:
4882 case EL_EDELSTEIN_ROT:
4883 case EL_EDELSTEIN_LILA:
4884 RaiseScore(level.score[SC_EDELSTEIN]);
4887 RaiseScore(level.score[SC_DIAMANT]);
4891 RaiseScore(level.score[SC_KAEFER]);
4895 RaiseScore(level.score[SC_FLIEGER]);
4899 RaiseScore(level.score[SC_MAMPFER]);
4902 RaiseScore(level.score[SC_ROBOT]);
4905 RaiseScore(level.score[SC_PACMAN]);
4908 RaiseScore(level.score[SC_KOKOSNUSS]);
4911 RaiseScore(level.score[SC_DYNAMIT]);
4914 RaiseScore(level.score[SC_SCHLUESSEL]);
4921 /* ---------- new game button stuff ---------------------------------------- */
4923 /* graphic position values for game buttons */
4924 #define GAME_BUTTON_XSIZE 30
4925 #define GAME_BUTTON_YSIZE 30
4926 #define GAME_BUTTON_XPOS 5
4927 #define GAME_BUTTON_YPOS 215
4928 #define SOUND_BUTTON_XPOS 5
4929 #define SOUND_BUTTON_YPOS (GAME_BUTTON_YPOS + GAME_BUTTON_YSIZE)
4931 #define GAME_BUTTON_STOP_XPOS (GAME_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
4932 #define GAME_BUTTON_PAUSE_XPOS (GAME_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
4933 #define GAME_BUTTON_PLAY_XPOS (GAME_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
4934 #define SOUND_BUTTON_MUSIC_XPOS (SOUND_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
4935 #define SOUND_BUTTON_LOOPS_XPOS (SOUND_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
4936 #define SOUND_BUTTON_SIMPLE_XPOS (SOUND_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
4943 } gamebutton_info[NUM_GAME_BUTTONS] =
4946 GAME_BUTTON_STOP_XPOS, GAME_BUTTON_YPOS,
4951 GAME_BUTTON_PAUSE_XPOS, GAME_BUTTON_YPOS,
4956 GAME_BUTTON_PLAY_XPOS, GAME_BUTTON_YPOS,
4961 SOUND_BUTTON_MUSIC_XPOS, SOUND_BUTTON_YPOS,
4962 SOUND_CTRL_ID_MUSIC,
4963 "background music on/off"
4966 SOUND_BUTTON_LOOPS_XPOS, SOUND_BUTTON_YPOS,
4967 SOUND_CTRL_ID_LOOPS,
4968 "sound loops on/off"
4971 SOUND_BUTTON_SIMPLE_XPOS, SOUND_BUTTON_YPOS,
4972 SOUND_CTRL_ID_SIMPLE,
4973 "normal sounds on/off"
4977 void CreateGameButtons()
4981 for (i=0; i<NUM_GAME_BUTTONS; i++)
4983 Pixmap gd_pixmap = pix[PIX_DOOR];
4984 struct GadgetInfo *gi;
4987 unsigned long event_mask;
4988 int gd_xoffset, gd_yoffset;
4989 int gd_x1, gd_x2, gd_y1, gd_y2;
4992 gd_xoffset = gamebutton_info[i].x;
4993 gd_yoffset = gamebutton_info[i].y;
4994 gd_x1 = DOOR_GFX_PAGEX4 + gd_xoffset;
4995 gd_x2 = DOOR_GFX_PAGEX3 + gd_xoffset;
4997 if (id == GAME_CTRL_ID_STOP ||
4998 id == GAME_CTRL_ID_PAUSE ||
4999 id == GAME_CTRL_ID_PLAY)
5001 button_type = GD_TYPE_NORMAL_BUTTON;
5003 event_mask = GD_EVENT_RELEASED;
5004 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
5005 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
5009 button_type = GD_TYPE_CHECK_BUTTON;
5011 ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
5012 (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
5013 (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
5014 event_mask = GD_EVENT_PRESSED;
5015 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset;
5016 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
5019 gi = CreateGadget(GDI_CUSTOM_ID, id,
5020 GDI_INFO_TEXT, gamebutton_info[i].infotext,
5021 GDI_X, DX + gd_xoffset,
5022 GDI_Y, DY + gd_yoffset,
5023 GDI_WIDTH, GAME_BUTTON_XSIZE,
5024 GDI_HEIGHT, GAME_BUTTON_YSIZE,
5025 GDI_TYPE, button_type,
5026 GDI_STATE, GD_BUTTON_UNPRESSED,
5027 GDI_CHECKED, checked,
5028 GDI_DESIGN_UNPRESSED, gd_pixmap, gd_x1, gd_y1,
5029 GDI_DESIGN_PRESSED, gd_pixmap, gd_x2, gd_y1,
5030 GDI_ALT_DESIGN_UNPRESSED, gd_pixmap, gd_x1, gd_y2,
5031 GDI_ALT_DESIGN_PRESSED, gd_pixmap, gd_x2, gd_y2,
5032 GDI_EVENT_MASK, event_mask,
5033 GDI_CALLBACK_ACTION, HandleGameButtons,
5037 Error(ERR_EXIT, "cannot create gadget");
5039 game_gadget[id] = gi;
5043 static void MapGameButtons()
5047 for (i=0; i<NUM_GAME_BUTTONS; i++)
5048 MapGadget(game_gadget[i]);
5051 void UnmapGameButtons()
5055 for (i=0; i<NUM_GAME_BUTTONS; i++)
5056 UnmapGadget(game_gadget[i]);
5059 static void HandleGameButtons(struct GadgetInfo *gi)
5061 int id = gi->custom_id;
5063 if (game_status != PLAYING)
5068 case GAME_CTRL_ID_STOP:
5071 CloseDoor(DOOR_CLOSE_1);
5072 game_status = MAINMENU;
5077 if (level_editor_test_game ||
5078 Request("Do you really want to quit the game ?",
5079 REQ_ASK | REQ_STAY_CLOSED))
5082 if (options.network)
5083 SendToServer_StopPlaying();
5087 game_status = MAINMENU;
5092 OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
5095 case GAME_CTRL_ID_PAUSE:
5096 if (options.network)
5100 SendToServer_ContinuePlaying();
5102 SendToServer_PausePlaying();
5109 case GAME_CTRL_ID_PLAY:
5113 if (options.network)
5114 SendToServer_ContinuePlaying();
5118 tape.pausing = FALSE;
5119 DrawVideoDisplay(VIDEO_STATE_PAUSE_OFF,0);
5124 case SOUND_CTRL_ID_MUSIC:
5125 if (setup.sound_music)
5127 setup.sound_music = FALSE;
5128 FadeSound(background_loop[level_nr % num_bg_loops]);
5130 else if (sound_loops_allowed)
5132 setup.sound = setup.sound_music = TRUE;
5133 PlaySoundLoop(background_loop[level_nr % num_bg_loops]);
5137 case SOUND_CTRL_ID_LOOPS:
5138 if (setup.sound_loops)
5139 setup.sound_loops = FALSE;
5140 else if (sound_loops_allowed)
5141 setup.sound = setup.sound_loops = TRUE;
5144 case SOUND_CTRL_ID_SIMPLE:
5145 if (setup.sound_simple)
5146 setup.sound_simple = FALSE;
5147 else if (sound_status==SOUND_AVAILABLE)
5148 setup.sound = setup.sound_simple = TRUE;