package caishen import ( "encoding/json" "math" "math/rand" "time" "mongo.games.com/goserver/core/basic" "mongo.games.com/goserver/core/task" "mongo.games.com/game/common" rule "mongo.games.com/game/gamerule/caishen" "mongo.games.com/game/gamesrv/base" "mongo.games.com/game/model" "mongo.games.com/game/proto" "mongo.games.com/game/protocol/caishen" "mongo.games.com/game/protocol/gamehall" "mongo.games.com/goserver/core/logger" ) type CaiShenJackpot struct { createdTime time.Time userName string priceValue int64 roomID int64 spinID string } type CaiShenSceneData struct { *base.Scene //房间信息 players map[int32]*CaiShenPlayerData //玩家信息 jackpot *base.SlotJackpotPool //奖池 lastJackpotValue int64 //上一次奖池变化时的值 lastJackPot time.Time //增加奖池时间 lastBurstJackPot map[int32]time.Time //爆池时间 } func NewCaiShenSceneData(s *base.Scene) *CaiShenSceneData { return &CaiShenSceneData{ Scene: s, players: make(map[int32]*CaiShenPlayerData), jackpot: &base.SlotJackpotPool{}, } } func (this *CaiShenSceneData) SaveData(force bool) { } func (this *CaiShenSceneData) OnPlayerLeave(p *base.Player, reason int) { if p, exist := this.players[p.SnId]; exist { delete(this.players, p.SnId) } } func (this *CaiShenSceneData) SceneDestroy(force bool) { //销毁房间 this.Scene.Destroy(force) } func (this *CaiShenSceneData) init() bool { if this.DbGameFree == nil { return false } params := this.DbGameFree.GetJackpot() this.jackpot = &base.SlotJackpotPool{} if this.jackpot.Small <= 0 { this.jackpot.Small = 0 this.jackpot.VirtualJK = int64(params[rule.CAISHEN_JACKPOT_InitJackpot]) * int64(this.DbGameFree.GetBaseScore()) } str := base.XSlotsPoolMgr.GetPool(this.GetGameFreeId(), this.Platform) if str != "" { jackpot := &base.SlotJackpotPool{} err := json.Unmarshal([]byte(str), jackpot) if err == nil { this.jackpot = jackpot } } if this.jackpot != nil { base.SlotsPoolMgr.SetPool(this.GetGameFreeId(), this.Platform, this.jackpot) } this.lastJackPot = time.Now() this.lastBurstJackPot = make(map[int32]time.Time) this.SetLastBurstJackPot() return true } type CaiShenSpinResult struct { LinesInfo []*caishen.CaiShenLinesInfo SlotsData []int32 TotalPrizeLine int64 // 线条总金额 TotalPrizeJackpot int64 // 爆奖总金额 JackpotCnt int // 爆奖的次数 AddFreeTimes int32 // 新增免费次数 IsJackpot bool // 是否爆奖 BonusGame caishen.CaiShenBonusGameInfo BonusX []int32 TotalWinRate int32 // 中奖总倍率 TotalTaxScore int64 // 税收 WinLines []int // 赢分的线 } func (this *CaiShenSceneData) CalcLinePrize(cards []int, betLines []int64, betValue int64) (spinRes CaiShenSpinResult) { taxRate := this.DbGameFree.GetTaxRate() calcTaxScore := func(score int64, taxScore *int64) int64 { newScore := int64(float64(score) * float64(10000-taxRate) / 10000.0) if taxScore != nil { *taxScore += score - newScore } return newScore } lines := rule.CalcLine(cards, betLines) for _, line := range lines { if line.Element == rule.Element_JACKPOT && line.Count == rule.LINE_CELL { spinRes.IsJackpot = true spinRes.JackpotCnt++ } curScore := betValue * int64(line.Score) curScore = calcTaxScore(curScore, &spinRes.TotalTaxScore) spinRes.TotalPrizeLine += curScore spinRes.TotalWinRate += int32(line.Score) lineInfo := &caishen.CaiShenLinesInfo{ LineId: proto.Int32(int32(line.Index)), Position: line.Position, PrizeValue: proto.Int64(curScore), } spinRes.LinesInfo = append(spinRes.LinesInfo, lineInfo) spinRes.WinLines = append(spinRes.WinLines, int(lineInfo.GetLineId())) } if spinRes.IsJackpot { // 爆奖只计一条线 spinRes.TotalPrizeJackpot = calcTaxScore(this.jackpot.VirtualJK, &spinRes.TotalTaxScore) } var countBonus, countFree int for _, card := range cards { if card == rule.Element_BONUS { countBonus++ } if card == rule.Element_SCATTER { countFree++ } spinRes.SlotsData = append(spinRes.SlotsData, int32(card)) } // bonus game if countBonus >= 3 { if countBonus == 3 { spinRes.BonusX = []int32{1, 2, 3} } else if countBonus == 4 { spinRes.BonusX = []int32{2, 3, 4} } else { spinRes.BonusX = []int32{3, 4, 5} countBonus = 5 } totalBet := int64(len(betLines)) * betValue bonusGame := rule.GenerateBonusGame(int(totalBet), countBonus-2) var totalBonusValue int64 bonusData := make([]int64, 0) for _, value := range bonusGame.BonusData { value = calcTaxScore(value, nil) totalBonusValue += value bonusData = append(bonusData, value) } spinRes.BonusGame = caishen.CaiShenBonusGameInfo{ TotalPrizeValue: proto.Int64(totalBonusValue * int64(bonusGame.Mutiplier)), Mutiplier: proto.Int32(int32(bonusGame.Mutiplier)), DataMultiplier: proto.Int64(int64(totalBonusValue)), BonusData: bonusData, } // 小游戏税收 bonusTax := (bonusGame.DataMultiplier - totalBonusValue) * int64(bonusGame.Mutiplier) spinRes.TotalTaxScore += bonusTax } // add free if countFree >= 3 { spinRes.AddFreeTimes = int32(countFree-2) * 4 } return } func (this *CaiShenSceneData) BroadcastJackpot(sync bool) { if this.lastJackpotValue != this.jackpot.VirtualJK || sync { this.lastJackpotValue = this.jackpot.VirtualJK pack := &gamehall.SCHundredSceneGetGameJackpot{} jpfi := &gamehall.GameJackpotFundInfo{ GameFreeId: proto.Int32(this.DbGameFree.Id), JackPotFund: proto.Int64(this.jackpot.VirtualJK), } pack.GameJackpotFund = append(pack.GameJackpotFund, jpfi) proto.SetDefaults(pack) //以平台为标识向该平台内所有玩家广播奖池变动消息,游戏内外的玩家可监听该消息,减少由gamesrv向worldsrv转发这一步 tags := []string{this.Platform} logger.Logger.Trace("jackpot caishen", pack) base.PlayerMgrSington.BroadcastMessageToGroup(int(gamehall.HundredScenePacketID_PACKET_SC_GAMEJACKPOT), pack, tags) } } func (this *CaiShenSceneData) PushCoinPool(prizeFundAdd int64, IsNovice bool) { if IsNovice { base.CoinPoolMgr.PushCoinNovice(this.GetGameFreeId(), this.GroupId, this.Platform, prizeFundAdd) } else { base.CoinPoolMgr.PushCoin(this.GetGameFreeId(), this.GroupId, this.Platform, prizeFundAdd) } } func (this *CaiShenSceneData) PopCoinPool(winCoin int64, IsNovice bool) { if IsNovice { base.CoinPoolMgr.PopCoinNovice(this.GetGameFreeId(), this.GroupId, this.Platform, winCoin) } else { base.CoinPoolMgr.PopCoin(this.GetGameFreeId(), this.GroupId, this.Platform, winCoin) } } func (this *CaiShenSceneData) RecordBurstLog(name string, wincoin, totalbet int64) { log := model.NewBurstJackpotLog(this.Platform, this.DbGameFree.GameId, this.GetGameFreeId(), name, wincoin, totalbet) task.New(nil, task.CallableWrapper(func(o *basic.Object) interface{} { return model.InsertBurstJackpotLogs(log) }), nil, "InsertBurstJackpotLogs").Start() } func (this *CaiShenSceneData) BurstHistory(player *CaiShenPlayerData) { task.New(nil, task.CallableWrapper(func(o *basic.Object) interface{} { return model.GetBurstJackpotLog(this.Platform, this.DbGameFree.GameId) }), task.CompleteNotifyWrapper(func(data interface{}, t task.Task) { var logsp []*caishen.CaiShenBurstHistoryInfo if data != nil { logs := data.([]model.BurstJackpotLog) if len(logs) > 0 { for _, log := range logs { logsp = append(logsp, &caishen.CaiShenBurstHistoryInfo{ UserName: log.Name, PriceValue: log.WinCoin, TotalBet: log.TotalBet, Ts: log.Ts, }) } } } pack := &caishen.SCCaiShenBurstHistory{ BurstLog: logsp, } logger.Logger.Trace("SCCaiShenBurstHistory:", pack) player.SendToClient(int(caishen.CaiShenPacketID_PACKET_SC_CAISHEN_BURSTHISTORY), pack) }), "BurstHistory").Start() } func (this *CaiShenSceneData) GetLastBurstJackPot() time.Time { return this.lastBurstJackPot[this.GetGameFreeId()] } func (this *CaiShenSceneData) SetLastBurstJackPot() { var randT = rand.Intn(25200-7200+1) + 7200 switch this.DbGameFree.SceneType { case 1: randT = rand.Intn(25200-7200+1) + 7200 case 2: randT = rand.Intn(46800-32400+1) + 32400 case 3: randT = rand.Intn(108000-72000+1) + 72000 case 4: randT = rand.Intn(180000-108000+1) + 108000 } this.lastBurstJackPot[this.GetGameFreeId()] = time.Now().Add(time.Second * time.Duration(randT)) } func (this *CaiShenSceneData) AIAddJackPot() { if time.Now().Sub(this.lastJackPot) > 0 { var randT = rand.Intn(3) + 1 switch this.DbGameFree.SceneType { case 1: randT = rand.Intn(3) + 1 case 2: randT = rand.Intn(6-1) + 2 case 3: randT = rand.Intn(12-5) + 6 case 4: randT = rand.Intn(20-9) + 10 default: randT = rand.Intn(3) + 1 } this.lastJackPot = time.Now().Add(time.Second * time.Duration(randT)) val := int64(math.Floor(float64(this.DbGameFree.GetBaseScore()) * float64(rule.LINENUM) * float64(500) / 10000)) this.jackpot.VirtualJK += val } } func (this *CaiShenSceneData) AIBurstJackPot() { if time.Now().Sub(this.GetLastBurstJackPot()) > 0 { this.SetLastBurstJackPot() jackpotParams := this.DbGameFree.GetJackpot() var jackpotInit = int64(jackpotParams[rule.CAISHEN_JACKPOT_InitJackpot]) * int64(this.DbGameFree.GetBaseScore()) //奖池初始值 //AI机器人爆奖 val := this.jackpot.VirtualJK this.jackpot.VirtualJK = jackpotInit bet := int64(this.DbGameFree.GetBaseScore()) * int64(rule.LINENUM) this.RecordBurstLog(this.RandNickName(), val, int64(bet)) } } func (this *CaiShenSceneData) KickPlayerByTime() { if time.Now().Sub(this.GameStartTime) > time.Second*3 { this.GameStartTime = time.Now() for _, p := range this.players { if p.IsOnLine() { p.leavetime = 0 continue } p.leavetime++ if p.leavetime < 60 { continue } //踢出玩家 this.PlayerLeave(p.Player, common.PlayerLeaveReason_LongTimeNoOp, true) } //for _, p := range this.players { // //游戏次数达到目标值 // todayGamefreeIDSceneData, _ := p.GetDaliyGameData(int(this.DbGameFree.GetId())) // if !p.IsRob && // todayGamefreeIDSceneData != nil && // this.DbGameFree.GetPlayNumLimit() != 0 && // todayGamefreeIDSceneData.GameTimes >= int64(this.DbGameFree.GetPlayNumLimit()) { // this.PlayerLeave(p.Player, common.PlayerLeaveReason_GameTimes, true) // } //} if this.CheckNeedDestroy() { for _, player := range this.players { if !player.IsRob { if time.Now().Sub(player.LastOPTimer) > 10*time.Second { //离开有统计 this.PlayerLeave(player.Player, common.PlayerLeaveReason_OnDestroy, true) } } } if this.GetRealPlayerCnt() == 0 { this.SceneDestroy(true) } } } }