You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 

236 lines
5.2 KiB

package game
import (
"errors"
"sonet/pkg/utils/state"
"sync"
)
type MjTable struct {
state.State
TableId string
MasterId string // 房主id
Stage int8 // 游戏阶段:1.等待开始;2.游戏开始;3.undefined; 4.结算阶段
GameTimes int32 // 游戏次数
PlayerCount int16
Players [4]*MjPlayer // 玩家列表
PlayerDirects [4]int8 // 玩家方向: 0.东 1.南 2.西 3.北
MjWall *MjWall // 麻将牌堆
HitPlayerId string // 回合出牌玩家
Hits []int8
Lock *sync.Mutex
}
func NewMjTable(tableId string, masterId string) *MjTable {
return &MjTable{
TableId: tableId,
MasterId: masterId,
Stage: 1,
Players: [4]*MjPlayer{},
PlayerDirects: [4]int8{0, 1, 2, 3},
MjWall: &MjWall{},
Lock: &sync.Mutex{},
}
}
func (t *MjTable) Init() {
t.InitState(t, state.WithState2Mapping(func(states state.S, setters *[]state.Setter) {
if mjWall, ok := states["EastPlayerIndex"]; ok {
delete(states, "EastPlayerIndex")
states["eastPlayerId"] = mjWall.(*MjWall).RestNum()
}
if mjWall, ok := states["MjWall"]; ok {
delete(states, "MjWall")
states["RestTiles"] = mjWall.(*MjWall).RestNum()
}
}))
}
// GameStart 下一句游戏
func (t *MjTable) GameStart() (err error) {
// 换东南西北
if t.GameTimes > 0 {
for i := 0; i < 4; i++ {
t.PlayerDirects[i] = (t.PlayerDirects[i] + 1) % 4
}
}
// 初始化麻将牌堆
t.MjWall.Init()
return t.SetStateEx(state.S{
"Stage": int8(2),
"PlayerDirects": t.PlayerDirects,
"GameTimes": t.GameTimes + 1,
"MjWall": t.MjWall,
"Hits": []int8{},
})
}
// GetEastPlayerIndex 东玩家索引
func (t *MjTable) GetEastPlayerIndex() int {
for i, direct := range t.PlayerDirects {
if direct == 0 {
return i
}
}
return 0
}
// EastPlayerSit 东玩家(为nil则顺时针最近玩家)索引位置
func (t *MjTable) EastPlayerSit() int {
for i, direct := range t.PlayerDirects {
if direct == 0 { // 东
if t.Players[i] != nil {
return i
} else {
for j := 1; j < 4; j++ {
sit := (i + j) % 4
if t.Players[sit] != nil {
return sit
}
}
}
break
}
}
return 0
}
// PrevHit 最后出的一张牌
func (t *MjTable) PrevHit() int8 {
length := len(t.Hits)
if length == 0 {
return 0
}
return t.Hits[length-1]
}
func (t *MjTable) GetPlayer(playerId string) *MjPlayer {
for _, p := range t.Players {
if p != nil && p.PlayerId == playerId {
return p
}
}
return nil
}
func (t *MjTable) GetPlayers(excludePlayerIds ...string) (players []*MjPlayer) {
excludes := make(map[string]bool, len(excludePlayerIds))
for _, exclude := range excludePlayerIds {
excludes[exclude] = true
}
players = make([]*MjPlayer, 0, t.PlayerCount)
for _, player := range t.Players {
if player != nil {
if excludes[player.PlayerId] { // 跳过要忽略的
continue
}
players = append(players, player)
}
}
return
}
func (t *MjTable) GetPlayerIds(excludePlayerIds ...string) (playerIds []string) {
players := t.GetPlayers(excludePlayerIds...)
playerIds = make([]string, 0, len(players))
for _, p := range players {
playerIds = append(playerIds, p.PlayerId)
}
return
}
func (t *MjTable) PlayerJoin(player *MjPlayer) (int, error) {
for i := 0; i < 4; i++ {
if t.Players[i] == nil {
err := t.SetStateEx(state.S{
"Players": state.SetArray(i, player),
})
if err != nil {
return i, err
}
player.MjTable = t
player.Status = 1
t.PlayerCount++
return i, err
}
}
return 0, errors.New("牌桌已满")
}
func (t *MjTable) PlayerLeave(playerId string) (err error) {
for i := 0; i < 4; i++ {
if t.Players[i] != nil && t.Players[i].PlayerId == playerId {
player := t.Players[i]
err = t.SetStateEx(state.S{
"Players": state.SetArray(i, nil),
})
if err != nil {
return
}
player.MjTable = nil
t.PlayerCount--
break
}
}
return
}
// NextDrawPlayer 下一摸牌玩家
func (t *MjTable) NextDrawPlayer(curPlayerId string) (nextPlayer *MjPlayer, err error) {
if t.MjWall.RestNum() == 0 { // 没牌了结算
err = t.GameOver()
return
}
for i, p := range t.Players {
if p != nil && p.PlayerId == curPlayerId {
for j := 1; j <= 4; j++ {
next := t.Players[(i+j)%4]
if next != nil && next.Status != 3 {
nextPlayer = next
return
}
}
break
}
}
// 没下个玩家了结算
err = t.GameOver()
return
}
// GameOver 游戏结束, 结算
func (t *MjTable) GameOver() (err error) {
// 游戏结束
for _, player := range t.GetPlayers() {
player.SetState(state.S{
"Status": int8(4),
"MjHand": player.MjHand,
"MjDraw": player.MjDraw,
})
player.State2Broadcast()
}
t.SetState(state.S{"Stage": int8(4)})
t.State2Broadcast()
return
}
// PlayerWin 计算胡牌倍数
func (t *MjTable) PlayerWin(player *MjPlayer, selfDraw bool, wins []HandWin) (settle *WinSettle) {
settle = &WinSettle{
Weights: player.WinWeights(selfDraw),
}
for _, win := range wins {
winType := win.GetWinType(player.MjPeng, player.MjGang)
calcWeight := winType.Weight()
for _, weight := range settle.Weights {
calcWeight *= weight.Weight()
}
if calcWeight > settle.Weight {
settle.Weight = calcWeight
settle.WinType = winType
}
}
return
}