📄 game.c
字号:
{ CMsgPlayerDoesntClaimMsg *m = (CMsgPlayerDoesntClaimMsg *)cm; setups; affected_id = m->id; /* because clients might easily send noclaims after a timeout, we do not return error for bad noclaims. However, this is a problem for entirely human clients, if there is no timeout, since then a typo will hang the game. FIXME: we should return error if the game does not have a time out set. */ if ( chk ) { /* noclaim is legitimate while konging */ if ( ! ( (g->state == Discarding || g->state == DeclaringSpecials) && g->konging ) ) { if ( g->state != Discarded ) { g->cmsg_err = "No discard to pass on"; return affected_id; } if ( g->serial != m->discard ) { g->cmsg_err = "Too late to pass on that discard"; return affected_id;; } } } /* We should only record this no claim if the discard serial is correct. (Otherwise it's probably a duplicate caused by a race condition.) If it doesn't match, just ignore it, don't return error. */ if ( g->serial == m->discard ) g->claims[s] = NoClaim; return affected_id; } case CMsgDangerousDiscard: { CMsgDangerousDiscardMsg *m = (CMsgDangerousDiscardMsg *)cm; setups; affected_id = m->id; if ( chk ) { if ( m->discard != g->serial ) { g->cmsg_err = "Not current discard (dangerous discard message)"; return -1; } if ( m->id != g->players[g->supplier]->id ) { g->cmsg_err = "Dangerous discarder doesn't match supplier"; return -1; } } game_setflag(g,GFDangerousDiscard); if ( m->nochoice ) game_setflag(g,GFNoChoice); return affected_id; } case CMsgPlayerClaimsPung: { CMsgPlayerClaimsPungMsg *m = (CMsgPlayerClaimsPungMsg *)cm; setups; affected_id = m->id; if ( chk ) { if ( g->state != Discarded ) { g->cmsg_err = "Can't pung now"; return -1; } if ( g->serial != m->discard ) { g->cmsg_err = "Too late: next discard has been made"; return -1; } if ( g->player == s ) { g->cmsg_err = "Can't claim own discard!"; return -1; } if ( ! player_can_pung(p,g->tile) ) { g->cmsg_err = "Can't pung that tile"; return -1; } if ( pflag(p,Calling) ) { g->cmsg_err = "Calling players can only claim Mah-Jong"; return -1; } } /* end of checking */ g->claims[s] = PungClaim; return affected_id; } case CMsgPlayerClaimsKong: { CMsgPlayerClaimsKongMsg *m = (CMsgPlayerClaimsKongMsg *)cm; setups; affected_id = m->id; if ( chk ) { if ( g->state != Discarded ) { g->cmsg_err = "Can't kong now"; return -1; } if ( g->serial != m->discard ) { g->cmsg_err = "Too late: next discard has been made"; return -1; } if ( g->player == s ) { g->cmsg_err = "Can't claim own discard!"; return -1; } if ( ! player_can_kong(p,g->tile) ) { g->cmsg_err = "Can't kong that tile"; return -1; } if ( pflag(p,Calling) ) { g->cmsg_err = "Calling players can only claim Mah-Jong"; return -1; } } /* end of checking */ g->claims[s] = KongClaim; return affected_id; } case CMsgPlayerClaimsChow: { CMsgPlayerClaimsChowMsg *m = (CMsgPlayerClaimsChowMsg *)cm; setups; affected_id = m->id; if ( chk ) { if ( g->state != Discarded ) { g->cmsg_err = "Can't chow now"; return -1; } if ( g->serial != m->discard ) { g->cmsg_err = "Too late: next discard has been made"; return -1; } if ( g->player == s ) { g->cmsg_err = "Can't claim own discard!"; return -1; } if ( s != nextseat(g->player) ) { g->cmsg_err = "Can't chow out of turn"; return -1; } if ( ! player_can_chow(p,g->tile,m->cpos) ) { g->cmsg_err = "Can't make that chow"; return -1; } if ( pflag(p,Calling) ) { g->cmsg_err = "Calling players can only claim Mah-Jong"; return -1; } } /* end of checking */ g->claims[s] = ChowClaim; g->cpos = m->cpos; return affected_id; } case CMsgPlayerClaimsMahJong: { CMsgPlayerClaimsMahJongMsg *m = (CMsgPlayerClaimsMahJongMsg *)cm; setups; affected_id = m->id; if ( g->state == Discarded ) { if ( chk ) { if ( g->serial != m->discard ) { g->cmsg_err = "Too late: next discard has been made"; return -1; } if ( g->player == s ) { g->cmsg_err = "Can't claim own discard!"; return -1; } if ( ! player_can_mah_jong(p,g->tile,mjspecflags) ) { g->cmsg_err = "Can't mah-jong with that tile"; return -1; } } g->claims[s] = MahJongClaim; } else if ( (g->state == Discarding || g->state == DeclaringSpecials) && g->konging ) { /* trying to rob a kong */ /* arguably one should be able to abandon one's kong and claim mahjong instead. However, at present we have no provision for retracting other claims, so we don't have it here either. */ if ( chk ) { if ( g->serial != m->discard ) { g->cmsg_err = "Too late: next discard has been made"; return -1; } if ( g->player == s ) { g->cmsg_err = "Can't rob own kong"; return -1; } if ( ! player_can_mah_jong(p,g->tile,mjspecflags) ) { g->cmsg_err = "Can't mah-jong with that tile"; return -1; } /* closed kongs can be robbed only for thirteen unique wonders */ if ( g->konging == DeclaringKong ) { if ( ! player_can_thirteen_wonders(p,g->tile) ) { g->cmsg_err = "Can only rob a closed kong for Thirteen Wonders"; return -1; } } /* in principle, we ought to check that the robbee actually has the kong. However, that would be a consistency error, and in that case we'll die soon enough anyway! */ } g->claims[s] = MahJongClaim; } else { g->cmsg_err = "Nothing to claim"; return -1; } return affected_id; } case CMsgClaimDenied: return affected_id; /* nothing to do */ case CMsgStateSaved: return affected_id; /* nothing to do */ case CMsgPlayerPairs: { CMsgPlayerPairsMsg *m = (CMsgPlayerPairsMsg *)cm; setups; affected_id = m->id; if ( chk ) { /* sanity checking */ /* Note that normally we assume the legality of the pair was previously checked at claim time */ if ( ! ( g->state == MahJonging && g->mjpending && s == g->player ) ) { g->cmsg_err = "Nothing to pair"; return -1; } if ( m->tile != g->tile ) { g->cmsg_err = "Paired tile isn't discard"; return -2; } } if ( g->state == MahJonging ) { /* checking is complex: not only must the player be able make the pair, but it must then be able to complete the hand */ Player cp; if ( chk ) copy_player(&cp,p); /* in case next fails */ if ( ! player_pairs(p,m->tile) ) { g->cmsg_err = "Can't pair that tile"; return -1; } if ( chk && ! player_can_mah_jong(p,HiddenTile,mjspecflags) ) { g->cmsg_err = "Can't go out with that pair"; copy_player(p,&cp); /* restore status quo */ return -1; } g->mjpending = 0; if ( chk ) mark_dangerous_discards(g); } else { g->cmsg_err = "Nothing to pair"; return -1; } return affected_id; }/* this is a bit awkward: the discard is exposed, so really the special set is exposed. But we have no means of putting a special set into the player's hand. I think we will rely on the external logic using the whence game field to distinguish */ case CMsgPlayerSpecialSet: { CMsgPlayerSpecialSetMsg *m = (CMsgPlayerSpecialSetMsg *)cm; setups; affected_id = m->id; if ( chk ) { /* sanity checking */ /* Note that normally we assume the legality of the set was previously checked at claim time */ if ( ! ( g->state == MahJonging && g->mjpending && s == g->player ) ) { g->cmsg_err = "Nothing to claim"; return -1; } if ( m->tile != g->tile ) { g->cmsg_err = "Claimed tile isn't discard"; return -2; } } if ( g->state == MahJonging ) { /* It is a rule that the special set must be formed last, and use up all the remaining tiles */ Player cp; int res; if ( chk ) copy_player(&cp,p); /* in case next fails */ if ( chk ) { /* only currently recognized special hand is 13 wonders */ if ( ! player_can_thirteen_wonders(p,g->tile) ) { g->cmsg_err = "No special hand to form"; return -1; } } /* first we use the given tiles as in a ShowTiles */ res = player_shows_tiles(p,m->tiles); if ( ! res ) { /* we are in deep trouble */ g->cmsg_err = "player_shows_tiles failed"; return -2; } if ( chk ) { /* better be the same number of tiles before and after */ if ( p->num_concealed != cp.num_concealed ) { g->cmsg_err = "Wrong number of tiles in special set"; copy_player(p,&cp); /* restore status quo */ return -1; } } /* now we'll add the discard to the hand */ if ( ! player_draws_tile(p,m->tile) ) { /* How can this happen ? */ g->cmsg_err = "Error in player_draws_tile"; return -2; } if ( chk && ! player_can_mah_jong(p,HiddenTile,mjspecflags) ) { g->cmsg_err = "No mah jong hand!"; copy_player(p,&cp); /* restore status quo */ return -1; } g->mjpending = 0; } else { g->cmsg_err = "Can't declare special set now"; return -1; } return affected_id; } case CMsgPlayerPungs: { CMsgPlayerPungsMsg *m = (CMsgPlayerPungsMsg *)cm; setups; affected_id = m->id; if ( chk ) { /* sanity checking */ /* Note that normally we assume the legality of the pung was previously checked at claim time */ if ( ! (g->state == Discarded || (g->state == MahJonging && g->mjpending && g->player == s) ) ) { g->cmsg_err = "Nothing to pung"; return -1; } if ( m->tile != g->tile ) { g->cmsg_err = "Punged tile isn't discard"; return -2; } } /* what happens now depends on whether this is a claim for MahJong or not */ if ( g->state == Discarded ) { if ( ! player_pungs(p,m->tile) ) { g->cmsg_err = "Can't pung that tile"; return -1; } g->state = Discarding; g->konging = NotKonging; g->tile = m->tile; g->needs = FromNone; g->supplier = g->player; g->player = s; g->whence = FromDiscard; g->exposed_tile_count[m->tile] += 2; } else if ( g->state == MahJonging ) { /* checking is more complex: not only must the player be able make the pung, but it must then be able to complete the hand */ Player cp; if ( chk ) copy_player(&cp,p); /* in case next fails */ if ( ! player_pungs(p,m->tile) ) { g->cmsg_err = "Can't pung that tile"; return -1; } if ( chk && ! player_can_mah_jong(p,HiddenTile,mjspecflags) ) { g->cmsg_err = "Can't go out with that pung"; copy_player(p,&cp); /* restore status quo */ return -1; } g->mjpending = 0; } else { g->cmsg_err = "Nothing to chow"; return -1; } if ( chk ) { mark_dangerous_discards(g); set_danger_flags(g,p); /* the player may now be dangerous */ } return affected_id; } case CMsgPlayerKongs: { CMsgPlayerKongsMsg *m = (CMsgPlayerKongsMsg *)cm; setups; affected_id = m->id; if ( chk ) { /* we'll assume the legality of the claim was checked */ if ( g->state != Discarded ) { g->cmsg_err = "Nothing to kong"; return -1; } } if ( ! player_kongs(p,m->tile) ) { g->cmsg_err = "Can't kong that tile"; return -2;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -