📄 game.c
字号:
return affected_id; case CMsgInfoTiles: /* Note that this always forces the player into compliance. It's up to the client not to call this routine when it's not wanted */ { CMsgInfoTilesMsg *m = (CMsgInfoTilesMsg *)cm; setups; set_player_tiles(p,m->tileinfo); } return affected_id; case CMsgNewRound: { CMsgNewRoundMsg *m = (CMsgNewRoundMsg *)cm; g->round = m->round; g->hands_as_east = 0; /* superfluous */ } return affected_id; case CMsgPause: { CMsgPauseMsg *m = (CMsgPauseMsg *)cm; int i; seats s; if ( g->paused ) { /* it is legitimate to start a new pause before one is complete */ free(g->paused); } g->paused = (char *)malloc(strlen(m->reason)+1); if ( g->paused == NULL ) { warn("unable to malloc space for pause reason"); return -2; } strcpy(g->paused,m->reason); for ( i=0; i < NUM_SEATS; i++) g->ready[i] = 0; s = game_id_to_seat(g,m->exempt); if ( s != noseat ) g->ready[s] = 1; affected_id = 0; /* affects everybody, really */ } return affected_id; case CMsgPlayerReady: { CMsgPlayerReadyMsg *m = (CMsgPlayerReadyMsg *)cm; int i,ready; setups; affected_id = m->id; if ( g->paused ) { g->ready[s] = 1; ready = 1; for ( i=0 ; i < NUM_SEATS; i++ ) ready = (ready && g->ready[i]); if ( ready ) { free(g->paused); g->paused = NULL; } } } return affected_id; case CMsgNewHand: { CMsgNewHandMsg *m = (CMsgNewHandMsg *)cm; PlayerP players[NUM_SEATS]; int n; if ( chk ) { if ( g->state != HandComplete ) { g->cmsg_err = "Still playing current hand"; return -1; } } /* If east has changed, reset hands_as_east */ if ( m->east != g->players[east]->id ) g->hands_as_east = 0; /* first we have to rotate the players so that the current east is correct. */ for (i=0; i < NUM_SEATS; i++) players[i] = g->players[i]; n = game_id_to_seat(g,m->east); for (i=0; i < NUM_SEATS; i++) g->players[i] = players[(i+n)%NUM_SEATS]; /* game state */ g->state = Dealing; /* reset the hand scores and clear the flags */ for ( i = 0 ; i < NUM_SEATS ; i++ ) { /* Warning: this knows TileWind = seats+1 */ player_newhand(g->players[i],i+1); } /* clear game flags */ g->flags = 0; g->konging = NotKonging; g->wall.live_used = 0; g->wall.size = game_get_option_value(g,GOFlowers,NULL) ? 144 : 136; g->wall.dead_end = g->wall.size; g->wall.live_end = g->wall.dead_end - (game_get_option_value(g,GODeadWall,NULL) ? (game_get_option_value(g,GODeadWall16,NULL) ? 16 : 14) : 0) ; g->serial = 0; /* must be initialized */ for ( i = 0 ; i < MaxTile ; i++ ) { g->exposed_tile_count[i] = 0; g->discarded_tile_count[i] = 0; } } return affected_id; case CMsgPlayerDraws: { CMsgPlayerDrawsMsg *m = (CMsgPlayerDrawsMsg *)cm; Tile t; setups; affected_id = m->id; if ( chk ) { /* can this player draw? */ if ( g->state == Dealing ) { /* ought to check, but currently don't */ } else if ( g->state == Discarded ) { seats i; seats ds = g->player; /* legal if it's our turn and all claims have been received */ if ( s != nextseat(ds) ) { g->cmsg_err = "Drawing out of turn"; return -1; } for ( i = 0 ; i < NUM_SEATS ; i++ ) if ( i != ds && g->claims[i] == UnknownClaim ) { g->cmsg_err = "Drawing before all claims received"; return -1; } /* better not be a claim */ for ( i = 0 ; i < NUM_SEATS ; i++ ) if ( i != ds && g->claims[i] > NoClaim ) { g->cmsg_err = "Somebody has claimed the discard"; return -1; } } else if ( g->state == DeclaringSpecials ) { /* legal if it's our turn and we need a tile */ if ( s != g->player ) { g->cmsg_err = "Can't draw out of turn"; return -1; } if ( g->needs != FromWall ) { g->cmsg_err = "Don't need a tile now"; return -1; } } else if ( g->state == Discarding ) { /* only legal if we're drawing a replacement for a special under the rules where this comes from the live wall */ if ( g->needs != FromWall ) { g->cmsg_err = "Already drawn or claimed a tile"; return -1; } } else { /* In any other state */ g->cmsg_err = "Can't draw a tile now"; return -1; } } /* end of legality checking */ t = game_draw_tile(g); if ( t == ErrorTile ) { g->cmsg_err = "Wall exhausted"; return -1; } if ( chk && !teq(t,m->tile) ) { g->cmsg_err = "Drawing tile not the first in wall"; warn(g->cmsg_err); return -2; } /* if this fails, we're in a mess */ if ( ! player_draws_tile(p,m->tile) ) { g->cmsg_err = "Unexpected failure drawing tile (player)"; warn(g->cmsg_err); return -2; } /* next state of game */ if ( g->state == Dealing ) { /* if this is east, store the tile */ if ( s == east ) g->tile = m->tile; /* if deal is complete ... */ if ( g->players[east]->num_concealed == MAX_CONCEALED && g->players[south]->num_concealed == MAX_CONCEALED-1 && g->players[west]->num_concealed == MAX_CONCEALED-1 && g->players[north]->num_concealed == MAX_CONCEALED-1 ) { g->state = DeclaringSpecials; /* g->tile was set before if nec */ g->needs = FromNone; g->player = east; g->whence = FromWall; } } else { if ( g->state == Discarded ) { g->state = Discarding; } g->tile = m->tile; g->needs = FromNone; g->player = s; g->whence = FromWall; g->konging = NotKonging; } } return affected_id; case CMsgPlayerDrawsLoose: { CMsgPlayerDrawsLooseMsg *m = (CMsgPlayerDrawsLooseMsg *)cm; Tile t; setups; affected_id = m->id; if ( chk ) { /* can this player draw a loose tile? */ /* can only happen when it's currently our turn */ if ( s != g->player ) { g->cmsg_err = "Can't draw a loose tile out of turn"; return -1; } else if ( (g->state == Discarding || g->state == DeclaringSpecials) && g->needs == FromLoose ) { /* OK */ } else { /* In any other state */ g->cmsg_err = "Can't draw a tile now"; return -1; } } /* end of legality checking */ t = game_draw_loose_tile(g); if ( t == ErrorTile ) { g->cmsg_err = "Dead wall exhausted"; return -1; } if ( chk && !teq(t,m->tile) ) { g->cmsg_err = "Drawing tile not the first loose tile"; warn(g->cmsg_err); return -2; } /* if this fails, we're in a mess */ if ( ! player_draws_loose_tile(p,m->tile) ) { g->cmsg_err = "Unexpected failure drawing tile (player)"; warn(g->cmsg_err); return -2; } /* next state of game is unchanged apart from tile info */ g->needs = FromNone; g->whence = FromLoose; g->tile = m->tile; g->konging = NotKonging; } return affected_id; case CMsgPlayerDeclaresSpecial: { CMsgPlayerDeclaresSpecialMsg *m = (CMsgPlayerDeclaresSpecialMsg *)cm; setups; affected_id = m->id; if ( chk ) { /* Specials may be declared during the initial phase, or if it is the player's turn to discard, and at no other time. Morever, all rules I've seen say that specials can only be declared after drawing from the wall. Silly, but there it is. Pre-release version didn't have this, so we need to keep it for compatability. */ if ( g->state == DeclaringSpecials || (g->state == Discarding && (g->protversion < 1010 || g->whence != FromDiscard) ) ) { if ( g->player != s ) { g->cmsg_err = "Can only declare in turn"; return -1; } } else { g->cmsg_err = "Can't discard specials now"; return -1; } } if ( m->tile == HiddenTile ) { if ( chk && g->state == Discarding ) { /* this shouldn't happen. Although it's harmless, we will nonetheless return an error */ g->cmsg_err = "Can't declare blank special now"; return -1; } if ( s == north ) { g->state = Discarding; g->konging = NotKonging; g->needs = FromNone; /* we need to set whence. It could be from loose or wall, according to what happened during declaring specials. However, if east is going to go out immediately, it doesn't matter, since that's a limit anyway. So we just set it to FromWall. FIXME: in fact the only reason we need to do this is because the controller deals itself, rather than passing cmsgs to the game. Sometime we should fix the controller. But that probably means making this routine fill in details of cmsgs: which seems right anyway. */ g->whence = FromWall; g->player = east; } else { /* why do we set the state? So that resumption works: otherwise, there's no cue to enter the ds state */ g->state = DeclaringSpecials; g->player = nextseat(s); } } else { if ( ! player_declares_special(p,m->tile) ) { /* This can only fail if the player doesn't hold the tile */ /* Actually, that's not true. It can also fail if the "hiddenness" is inconsistent. In this case, player will scream anyway. */ g->cmsg_err = "Don't have that special"; return -1; } if ( game_get_option_value(g,GOFlowersLoose,NULL) ) g->needs = FromLoose; else g->needs = FromWall; g->exposed_tile_count[m->tile]++; /* who's counting specials ? */ } } return affected_id; case CMsgStartPlay: affected_id = ((CMsgStartPlayMsg *)cm)->id; g->active = 1; return affected_id; case CMsgStopPlay: g->active = 0; return affected_id; case CMsgPlayerDiscards: { CMsgPlayerDiscardsMsg *m = (CMsgPlayerDiscardsMsg *)cm; int nodiscard; setups; affected_id = m->id; if ( chk ) { /* if it's not time to discard, return error */ if ( ! (g->state == Discarding && g->player == s) ) { g->cmsg_err = "Can't discard now"; return -1; } /* give a more helpful error message when the player is waiting for a tile */ if ( g->needs ) { g->cmsg_err = "Need to draw a tile before discarding"; return -1; } /* players aren't allowed to discard if the game is paused */ if ( g->paused ) { g->cmsg_err = "Can't discard until everybody's ready"; return -1; } /* check legitimacy of calling declaration */ /* must be no sets declared */ if ( m->calling && p->num_concealed < 14 ) { g->cmsg_err = "Must have concealed hand to declare calling"; return -1; } /* multiple calling declarations are silly */ if ( m->calling && pflag(p,Calling) ) { g->cmsg_err = "Already calling"; return -1; } /* at present, only original call allowed */ if ( m->calling && ! pflag(p,NoDiscard) ) { g->cmsg_err = "Only Original Call declaration allowed"; return -1; } /* if player is calling, can only discard the tile drawn. It is supposed to be impossible for a calling player to have claimed from a discard; but just as a precaution we'll check for it and panic. */ if ( pflag(p,Calling) ) { if ( g->whence == FromDiscard ) { g->cmsg_err = "Calling player discarding after claim??"; warn("Calling player discarding after claim"); return -2; } if ( m->tile != g->tile ) { g->cmsg_err = "Calling: can't discard that tile"; return -1; } } } nodiscard = pflag(p,NoDiscard); /* save as will be cleared by this */ if ( ! player_discards(p,m->tile) ) { g->cmsg_err = "You can't discard that tile"; return -1; } g->state = Discarded; g->player = s; g->tile = m->tile; g->serial = m->discard; for ( i = 0 ; i < NUM_SEATS ; i++ ) g->claims[i] = UnknownClaim; g->chowpending = 0; game_clearflag(g,GFKong); game_clearflag(g,GFKongUponKong); game_clearflag(g,GFDangerousDiscard); game_clearflag(g,GFNoChoice); if ( m->calling ) { psetflag(p,Calling); if ( nodiscard ) { psetflag(p,OriginalCall); } } g->exposed_tile_count[m->tile]++; g->discarded_tile_count[m->tile]++; return affected_id; } case CMsgPlayerDoesntClaim:
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -