package entertainment import ( "errors" "fmt" "go_dreamfactory/comm" "go_dreamfactory/lego/sys/event" "go_dreamfactory/lego/sys/timewheel" "go_dreamfactory/pb" "go_dreamfactory/sys/configure" cfg "go_dreamfactory/sys/configure/structs" "time" "go.mongodb.org/mongo-driver/bson/primitive" "google.golang.org/protobuf/proto" ) const ( MaxPs = 2 // 最大体力 ) //游戏房间 type Room struct { Id string // 房间id szSession []comm.IUserSession player1 *pb.PlayerData // 玩家1 player2 *pb.PlayerData // 玩家2 chessboard *MapData // 地图数据 module *Entertainment round int32 // 当前轮数 closeRoomTimer *timewheel.Task //房间解散倒计时定时器 curPower string // 当前操作的玩家 NexPower string // 下一个操作的玩家 MaxRound int32 // 最大回合数 rd1 bool // 玩家1 是否准备 rd2 bool // 玩家2 是否准备 Status int32 //房间游戏状态 0未开始 1 已开始 2 已结束 RoomType int32 // 房间类型 1 是好友创房 0对战 2 AI 对战 MaxTime int32 // 操作时间 Playtype int32 //当前房间玩法 } func (this *Room) CloseRoomTimeOut(task *timewheel.Task, args ...interface{}) { fmt.Printf("解散房间超时+++++%d\n", time.Now().Unix()) //倒计时结束还没结束基本是游戏异常 直接清理房间 this.ModifyUserRoomInfoData(false) } // 随机一个玩法 func (this *Room) RandomPlayType() (itype int32) { var weight []int32 // 权重 // 开始随机玩法 list := this.module.configure.GetGameConsumeIntegral() for _, v := range list { weight = append(weight, v.Weight) } itype = list[comm.GetRandW(weight)].Key return } func (this *Room) InitRoom(module *Entertainment, p1 *pb.PlayerData, p2 *pb.PlayerData) *Room { var room *Room this.module = module this.chessboard = new(MapData) this.Playtype = this.RandomPlayType() this.chessboard.InitMap(module, this.Playtype) // 初始化棋盘 if s1, ok := this.module.GetUserSession(p1.Userinfo.Uid); !ok { this.module.PutUserSession(s1) } else { this.szSession = append(this.szSession, s1.Clone()) } if p2.Userinfo.Uid != "999" { // 是否是机器人 if s2, ok := this.module.GetUserSession(p2.Userinfo.Uid); !ok { this.module.PutUserSession(s2) } else { this.szSession = append(this.szSession, s2.Clone()) } } else { this.RoomType = 2 } this.MaxRound = module.ModuleTools.GetGlobalConf().ConsumeRounds room = &Room{ Id: primitive.NewObjectID().Hex(), szSession: this.szSession, player1: p1, player2: p2, chessboard: this.chessboard, module: module, round: 1, MaxRound: this.MaxRound, Status: 0, Playtype: this.Playtype, RoomType: this.RoomType, } if err := this.module.SendMsgSyncToSession(string(this.module.GetType()), "enterroom", &pb.EntertainEnterRoomPush{ Rooid: room.Id, Servepath: fmt.Sprintf("%s/%s", this.module.service.GetType(), this.module.service.GetId()), User1: room.player1, User2: room.player2, }, this.szSession...); err != nil { this.module.Errorln(err) } return room } // AI 操作了 func (this *Room) AiOperator() { var ( curScore int32 szMap []*pb.MapData bAddPs bool oid1 int32 oid2 int32 ) this.player2.Ps-- // 交换元素 szMap, oid1, oid2, bAddPs = this.chessboard.AiSwapGirde() if bAddPs { this.player2.Ps++ if this.player2.Ps > MaxPs { this.player2.Ps = MaxPs } } if this.player2.Ps <= 0 { // 权限给下一个人 this.NexPower = this.player1.Userinfo.Uid this.player1.Ps = MaxPs this.round++ } // 校验下次是不是消除 if !this.chessboard.CheckAndRefreshPlat() { this.chessboard.RedsetPlatData() szMap = append(szMap, &pb.MapData{ Data: this.chessboard.GetPalatData(), ChangeType: 1, }) } for _, v := range szMap { // curScore += v.CurSocre this.player2.Score += v.CurSocre v.CurSocre = this.player2.Score this.player2.Energy += v.CurEnergy v.CurEnergy = this.player2.Energy } // 广播消息 if err := this.module.SendMsgSyncToSession(string(this.module.GetType()), "operatorrst", &pb.EntertainOperatorRstPush{ Mpadata: szMap, Power: this.NexPower, Curpower: this.curPower, Score: curScore, Round: this.round, User1: this.player1, User2: this.player2, Itype: 0, Curid: oid1, Targetid: oid2, }, this.szSession...); err != nil { this.module.Errorln(err) } if this.round > this.MaxRound { // 游戏结束 if this.player1.Score == this.player2.Score { this.MaxRound += 1 // 增加一回合 } else { this.GameOver() } return } this.curPower = this.NexPower if len(this.szSession) == 1 && this.curPower == this.player2.Userinfo.Uid { this.AiOperator() } } func (this *Room) ReceiveMessage(session comm.IUserSession, stype string, msg proto.Message) (errdata *pb.ErrorData) { switch stype { case "operator": // 操作消息 var ( curScore int32 // 该次操作的得分 oid1 int32 // 唯一id oid2 int32 color int32 // 校验消除的颜色 bAddPs bool ) var szMap []*pb.MapData req := msg.(*pb.EntertainOperatorReq) if session.GetUserId() != this.curPower { // 校验是不是你的权限 errdata = &pb.ErrorData{ Code: pb.ErrorCode_EntertainNoPower, Title: pb.ErrorCode_EntertainNoPower.ToString(), } return } if this.curPower == this.player1.Userinfo.Uid { color = 1 } else { color = 2 } if req.Itype == 1 { //释放技能 // 能量校验 if color == 1 { // 玩家1 放技能 conf, err := this.module.configure.GetGameConsumeHero(this.player1.Cardid) if err != nil { errdata = &pb.ErrorData{ Code: pb.ErrorCode_EntertainNoHeroSkill, Title: pb.ErrorCode_EntertainNoHeroSkill.ToString(), } return } if this.player1.Energy >= conf.Skillload { this.player1.Energy = 0 // 清零 if _, m := this.chessboard.SkillUp(req.Curid, color, conf.Skilleffect, conf.Skillvalue, true); len(m) > 0 { szMap = append(szMap, m...) } else { szMap = append(szMap, &pb.MapData{ Data: this.chessboard.GetPalatData(), }) } for _, v := range szMap { curScore += v.CurSocre this.player1.Score += v.CurSocre v.CurSocre = this.player1.Score } } else { errdata = &pb.ErrorData{ Code: pb.ErrorCode_EntertainNoEnergy, Title: pb.ErrorCode_EntertainNoEnergy.ToString(), } return } this.NexPower = this.curPower } else { conf, err := this.module.configure.GetGameConsumeHero(this.player2.Cardid) if err != nil { errdata = &pb.ErrorData{ Code: pb.ErrorCode_EntertainNoHeroSkill, Title: pb.ErrorCode_EntertainNoHeroSkill.ToString(), } return } if this.player2.Energy >= conf.Skillload { this.player2.Energy = 0 // 清零 if _, m := this.chessboard.SkillUp(0, color, conf.Skilleffect, conf.Skillvalue, true); len(m) > 0 { szMap = append(szMap, m...) } else { szMap = append(szMap, &pb.MapData{ Data: this.chessboard.GetPalatData(), }) } for _, v := range szMap { curScore += v.CurSocre this.player2.Score += v.CurSocre v.CurSocre = this.player2.Score } } else { errdata = &pb.ErrorData{ Code: pb.ErrorCode_EntertainNoEnergy, Title: pb.ErrorCode_EntertainNoEnergy.ToString(), } return } this.NexPower = this.curPower } if err := this.module.SendMsgSyncToSession(string(this.module.GetType()), "operatorrst", &pb.EntertainOperatorRstPush{ Mpadata: szMap, Power: this.NexPower, Curpower: this.curPower, Score: curScore, Round: this.round, User1: this.player1, User2: this.player2, Itype: req.Itype, Curid: oid1, Targetid: oid2, }, this.szSession...); err != nil { this.module.Errorln(err) } return } if req.Itype == 0 && req.Curid == 0 && req.Targetid == 0 { if this.curPower == this.player1.Userinfo.Uid { this.NexPower = this.player2.Userinfo.Uid this.player2.Ps = 2 this.player1.Ps = 0 } if this.curPower == this.player2.Userinfo.Uid { this.NexPower = this.player1.Userinfo.Uid this.player1.Ps = 2 this.player2.Ps = 0 } } else { bCanChange := false oid1 = this.chessboard.Plat[req.Curid].Oid oid2 = this.chessboard.Plat[req.Targetid].Oid if b, m := this.chessboard.CheckSpecialElemChange(req.Curid, req.Targetid, color); !b { // 交换元素 if b = this.chessboard.SwapGirde(req.Curid, req.Targetid); !b { // 交换格子 errdata = &pb.ErrorData{ Code: pb.ErrorCode_EntertainCantSwap, // 不能交换 直接返回 Title: pb.ErrorCode_EntertainCantSwap.ToString(), } return } } else { bCanChange = true szMap = append(szMap, m...) } if m, b := this.chessboard.CheckMap(color, true); len(m) > 0 { szMap = append(szMap, m...) bAddPs = b bCanChange = true } if !bCanChange { // 不能消除 this.chessboard.SwapGirde(req.Targetid, req.Curid) // 换到原来的位置 errdata = &pb.ErrorData{ Code: pb.ErrorCode_EntertainCantSwap, // 不能交换 直接返回 Title: pb.ErrorCode_EntertainCantSwap.ToString(), } return } // 校验下次是不是消除 if !this.chessboard.CheckAndRefreshPlat() { this.chessboard.RedsetPlatData() szMap = append(szMap, &pb.MapData{ Data: this.chessboard.GetPalatData(), ChangeType: 1, }) } if this.curPower == this.player1.Userinfo.Uid { //权限校验 this.player1.Score += curScore this.player1.Ps-- if bAddPs { this.player1.Ps++ if this.player1.Ps > MaxPs { this.player1.Ps = MaxPs } } if this.player1.Ps <= 0 { this.NexPower = this.player2.Userinfo.Uid this.player2.Ps = MaxPs } } else { // this.curPower == this.player2.Uid this.player2.Score += curScore this.player2.Ps-- if bAddPs { this.player2.Ps++ if this.player2.Ps > MaxPs { this.player2.Ps = MaxPs } } if this.player2.Ps <= 0 { this.NexPower = this.player1.Userinfo.Uid this.player1.Ps = MaxPs } } for _, v := range szMap { // curScore += v.CurSocre if color == 1 { this.player1.Score += v.CurSocre v.CurSocre = this.player1.Score this.player1.Energy += v.CurEnergy v.CurEnergy = this.player1.Energy } else { this.player2.Score += v.CurSocre v.CurSocre = this.player2.Score this.player2.Energy += v.CurEnergy v.CurEnergy = this.player2.Energy } } } if this.NexPower == this.player1.Userinfo.Uid && this.NexPower != this.curPower { this.round++ } // 广播消息 if err := this.module.SendMsgSyncToSession(string(this.module.GetType()), "operatorrst", &pb.EntertainOperatorRstPush{ Mpadata: szMap, Power: this.NexPower, Curpower: this.curPower, Score: curScore, Round: this.round, User1: this.player1, User2: this.player2, Itype: req.Itype, Curid: oid1, Targetid: oid2, }, this.szSession...); err != nil { this.module.Errorln(err) } if this.round > this.MaxRound { // 游戏结束 if this.player1.Score == this.player2.Score { this.MaxRound += 1 // 增加一回合 } else { this.GameOver() } return } // 变更权限 this.curPower = this.NexPower case "ready": var bStartGame bool // 可以开始游戏 this.NexPower = this.player1.Userinfo.Uid this.curPower = this.player1.Userinfo.Uid this.player1.Ps = MaxPs this.player2.Ps = 0 if len(this.szSession) == 1 { // AI对战的话直接开始游戏 bStartGame = true } else { if this.player1.Userinfo.Uid == session.GetUserId() { this.rd1 = true } else if this.player2.Userinfo.Uid == session.GetUserId() { this.rd2 = true } if this.player1.Userinfo.Uid != session.GetUserId() || this.RoomType != 1 { // 房主 if err := this.module.SendMsgSyncToSession(string(this.module.GetType()), "gameready", &pb.EntertainGameReadyPush{ P1Ready: this.rd1, P2Ready: this.rd2, }, this.szSession...); err != nil { this.module.Errorln(err) } } if this.rd1 && this.rd2 { // 两个玩家都准备好了 那么就开始游戏 bStartGame = true this.rd1 = false this.rd2 = false } } if bStartGame { // 初始化规则相关 if len(this.szSession) == 1 { this.MaxTime = this.module.ModuleTools.GetGlobalConf().ConsumePveTime } else { this.MaxTime = this.module.ModuleTools.GetGlobalConf().ConsumePvpTime } this.Status = 1 this.round = 1 if err := this.module.SendMsgSyncToSession(string(this.module.GetType()), "startgame", &pb.EntertainStartGamePush{ User1: this.player1, User2: this.player2, Mpadata: &pb.MapData{Data: this.chessboard.Plat}, Power: this.NexPower, Round: this.round, Roomid: this.Id, // 房间ID Playtype: this.Playtype, // 通过权重随机一个玩法 }, this.szSession...); err != nil { this.module.Errorln(err) } this.ModifyUserRoomInfoData(true) // 游戏开始开启一个定时器 1小时如果还不结束 自动清理 this.closeRoomTimer = timewheel.Add(time.Hour, this.CloseRoomTimeOut) } case "reconnect": // 重连 session.SendMsg(string(this.module.GetType()), "reconnect", &pb.EntertainReconnectResp{ Roomid: this.Id, Mpadata: &pb.MapData{ Data: this.chessboard.GetPalatData(), CurSocre: 0, CurEnergy: 0, }, Power: this.NexPower, Curpower: this.curPower, Score: 0, Round: this.round, User1: this.player1, User2: this.player2, }) case "dissolve": // 房主解散房间 req := msg.(*pb.EntertainDissolveRoomReq) if this.player1.Userinfo.Uid != req.Roomid { errdata = &pb.ErrorData{ Code: pb.ErrorCode_EntertainNotMaster, // 不是房主 Title: pb.ErrorCode_EntertainNotMaster.ToString(), } return } // 游戏中不允许解散 if this.Status == 1 { errdata = &pb.ErrorData{ Code: pb.ErrorCode_EntertainDissolveFailed, // 游戏中不能解散 Title: pb.ErrorCode_EntertainDissolveFailed.ToString(), } return } if err := this.module.SendMsgSyncToSession(string(this.module.GetType()), "dissolvemasterroom", &pb.EntertainDissolveMasterRoomPush{ Roomid: this.Id, }, this.szSession...); err != nil { this.module.Errorln(err) } this.ModifyUserRoomInfoData(false) case "operatorover": // 操作完成 开启一个定时器 if this.player1.Userinfo.Uid == session.GetUserId() { this.rd1 = true } else if this.player2.Userinfo.Uid == session.GetUserId() { this.rd2 = true } if len(this.szSession) == 1 { if err := this.module.SendMsgSyncToSession(string(this.module.GetType()), "startimer", &pb.EntertainStarTimerPush{ Time: this.MaxTime, Power: this.curPower, }, this.szSession...); err != nil { this.module.Errorln(err) } if this.curPower == this.player2.Userinfo.Uid { this.AiOperator() } } else { if this.rd1 && this.rd2 { // 两个玩家都准备好了 那么就开始游戏 this.rd1 = false this.rd2 = false if err := this.module.SendMsgSyncToSession(string(this.module.GetType()), "startimer", &pb.EntertainStarTimerPush{ Time: this.MaxTime, Power: this.curPower, }, this.szSession...); err != nil { this.module.Errorln(err) } } } //this.operatetimer = timewheel.Add(time.Second*time.Duration(this.MaxTime), this.operateTimeOut) case "offline": req := msg.(*pb.RPCGeneralReqA2) fmt.Printf("useroffline: %v\n", req) if this.RoomType == 2 { // AI 对战直接结束 this.ModifyUserRoomInfoData(false) return } } return } // 游戏结束 func (this *Room) GameOver() (errdata *pb.ErrorData) { var ( atno []*pb.UserAtno winindex int32 bReward bool res []*cfg.Gameatn winner *pb.PlayerData lostPlayer *pb.PlayerData // 输的玩家 box *pb.BoxData // 是否可以获得宝箱奖励 ) winner = this.player1 bReward = true if this.player1.Score < this.player2.Score { winner = this.player2 winindex = 1 if len(this.szSession) == 1 { // 赢家是AI 的话不发奖 bReward = false } } if bReward { // 发奖 if user, err := this.module.ModuleUser.GetUser(winner.Userinfo.Uid); err == nil { if conf, err := this.module.configure.GetGameConsumeintegral(user.Consumeexp); err == nil { //res = append(res, conf.Onereward...) 这奖励手动领取 res = append(res, conf.Rewards...) for _, v := range res { if v.A == "attr" && v.T == "consumeexp" { if winner.Userinfo.Uid == this.player1.Userinfo.Uid { this.player1.Consumeexp += v.N } else { this.player2.Consumeexp += v.N } } } if errdata, atno = this.module.DispenseAtno(this.szSession[winindex], res, true); errdata != nil { return } this.szSession[winindex].Push() } } } // 失败的一方扣除卡 if this.player1 != winner { lostPlayer = this.player1 } else { lostPlayer = this.player2 } // 失败卡类型 if conf, err := this.module.configure.GetGameConsumeHero(lostPlayer.Cardid); err == nil && lostPlayer.Userinfo.Uid != "999" { if conf.Type != 1 { //卡片类型不为1 if list, err := this.module.model.getEntertainmList(lostPlayer.Userinfo.Uid); err == nil { if list.Card[lostPlayer.Cardid] > 1 { list.Card[lostPlayer.Cardid] -= 1 this.module.model.modifyEntertainmList(lostPlayer.Userinfo.Uid, map[string]interface{}{ "card": list.Card, }) } } } } // 查看能不能获得箱子 if list, err := this.module.model.getEntertainmList(winner.Userinfo.Uid); err == nil { if len(list.Box) < 3 { // 可以获得一个宝箱 if c, err := this.module.configure.GetGameRandomConsumeBoxConf(); err == nil { if c.Color != 0 { box = &pb.BoxData{ Boxid: c.Boxid, Opentime: int64(c.Cd) + configure.Now().Unix(), } list.Box = append(list.Box, box) this.module.model.modifyEntertainmList(winner.Userinfo.Uid, map[string]interface{}{ "box": list.Box, }) } } } } // 修改房间状态 this.Status = 2 this.module.SendMsgSyncToSession(string(this.module.GetType()), "gameover", &pb.EntertainGameOverPush{ User1: this.player1, User2: this.player2, Mpadata: &pb.MapData{ Data: this.chessboard.Plat, }, Power: "", // 清理权限 Round: this.round, Win: winner.Userinfo.Uid, Reward: atno, Box: box, }, this.szSession...) this.ModifyUserRoomInfoData(false) if this.closeRoomTimer != nil { // 游戏结束 清理定时器 timewheel.Remove(this.closeRoomTimer) this.closeRoomTimer = nil } return } // 加入房间 func (this *Room) JoinRoom(module *Entertainment, p *pb.PlayerData) (room *Room, err error) { // 该房间是个空的 if this.player1 == nil { this.player1 = p this.module = module this.chessboard = new(MapData) this.Playtype = this.RandomPlayType() this.chessboard.InitMap(module, this.Playtype) // 初始化棋盘 if s1, ok := this.module.GetUserSession(p.Userinfo.Uid); !ok { this.module.PutUserSession(s1) } else { this.szSession = append(this.szSession, s1.Clone()) } this.Id = p.Userinfo.Uid // 房主ID 作为房间id this.RoomType = 1 this.MaxRound = this.module.ModuleTools.GetGlobalConf().ConsumeRounds room = this } else if this.player2 == nil { //异常处理 if this.player1.Userinfo.Uid == p.Userinfo.Uid { err = errors.New("重复加入") } this.player2 = p // 玩家2 赋值 if s1, ok := this.module.GetUserSession(p.Userinfo.Uid); !ok { this.module.PutUserSession(s1) } else { this.szSession = append(this.szSession, s1.Clone()) } // 推送消息 if err := this.module.SendMsgSyncToSession(string(this.module.GetType()), "joincreateroom", &pb.EntertainJoinCreateRoomPush{ Roomid: this.Id, User1: this.player1, User2: this.player2, }, this.szSession...); err != nil { this.module.Errorln(err) } } else { // 房间满了 加不进来 err = errors.New("房间已满") return } return } // 修改玩家的房间信息 func (this *Room) ModifyUserRoomInfoData(bStart bool) { var ( roomid string path string ) if bStart { roomid = this.Id path = fmt.Sprintf("%s/%s", this.module.service.GetType(), this.module.service.GetId()) } else { event.TriggerEvent(comm.EventCloseRoom, this.Id) // 释放房间 } // 修改房间记录信息 if this.player1 != nil && this.player1.Userinfo != nil { this.module.model.modifyEntertainmList(this.player1.Userinfo.Uid, map[string]interface{}{ "roomid": roomid, "servicePath": path, }) } if this.RoomType != 2 && this.player2 != nil && this.player2.Userinfo != nil { this.module.model.modifyEntertainmList(this.player2.Userinfo.Uid, map[string]interface{}{ "roomid": roomid, "servicePath": path, }) } return }