X-Git-Url: https://git.artsoft.org/?a=blobdiff_plain;f=src%2Fgame.c;h=ec95ff6dcf71d38bed26507ac7ee088a04a5e998;hb=95d786b6fe8474c30a22f839751e0d04c7682ded;hp=a48ad78bad3944432795406b1fae03677445dbe4;hpb=3dc317d10b44cc6b75db10ac194966ad8114d390;p=rocksndiamonds.git diff --git a/src/game.c b/src/game.c index a48ad78b..ec95ff6d 100644 --- a/src/game.c +++ b/src/game.c @@ -23,13 +23,95 @@ #include "joystick.h" #include "network.h" +/* for DigField() */ +#define DF_NO_PUSH 0 +#define DF_DIG 1 +#define DF_SNAP 2 + +/* for MoveFigure() */ +#define MF_NO_ACTION 0 +#define MF_MOVING 1 +#define MF_ACTION 2 + +/* for ScrollFigure() */ +#define SCROLL_INIT 0 +#define SCROLL_GO_ON 1 + +/* for Explode() */ +#define EX_PHASE_START 0 +#define EX_NORMAL 0 +#define EX_CENTER 1 +#define EX_BORDER 2 + +/* special positions in the game control window (relative to control window) */ +#define XX_LEVEL 37 +#define YY_LEVEL 20 +#define XX_EMERALDS 29 +#define YY_EMERALDS 54 +#define XX_DYNAMITE 29 +#define YY_DYNAMITE 89 +#define XX_KEYS 18 +#define YY_KEYS 123 +#define XX_SCORE 15 +#define YY_SCORE 159 +#define XX_TIME 29 +#define YY_TIME 194 + +/* special positions in the game control window (relative to main window) */ +#define DX_LEVEL (DX + XX_LEVEL) +#define DY_LEVEL (DY + YY_LEVEL) +#define DX_EMERALDS (DX + XX_EMERALDS) +#define DY_EMERALDS (DY + YY_EMERALDS) +#define DX_DYNAMITE (DX + XX_DYNAMITE) +#define DY_DYNAMITE (DY + YY_DYNAMITE) +#define DX_KEYS (DX + XX_KEYS) +#define DY_KEYS (DY + YY_KEYS) +#define DX_SCORE (DX + XX_SCORE) +#define DY_SCORE (DY + YY_SCORE) +#define DX_TIME (DX + XX_TIME) +#define DY_TIME (DY + YY_TIME) + +#define IS_LOOP_SOUND(s) ((s)==SND_KLAPPER || (s)==SND_ROEHR || \ + (s)==SND_NJAM || (s)==SND_MIEP) +#define IS_MUSIC_SOUND(s) ((s)==SND_ALCHEMY || (s)==SND_CHASE || \ + (s)==SND_NETWORK || (s)==SND_CZARDASZ || \ + (s)==SND_TYGER || (s)==SND_VOYAGER || \ + (s)==SND_TWILIGHT) + +/* score for elements */ +#define SC_EDELSTEIN 0 +#define SC_DIAMANT 1 +#define SC_KAEFER 2 +#define SC_FLIEGER 3 +#define SC_MAMPFER 4 +#define SC_ROBOT 5 +#define SC_PACMAN 6 +#define SC_KOKOSNUSS 7 +#define SC_DYNAMIT 8 +#define SC_SCHLUESSEL 9 +#define SC_ZEITBONUS 10 + +/* values for game_emulation */ +#define EMU_NONE 0 +#define EMU_BOULDERDASH 1 +#define EMU_SOKOBAN 2 + +/* to control special behaviour of certain game elements */ +int game_emulation = EMU_NONE; + + + + #ifdef DEBUG +#if 0 static unsigned int getStateCheckSum(int counter) { int x, y; unsigned int mult = 1; unsigned int checksum = 0; + /* static short lastFeld[MAX_LEV_FIELDX][MAX_LEV_FIELDY]; + */ static boolean first_game = TRUE; for (y=0; yjx, jy = player->jy; + + player->present = TRUE; + + /* + if (!network_playing || player->connected) + */ + + if (!options.network || player->connected) + { + player->active = TRUE; + + /* remove potentially duplicate players */ + if (StorePlayer[jx][jy] == Feld[x][y]) + StorePlayer[jx][jy] = 0; + + StorePlayer[x][y] = Feld[x][y]; + + if (options.verbose) + { + printf("Player %d activated.\n", player->element_nr); + printf("[Local player is %d and currently %s.]\n", + local_player->element_nr, + local_player->active ? "active" : "not active"); + } + } + + Feld[x][y] = EL_LEERRAUM; + player->jx = player->last_jx = x; + player->jy = player->last_jy = y; + } + break; + + case EL_BADEWANNE: + if (x < lev_fieldx-1 && Feld[x+1][y] == EL_SALZSAEURE) + Feld[x][y] = EL_BADEWANNE1; + else if (x > 0 && Feld[x-1][y] == EL_SALZSAEURE) + Feld[x][y] = EL_BADEWANNE2; + else if (y > 0 && Feld[x][y-1] == EL_BADEWANNE1) + Feld[x][y] = EL_BADEWANNE3; + else if (y > 0 && Feld[x][y-1] == EL_SALZSAEURE) + Feld[x][y] = EL_BADEWANNE4; + else if (y > 0 && Feld[x][y-1] == EL_BADEWANNE2) + Feld[x][y] = EL_BADEWANNE5; + break; + + case EL_KAEFER_R: + case EL_KAEFER_O: + case EL_KAEFER_L: + case EL_KAEFER_U: + case EL_KAEFER: + case EL_FLIEGER_R: + case EL_FLIEGER_O: + case EL_FLIEGER_L: + case EL_FLIEGER_U: + case EL_FLIEGER: + case EL_BUTTERFLY_R: + case EL_BUTTERFLY_O: + case EL_BUTTERFLY_L: + case EL_BUTTERFLY_U: + case EL_BUTTERFLY: + case EL_FIREFLY_R: + case EL_FIREFLY_O: + case EL_FIREFLY_L: + case EL_FIREFLY_U: + case EL_FIREFLY: + case EL_PACMAN_R: + case EL_PACMAN_O: + case EL_PACMAN_L: + case EL_PACMAN_U: + case EL_MAMPFER: + case EL_MAMPFER2: + case EL_ROBOT: + case EL_PACMAN: + InitMovDir(x, y); + break; + + case EL_AMOEBE_VOLL: + case EL_AMOEBE_BD: + InitAmoebaNr(x, y); + break; + + case EL_TROPFEN: + if (y == lev_fieldy - 1) + { + Feld[x][y] = EL_AMOEBING; + Store[x][y] = EL_AMOEBE_NASS; + } + break; + + case EL_DYNAMIT: + MovDelay[x][y] = 96; + break; + + case EL_BIRNE_AUS: + local_player->lights_still_needed++; + break; + + case EL_SOKOBAN_FELD_LEER: + local_player->sokobanfields_still_needed++; + break; + + case EL_MAULWURF: + case EL_PINGUIN: + local_player->friends_still_needed++; + break; + + case EL_SCHWEIN: + case EL_DRACHE: + MovDir[x][y] = 1 << RND(4); + break; + + default: + break; + } +} + void InitGame() { int i, j, x, y; @@ -154,9 +372,11 @@ void InitGame() network_player_action_received = FALSE; +#ifndef MSDOS /* initial null action */ if (network_playing) SendToServer_MovePlayer(MV_NO_MOVING); +#endif ZX = ZY = -1; @@ -195,120 +415,7 @@ void InitGame() if (emulate_sb && !IS_SB_ELEMENT(Feld[x][y])) emulate_sb = FALSE; - switch (Feld[x][y]) - { - case EL_SPIELFIGUR: - Feld[x][y] = EL_SPIELER1; - /* no break! */ - case EL_SPIELER1: - case EL_SPIELER2: - case EL_SPIELER3: - case EL_SPIELER4: - { - struct PlayerInfo *player = &stored_player[Feld[x][y] - EL_SPIELER1]; - int jx = player->jx, jy = player->jy; - - player->present = TRUE; - - /* - if (!network_playing || player->connected) - */ - - if (!options.network || player->connected) - { - player->active = TRUE; - - /* remove potentially duplicate players */ - if (StorePlayer[jx][jy] == Feld[x][y]) - StorePlayer[jx][jy] = 0; - - StorePlayer[x][y] = Feld[x][y]; - - printf("Player %d activated.\n", player->element_nr); - printf("[Local player is %d and currently %s.]\n", - local_player->element_nr, - local_player->active ? "active" : "not active"); - } - - Feld[x][y] = EL_LEERRAUM; - player->jx = player->last_jx = x; - player->jy = player->last_jy = y; - - break; - } - case EL_BADEWANNE: - if (x < lev_fieldx-1 && Feld[x+1][y] == EL_SALZSAEURE) - Feld[x][y] = EL_BADEWANNE1; - else if (x > 0 && Feld[x-1][y] == EL_SALZSAEURE) - Feld[x][y] = EL_BADEWANNE2; - else if (y > 0 && Feld[x][y-1] == EL_BADEWANNE1) - Feld[x][y] = EL_BADEWANNE3; - else if (y > 0 && Feld[x][y-1] == EL_SALZSAEURE) - Feld[x][y] = EL_BADEWANNE4; - else if (y > 0 && Feld[x][y-1] == EL_BADEWANNE2) - Feld[x][y] = EL_BADEWANNE5; - break; - case EL_KAEFER_R: - case EL_KAEFER_O: - case EL_KAEFER_L: - case EL_KAEFER_U: - case EL_KAEFER: - case EL_FLIEGER_R: - case EL_FLIEGER_O: - case EL_FLIEGER_L: - case EL_FLIEGER_U: - case EL_FLIEGER: - case EL_BUTTERFLY_R: - case EL_BUTTERFLY_O: - case EL_BUTTERFLY_L: - case EL_BUTTERFLY_U: - case EL_BUTTERFLY: - case EL_FIREFLY_R: - case EL_FIREFLY_O: - case EL_FIREFLY_L: - case EL_FIREFLY_U: - case EL_FIREFLY: - case EL_PACMAN_R: - case EL_PACMAN_O: - case EL_PACMAN_L: - case EL_PACMAN_U: - case EL_MAMPFER: - case EL_MAMPFER2: - case EL_ROBOT: - case EL_PACMAN: - InitMovDir(x, y); - break; - case EL_AMOEBE_VOLL: - case EL_AMOEBE_BD: - InitAmoebaNr(x, y); - break; - case EL_TROPFEN: - if (y == lev_fieldy - 1) - { - Feld[x][y] = EL_AMOEBING; - Store[x][y] = EL_AMOEBE_NASS; - } - break; - case EL_DYNAMIT: - MovDelay[x][y] = 96; - break; - case EL_BIRNE_AUS: - local_player->lights_still_needed++; - break; - case EL_SOKOBAN_FELD_LEER: - local_player->sokobanfields_still_needed++; - break; - case EL_MAULWURF: - case EL_PINGUIN: - local_player->friends_still_needed++; - break; - case EL_SCHWEIN: - case EL_DRACHE: - MovDir[x][y] = 1 << RND(4); - break; - default: - break; - } + InitField(x, y, TRUE); } /* check if any connected player was not found in playfield */ @@ -340,20 +447,38 @@ void InitGame() } } - /* when in single player mode, eliminate all but the first active player */ - if (!options.network && !setup.team_mode) + if (tape.playing) { + /* when playing a tape, eliminate all players who do not participate */ + + for (i=0; ijx, jy = player->jy; + + player->active = FALSE; + StorePlayer[jx][jy] = 0; + Feld[jx][jy] = EL_LEERRAUM; + } + } + } + else if (!options.network && !setup.team_mode) /* && !tape.playing */ + { + /* when in single player mode, eliminate all but the first active player */ + for (i=0; ijx, jy = player->jy; - - if (player->active) + if (stored_player[j].active) { + struct PlayerInfo *player = &stored_player[j]; + int jx = player->jx, jy = player->jy; + player->active = FALSE; StorePlayer[jx][jy] = 0; Feld[jx][jy] = EL_LEERRAUM; @@ -363,19 +488,29 @@ void InitGame() } } - for (i=0; ipresent, - player->connected, - player->active); - if (local_player == player) - printf("Player %d is local player.\n", i+1); + for (i=0; ipresent, + player->connected, + player->active); + if (local_player == player) + printf("Player %d is local player.\n", i+1); + } + } game_emulation = (emulate_bd ? EMU_BOULDERDASH : emulate_sb ? EMU_SOKOBAN : EMU_NONE); @@ -434,11 +569,12 @@ void InitGame() XAutoRepeatOff(display); - - for (i=0; i<4; i++) - printf("Spieler %d %saktiv.\n", - i+1, (stored_player[i].active ? "" : "nicht ")); - + if (options.verbose) + { + for (i=0; i<4; i++) + printf("Spieler %d %saktiv.\n", + i+1, (stored_player[i].active ? "" : "nicht ")); + } } void InitMovDir(int x, int y) @@ -623,15 +759,15 @@ boolean NewHiScore() LoadScore(level_nr); - if (!strcmp(setup.player_name, EMPTY_ALIAS) || - local_player->score < highscore[MAX_SCORE_ENTRIES-1].Score) + if (strcmp(setup.player_name, EMPTY_PLAYER_NAME) == 0 || + local_player->score < highscore[MAX_SCORE_ENTRIES - 1].Score) return -1; for (k=0; kscore > highscore[k].Score) { - /* Spieler kommt in Highscore-Liste */ + /* player has made it to the hall of fame */ if (k < MAX_SCORE_ENTRIES - 1) { @@ -641,7 +777,7 @@ boolean NewHiScore() for (l=k; l=TILEX) /* Zielfeld erreicht */ + if (ABS(MovPos[x][y])>=TILEX) /* object reached its destination */ { Feld[x][y] = EL_LEERRAUM; Feld[newx][newy] = element; @@ -2148,7 +2286,7 @@ void ContinueMoving(int x, int y) Stop[newx][newy] = TRUE; JustHit[x][newy] = 3; - if (DONT_TOUCH(element)) /* Käfer oder Flieger */ + if (DONT_TOUCH(element)) /* object may be nasty to player or others */ { TestIfBadThingHitsHero(newx, newy); TestIfBadThingHitsFriend(newx, newy); @@ -2161,7 +2299,7 @@ void ContinueMoving(int x, int y) (newy == lev_fieldy-1 || !IS_FREE(x, newy+1))) Impact(x, newy); } - else /* noch in Bewegung */ + else /* still moving on */ DrawLevelField(x, y); } @@ -2180,13 +2318,13 @@ int AmoebeNachbarNr(int ax, int ay) for (i=0; i<4; i++) { - int x = ax+xy[i%4][0]; - int y = ay+xy[i%4][1]; + int x = ax + xy[i][0]; + int y = ay + xy[i][1]; if (!IN_LEV_FIELD(x, y)) continue; - if (Feld[x][y] == element && AmoebaNr[x][y]>0) + if (Feld[x][y] == element && AmoebaNr[x][y] > 0) group_nr = AmoebaNr[x][y]; } @@ -2205,13 +2343,13 @@ void AmoebenVereinigen(int ax, int ay) { 0, +1 } }; - if (!new_group_nr) + if (new_group_nr == 0) return; for (i=0; i<4; i++) { - x = ax+xy[i%4][0]; - y = ay+xy[i%4][1]; + x = ax + xy[i][0]; + y = ay + xy[i][1]; if (!IN_LEV_FIELD(x, y)) continue; @@ -2223,14 +2361,22 @@ void AmoebenVereinigen(int ax, int ay) { int old_group_nr = AmoebaNr[x][y]; + if (old_group_nr == 0) + return; + AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr]; AmoebaCnt[old_group_nr] = 0; AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr]; AmoebaCnt2[old_group_nr] = 0; - for (yy=0; yy= 200 && element == EL_AMOEBE_BD) + if (element == EL_AMOEBE_BD && AmoebaCnt2[new_group_nr] >= 200) { - AmoebeUmwandeln2(newax, neway, EL_FELSBROCKEN); + AmoebeUmwandelnBD(newax, neway, EL_FELSBROCKEN); return; } } } - if (element!=EL_AMOEBE_NASS || newaylife[1]) { @@ -2513,7 +2699,7 @@ void Life(int ax, int ay) } } else if (IS_FREE(xx, yy) || Feld[xx][yy] == EL_ERDREICH) - { /* Randfeld ohne Amoebe */ + { /* free border field */ if (nachbarn>=life[2] && nachbarn<=life[3]) { Feld[xx][yy] = element; @@ -2528,10 +2714,10 @@ void Life(int ax, int ay) void Ablenk(int x, int y) { - if (!MovDelay[x][y]) /* neue Phase / noch nicht gewartet */ + if (!MovDelay[x][y]) /* next animation frame */ MovDelay[x][y] = level.dauer_ablenk * FRAMES_PER_SECOND; - if (MovDelay[x][y]) /* neue Phase / in Wartezustand */ + if (MovDelay[x][y]) /* wait some time before next frame */ { MovDelay[x][y]--; if (MovDelay[x][y]) @@ -2552,10 +2738,10 @@ void Ablenk(int x, int y) void Birne(int x, int y) { - if (!MovDelay[x][y]) /* neue Phase / noch nicht gewartet */ + if (!MovDelay[x][y]) /* next animation frame */ MovDelay[x][y] = 800; - if (MovDelay[x][y]) /* neue Phase / in Wartezustand */ + if (MovDelay[x][y]) /* wait some time before next frame */ { MovDelay[x][y]--; if (MovDelay[x][y]) @@ -2589,10 +2775,10 @@ void Blubber(int x, int y) void NussKnacken(int x, int y) { - if (!MovDelay[x][y]) /* neue Phase / noch nicht gewartet */ + if (!MovDelay[x][y]) /* next animation frame */ MovDelay[x][y] = 7; - if (MovDelay[x][y]) /* neue Phase / in Wartezustand */ + if (MovDelay[x][y]) /* wait some time before next frame */ { MovDelay[x][y]--; if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y))) @@ -2634,10 +2820,10 @@ void AusgangstuerOeffnen(int x, int y) { int delay = 6; - if (!MovDelay[x][y]) /* neue Phase / noch nicht gewartet */ + if (!MovDelay[x][y]) /* next animation frame */ MovDelay[x][y] = 5*delay; - if (MovDelay[x][y]) /* neue Phase / in Wartezustand */ + if (MovDelay[x][y]) /* wait some time before next frame */ { int tuer; @@ -2668,10 +2854,10 @@ void EdelsteinFunkeln(int x, int y) DrawGraphicAnimation(x, y, GFX_EDELSTEIN_BD, 4, 4, ANIM_REVERSE); else { - if (!MovDelay[x][y]) /* neue Phase / noch nicht gewartet */ + if (!MovDelay[x][y]) /* next animation frame */ MovDelay[x][y] = 11 * !SimpleRND(500); - if (MovDelay[x][y]) /* neue Phase / in Wartezustand */ + if (MovDelay[x][y]) /* wait some time before next frame */ { MovDelay[x][y]--; @@ -2709,10 +2895,10 @@ void MauerWaechst(int x, int y) { int delay = 6; - if (!MovDelay[x][y]) /* neue Phase / noch nicht gewartet */ + if (!MovDelay[x][y]) /* next animation frame */ MovDelay[x][y] = 3*delay; - if (MovDelay[x][y]) /* neue Phase / in Wartezustand */ + if (MovDelay[x][y]) /* wait some time before next frame */ { int phase; @@ -2764,10 +2950,10 @@ void MauerAbleger(int ax, int ay) boolean oben_massiv = FALSE, unten_massiv = FALSE; boolean links_massiv = FALSE, rechts_massiv = FALSE; - if (!MovDelay[ax][ay]) /* neue Mauer / noch nicht gewartet */ + if (!MovDelay[ax][ay]) /* start building new wall */ MovDelay[ax][ay] = 6; - if (MovDelay[ax][ay]) /* neue Mauer / in Wartezustand */ + if (MovDelay[ax][ay]) /* wait some time before building new wall */ { MovDelay[ax][ay]--; if (MovDelay[ax][ay]) @@ -2992,8 +3178,8 @@ static void PlayerActions(struct PlayerInfo *player, byte player_action) void GameActions() { - static long action_delay = 0; - long action_delay_value; + static unsigned long action_delay = 0; + unsigned long action_delay_value; int sieb_x = 0, sieb_y = 0; int i, x, y, element; byte *recorded_player_action; @@ -3005,8 +3191,40 @@ void GameActions() action_delay_value = (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay); + /* + if (tape.playing && tape.fast_forward) + { + char buf[100]; + + sprintf(buf, "FFWD: %ld ms", action_delay_value); + print_debug(buf); + } + */ + + /* main game synchronization point */ + + + + +#if 1 WaitUntilDelayReached(&action_delay, action_delay_value); +#else + + while (!DelayReached(&action_delay, action_delay_value)) + { + char buf[100]; + + sprintf(buf, "%ld %ld %ld", + Counter(), action_delay, action_delay_value); + print_debug(buf); + } + print_debug("done"); + +#endif + + + if (network_playing && !network_player_action_received) { @@ -3016,8 +3234,10 @@ void GameActions() #endif */ +#ifndef MSDOS /* last chance to get network player actions without main loop delay */ HandleNetworking(); +#endif if (game_status != PLAYING) return; @@ -3059,8 +3279,10 @@ void GameActions() stored_player[i].effective_action = stored_player[i].action; } +#ifndef MSDOS if (network_playing) SendToServer_MovePlayer(summarized_player_action); +#endif if (!options.network && !setup.team_mode) local_player->effective_action = summarized_player_action; @@ -3093,6 +3315,7 @@ void GameActions() #ifdef DEBUG + /* if (TimeFrames == 0 && !local_player->gone) { extern unsigned int last_RND(); @@ -3102,13 +3325,16 @@ void GameActions() last_RND(), getStateCheckSum(level.time - TimeLeft)); } + */ #endif #ifdef DEBUG + /* if (GameFrameDelay >= 500) printf("FrameCounter == %d\n", FrameCounter); + */ #endif @@ -3405,11 +3631,11 @@ boolean MoveFigure(struct PlayerInfo *player, int dx, int dy) if (player->MovPos) { - /* should only happen if pre-1.0 tape recordings are played */ + /* should only happen if pre-1.2 tape recordings are played */ /* this is only for backward compatibility */ #if DEBUG - printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.0 LEVEL TAPES.\n"); + printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES.\n"); #endif while (player->MovPos) @@ -3517,7 +3743,7 @@ boolean MoveFigure(struct PlayerInfo *player, int dx, int dy) else if (old_jx == jx && old_jy != jy) player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP); - DrawLevelField(jx, jy); /* für "ErdreichAnbroeckeln()" */ + DrawLevelField(jx, jy); /* for "ErdreichAnbroeckeln()" */ player->last_move_dir = player->MovDir; } @@ -3579,7 +3805,7 @@ void ScrollFigure(struct PlayerInfo *player, int mode) void ScrollScreen(struct PlayerInfo *player, int mode) { - static long screen_frame_counter = 0; + static unsigned long screen_frame_counter = 0; if (mode == SCROLL_INIT) { @@ -3843,6 +4069,7 @@ int DigField(struct PlayerInfo *player, case EL_EDELSTEIN_ROT: case EL_EDELSTEIN_LILA: case EL_DIAMANT: + case EL_SP_INFOTRON: RemoveField(x, y); local_player->gems_still_needed -= (element == EL_DIAMANT ? 3 : 1); if (local_player->gems_still_needed < 0) @@ -3970,7 +4197,7 @@ int DigField(struct PlayerInfo *player, case EL_AUSGANG_ZU: case EL_AUSGANG_ACT: - /* Tür ist (noch) nicht offen! */ + /* door is not (yet) open */ return MF_NO_ACTION; break; @@ -4090,7 +4317,11 @@ int DigField(struct PlayerInfo *player, break; default: - return MF_NO_ACTION; + if (IS_EATABLE(element)) /* other kinds of 'dirt' */ + Feld[x][y] = EL_LEERRAUM; + else + return MF_NO_ACTION; + break; }