From dee9fea3101fc0be8453b6ecd93100a24d1b5ec3 Mon Sep 17 00:00:00 2001 From: UnKownOwO <80520429@qq.com> Date: Sat, 7 Jan 2023 20:02:59 +0800 Subject: [PATCH] =?UTF-8?q?GCG=E6=B5=81=E7=A8=8B=E8=87=B3=E6=B8=B8?= =?UTF-8?q?=E6=88=8F=E4=B8=BB=E7=95=8C=E9=9D=A2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- gs/game/gcg_ai.go | 25 +- gs/game/gcg_manager.go | 432 +++++++++++++++++++-------- gs/game/user_gcg.go | 91 +++--- protocol/cmd/cmd_id_proto_obj_map.go | 1 + 4 files changed, 368 insertions(+), 181 deletions(-) diff --git a/gs/game/gcg_ai.go b/gs/game/gcg_ai.go index 032538f6..bc8d4ea6 100644 --- a/gs/game/gcg_ai.go +++ b/gs/game/gcg_ai.go @@ -3,7 +3,6 @@ package game import ( "hk4e/pkg/logger" "hk4e/protocol/proto" - "time" ) type GCGAi struct { @@ -29,13 +28,23 @@ func (g *GCGAi) ReceiveGCGMessagePackNotify(notify *proto.GCGMessagePackNotify) switch msg.AfterPhase { case proto.GCGPhaseType_GCG_PHASE_TYPE_ON_STAGE: logger.Error("请选择你的英雄 hhh") - go func() { - time.Sleep(1000 * 3) - // 默认选第一张牌 - cardInfo := gameController.cardList[0] - // 操控者选择角色牌 - g.game.ControllerSelectChar(gameController, cardInfo, []uint32{}) - }() + // 默认选第一张牌 + cardInfo := gameController.cardList[0] + // 操控者选择角色牌 + g.game.ControllerSelectChar(gameController, cardInfo, []uint32{}) + } + case *proto.GCGMessage_DiceRoll: + // 摇完骰子 + msg := message.GetPhaseChange() + switch msg.AfterPhase { + case proto.GCGPhaseType_GCG_PHASE_TYPE_ON_STAGE: + logger.Error("战斗意识?!") + cardInfo1 := g.game.controllerMap[g.controllerId].cardList[0] + cardInfo2 := g.game.controllerMap[g.controllerId].cardList[1] + g.game.AddMsgPack(0, proto.GCGActionType_GCG_ACTION_TYPE_NONE, g.game.GCGMsgPVEIntention(&proto.GCGMsgPVEIntention{CardGuid: cardInfo1.guid, SkillIdList: []uint32{cardInfo1.skillIdList[1]}}, &proto.GCGMsgPVEIntention{CardGuid: cardInfo2.guid, SkillIdList: []uint32{cardInfo2.skillIdList[0]}})) + g.game.SendAllMsgPack() + g.game.SetControllerAllow(g.game.controllerMap[g.controllerId], false, true) + g.game.AddMsgPack(0, proto.GCGActionType_GCG_ACTION_TYPE_SEND_MESSAGE, g.game.GCGMsgPhaseContinue()) } } } diff --git a/gs/game/gcg_manager.go b/gs/game/gcg_manager.go index 807635cb..6f06515c 100644 --- a/gs/game/gcg_manager.go +++ b/gs/game/gcg_manager.go @@ -24,6 +24,7 @@ const ( type GCGCardInfo struct { cardId uint32 // 卡牌Id guid uint32 // 唯一Id + controllerId uint32 // 拥有它的操控者 faceType uint32 // 卡面类型 tagList []uint32 // Tag tokenMap map[uint32]uint32 // Token @@ -32,6 +33,35 @@ type GCGCardInfo struct { isShow bool // 是否展示 } +func (g *GCGCardInfo) ToProto() *proto.GCGCard { + gcgCard := &proto.GCGCard{ + TagList: g.tagList, + Guid: g.guid, + IsShow: g.isShow, + TokenList: make([]*proto.GCGToken, 0, len(g.tokenMap)), + FaceType: g.faceType, + SkillIdList: g.skillIdList, + SkillLimitsList: make([]*proto.GCGSkillLimitsInfo, 0, len(g.skillLimitList)), + Id: g.cardId, + ControllerId: g.controllerId, + } + // Token + for k, v := range g.tokenMap { + gcgCard.TokenList = append(gcgCard.TokenList, &proto.GCGToken{ + Value: v, + Key: k, + }) + } + // TODO SkillLimitsList + for _, skillId := range g.skillLimitList { + gcgCard.SkillLimitsList = append(gcgCard.SkillLimitsList, &proto.GCGSkillLimitsInfo{ + SkillId: skillId, + LimitsList: nil, // TODO 技能限制列表 + }) + } + return gcgCard +} + type ControllerLoadState uint8 const ( @@ -64,12 +94,20 @@ func (g *GCGController) GetCardByGuid(cardGuid uint32) *GCGCardInfo { // GCGManager 七圣召唤管理器 type GCGManager struct { - gameMap map[uint32]*GCGGame // 游戏列表 uint32 -> guid - gameGuidCounter uint32 // 游戏guid生成计数器 + phaseFuncMap map[proto.GCGPhaseType]func(game *GCGGame) // 游戏阶段处理 + gameMap map[uint32]*GCGGame // 游戏列表 uint32 -> guid + gameGuidCounter uint32 // 游戏guid生成计数器 } func NewGCGManager() *GCGManager { gcgManager := new(GCGManager) + gcgManager.phaseFuncMap = map[proto.GCGPhaseType]func(game *GCGGame){ + proto.GCGPhaseType_GCG_PHASE_TYPE_START: gcgManager.PhaseStart, + proto.GCGPhaseType_GCG_PHASE_TYPE_DRAW: gcgManager.PhaseDraw, + proto.GCGPhaseType_GCG_PHASE_TYPE_DICE: gcgManager.PhaseRollDice, + proto.GCGPhaseType_GCG_PHASE_TYPE_PRE_MAIN: gcgManager.PhasePreMain, + proto.GCGPhaseType_GCG_PHASE_TYPE_MAIN: gcgManager.PhaseMain, + } gcgManager.gameMap = make(map[uint32]*GCGGame) return gcgManager } @@ -97,52 +135,24 @@ func (g *GCGManager) CreateGame(gameId uint32, playerList []*model.Player) *GCGG return game } -type GCGGameState uint8 - -const ( - GCGGameState_None GCGGameState = iota - GCGGameState_Waiting // 等待玩家加载 - GCGGameState_Running // 游戏运行中 - GCGGameState_Stoped // 游戏已结束 -) - -// 阶段对应的处理函数 -var phaseFuncMap = map[proto.GCGPhaseType]func(game *GCGGame){ - proto.GCGPhaseType_GCG_PHASE_TYPE_START: PhaseStartReady, - proto.GCGPhaseType_GCG_PHASE_TYPE_ON_STAGE: PhaseSelectChar, - proto.GCGPhaseType_GCG_PHASE_TYPE_DICE: PhaseRollDice, - proto.GCGPhaseType_GCG_PHASE_TYPE_PRE_MAIN: PhasePreMain, +// PhaseStart 阶段开始 +func (g *GCGManager) PhaseStart(game *GCGGame) { + // 设置除了先手的玩家不允许操控 + game.SetExceptControllerAllow(game.roundInfo.firstController, false, true) + // 游戏跳过阶段消息包 + game.AddMsgPack(0, proto.GCGActionType_GCG_ACTION_TYPE_SEND_MESSAGE, game.GCGMsgPhaseContinue()) + // 等待玩家进入 } -// PhaseStartReady 阶段开局准备 -func PhaseStartReady(game *GCGGame) { - // 客户端更新操控者 - game.AddMsgPack(0, proto.GCGActionType_GCG_ACTION_TYPE_NONE, game.GCGMsgUpdateController()) - // 分配先手 - game.AddMsgPack(0, proto.GCGActionType_GCG_ACTION_TYPE_PHASE_EXIT, game.GCGMsgClientPerform(proto.GCGClientPerformType_GCG_CLIENT_PERFORM_TYPE_FIRST_HAND, []uint32{game.roundInfo.firstController})) - // 游戏绘制卡牌阶段 应该 - game.GameChangePhase(proto.GCGPhaseType_GCG_PHASE_TYPE_DRAW) +// PhaseDraw 阶段抽取手牌 +func (g *GCGManager) PhaseDraw(game *GCGGame) { + // TODO 新手教程关不抽手牌 // 游戏选择角色卡牌阶段 - game.GameChangePhase(proto.GCGPhaseType_GCG_PHASE_TYPE_ON_STAGE) -} - -// PhaseSelectChar 阶段选择角色卡牌 -func PhaseSelectChar(game *GCGGame) { - // 该阶段确保每位玩家都选择了角色 - for _, controller := range game.controllerMap { - if controller.selectedCharCard == nil { - // 如果有没选择角色卡牌的则不执行后面 - return - } - } - // 回合信息 - game.AddMsgPack(0, proto.GCGActionType_GCG_ACTION_TYPE_SEND_MESSAGE, game.GCGMsgDuelDataChange()) - // 游戏投掷骰子阶段 - game.GameChangePhase(proto.GCGPhaseType_GCG_PHASE_TYPE_DICE) + game.ChangePhase(proto.GCGPhaseType_GCG_PHASE_TYPE_ON_STAGE) } // PhaseRollDice 阶段投掷骰子 -func PhaseRollDice(game *GCGGame) { +func (g *GCGManager) PhaseRollDice(game *GCGGame) { // 给每位玩家投掷骰子 for _, controller := range game.controllerMap { diceSideList := make([]proto.GCGDiceSideType, 0, 8) @@ -156,23 +166,38 @@ func PhaseRollDice(game *GCGGame) { game.roundInfo.diceSideMap[controller.controllerId] = diceSideList game.AddMsgPack(controller.controllerId, proto.GCGActionType_GCG_ACTION_TYPE_ROLL, game.GCGMsgDiceRoll(controller.controllerId, uint32(len(diceSideList)), diceSideList)) } - game.GameChangePhase(proto.GCGPhaseType_GCG_PHASE_TYPE_REROLL) - // g.controllerMap[1].allow = 1 - // g.controllerMap[2].allow = 0 - // msgPackInfo := g.CreateMsgPackInfo() - // msgPackInfo.AddMsgPack(0, proto.GCGActionType_GCG_ACTION_TYPE_NONE, g.GCGMsgPVEIntention(&proto.GCGMsgPVEIntention{CardGuid: g.controllerMap[2].cardList[0].guid, SkillIdList: []uint32{g.controllerMap[2].cardList[0].skillIdList[1]}}, &proto.GCGMsgPVEIntention{CardGuid: g.controllerMap[2].cardList[1].guid, SkillIdList: []uint32{g.controllerMap[2].cardList[1].skillIdList[0]}})) - // g.BroadcastMsgPackInfo(msgPackInfo, false) - // msgPackInfo1 := g.CreateMsgPackInfo() - // msgPackInfo1.AddMsgPack(0, proto.GCGActionType_GCG_ACTION_TYPE_NONE, g.GCGMsgUpdateController()) - // msgPackInfo1.AddMsgPack(0, proto.GCGActionType_GCG_ACTION_TYPE_SEND_MESSAGE, g.GCGMsgPhaseContinue()) - // g.BroadcastMsgPackInfo(msgPackInfo1, false) + // 等待玩家确认重投骰子 } // PhasePreMain 阶段战斗开始 -func PhasePreMain(game *GCGGame) { +func (g *GCGManager) PhasePreMain(game *GCGGame) { + // TODO 使用技能完善 game.AddMsgPack(0, proto.GCGActionType_GCG_ACTION_TYPE_TRIGGER_SKILL, game.GCGMsgUseSkill(195, 33024), game.GCGMsgUseSkillEnd(195, 33024)) + // 游戏行动阶段 + game.ChangePhase(proto.GCGPhaseType_GCG_PHASE_TYPE_MAIN) } +// PhaseMain 阶段行动 +func (g *GCGManager) PhaseMain(game *GCGGame) { + // 消耗费用信息 + for _, controller := range game.controllerMap { + if controller.player == nil { + continue + } + game.AddMsgPack(0, proto.GCGActionType_GCG_ACTION_TYPE_NOTIFY_COST, game.GCGMsgCostRevise(controller)) + GAME_MANAGER.SendMsg(cmd.GCGSkillPreviewNotify, controller.player.PlayerID, controller.player.ClientSeq, GAME_MANAGER.PacketGCGSkillPreviewNotify(controller)) + } +} + +type GCGGameState uint8 + +const ( + GCGGameState_None GCGGameState = iota + GCGGameState_Waiting // 等待玩家加载 + GCGGameState_Running // 游戏运行中 + GCGGameState_Stoped // 游戏已结束 +) + // GCGRoundInfo 游戏对局回合信息 type GCGRoundInfo struct { roundNum uint32 // 游戏当前回合数 @@ -193,6 +218,7 @@ type GCGGame struct { roundInfo *GCGRoundInfo // 游戏回合信息 controllerMap map[uint32]*GCGController // 操控者列表 uint32 -> controllerId msgPackList []*proto.GCGMessagePack // 消息包待发送区 + isLastMsgPack bool // 是否为阶段切换的最后一个消息包 用于粘包模拟官服效果 historyMsgPackList []*proto.GCGMessagePack // 历史消息包列表 historyCardList []*GCGCardInfo // 历史卡牌列表 } @@ -205,6 +231,7 @@ func (g *GCGGame) AddPlayer(player *model.Player) { controllerId: g.controllerIdCounter, cardList: make([]*GCGCardInfo, 0, 50), loadState: ControllerLoadState_None, + allow: 1, controllerType: ControllerType_Player, player: player, } @@ -224,6 +251,7 @@ func (g *GCGGame) AddAI() { controllerId: g.controllerIdCounter, cardList: make([]*GCGCardInfo, 0, 50), loadState: ControllerLoadState_InitFinish, + allow: 1, controllerType: ControllerType_AI, ai: &GCGAi{ game: g, @@ -237,26 +265,6 @@ func (g *GCGGame) AddAI() { g.controllerMap[g.controllerIdCounter] = controller } -// GameChangePhase 游戏更改阶段 -func (g *GCGGame) GameChangePhase(phase proto.GCGPhaseType) { - beforePhase := g.roundInfo.phaseType - // 修改游戏的阶段 - g.roundInfo.phaseType = phase - switch phase { - case proto.GCGPhaseType_GCG_PHASE_TYPE_ON_STAGE, proto.GCGPhaseType_GCG_PHASE_TYPE_DICE: - // 该阶段允许所有玩家行动 - for _, controller := range g.controllerMap { - controller.allow = 1 - } - case proto.GCGPhaseType_GCG_PHASE_TYPE_PRE_MAIN: - // 该阶段不允许所有玩家行动 - for _, controller := range g.controllerMap { - controller.allow = 0 - } - } - g.AddMsgPack(0, proto.GCGActionType_GCG_ACTION_TYPE_NEXT_PHASE, g.GCGMsgPhaseChange(beforePhase, phase)) -} - // GiveCharCard 给予操控者角色卡牌 func (g *GCGGame) GiveCharCard(controller *GCGController, charId uint32) { // 读取角色卡牌配置表 @@ -267,11 +275,12 @@ func (g *GCGGame) GiveCharCard(controller *GCGController, charId uint32) { } // 生成卡牌信息 g.cardGuidCounter++ - controller.cardList = append(controller.cardList, &GCGCardInfo{ - cardId: charId, - guid: g.cardGuidCounter, - faceType: 0, // 1为金卡 - tagList: gcgCharConfig.TagList, + cardInfo := &GCGCardInfo{ + cardId: charId, + guid: g.cardGuidCounter, + controllerId: controller.controllerId, + faceType: 0, // 1为金卡 + tagList: gcgCharConfig.TagList, tokenMap: map[uint32]uint32{ 1: uint32(gcgCharConfig.HPBase), // 血量 2: uint32(gcgCharConfig.HPBase), // 最大血量(不确定) @@ -281,7 +290,114 @@ func (g *GCGGame) GiveCharCard(controller *GCGController, charId uint32) { skillIdList: gcgCharConfig.SkillList, skillLimitList: []uint32{}, isShow: true, - }) + } + controller.cardList = append(controller.cardList, cardInfo) + // 添加历史卡牌 + g.historyCardList = append(g.historyCardList, cardInfo) +} + +// ChangePhase 游戏更改阶段 +func (g *GCGGame) ChangePhase(phase proto.GCGPhaseType) { + beforePhase := g.roundInfo.phaseType + // 修改游戏的阶段 + g.roundInfo.phaseType = phase + // 改变阶段覆盖掉上层可能有的true + g.isLastMsgPack = false + + // 操控者允许操作列表 + allowControllerMap := make([]*proto.Uint32Pair, 0, len(g.controllerMap)) + + // 根据阶段改变操控者允许状态 + switch phase { + case proto.GCGPhaseType_GCG_PHASE_TYPE_ON_STAGE, proto.GCGPhaseType_GCG_PHASE_TYPE_DICE: + // 该阶段允许所有操控者操作 + g.SetAllControllerAllow(true, false) + + for _, controller := range g.controllerMap { + pair := &proto.Uint32Pair{ + Key: controller.controllerId, + Value: controller.allow, + } + allowControllerMap = append(allowControllerMap, pair) + } + case proto.GCGPhaseType_GCG_PHASE_TYPE_MAIN: + // 行动阶段仅允许先手者操作 + for _, controller := range g.controllerMap { + // 跳过不是先手的操控者 + if controller.controllerId != g.roundInfo.firstController { + continue + } + g.SetControllerAllow(controller, true, false) + pair := &proto.Uint32Pair{ + Key: controller.controllerId, + Value: controller.allow, + } + allowControllerMap = append(allowControllerMap, pair) + } + } + + // 游戏下一阶段切换消息包 + g.AddMsgPack(0, proto.GCGActionType_GCG_ACTION_TYPE_NEXT_PHASE, g.GCGMsgPhaseChange(beforePhase, phase, allowControllerMap)) + + // 执行阶段处理前假装现在是最后一个阶段处理 + g.isLastMsgPack = true + + // 执行下一阶段 + phaseFunc, ok := GCG_MANAGER.phaseFuncMap[g.roundInfo.phaseType] + // 确保该阶段有进行处理的函数 + if ok { + phaseFunc(g) // 进行该阶段的处理 + } + + // 如果阶段里不嵌套处理别的阶段了就在此发送消息包 + // 总之就是确保发送的时候为最后一个阶段变更 + if g.isLastMsgPack { + // 发送阶段处理后的消息包 + g.SendAllMsgPack() + } +} + +// SetExceptControllerAllow 设置除了指定的操控者以外的是否允许操作 +func (g *GCGGame) SetExceptControllerAllow(controllerId uint32, isAllow bool, isAddMsg bool) { + for _, controller := range g.controllerMap { + if controller.controllerId == controllerId { + continue + } + g.SetControllerAllow(controller, isAllow, false) + } + // 是否添加消息包 + if isAddMsg { + // 更新客户端操控者允许状态消息包 + g.AddMsgPack(0, proto.GCGActionType_GCG_ACTION_TYPE_NONE, g.GCGMsgUpdateController()) + } +} + +// SetAllControllerAllow 设置全部操控者是否允许操作 +func (g *GCGGame) SetAllControllerAllow(isAllow bool, isAddMsg bool) { + for _, controller := range g.controllerMap { + g.SetControllerAllow(controller, isAllow, false) + } + // 是否添加消息包 + if isAddMsg { + // 更新客户端操控者允许状态消息包 + g.AddMsgPack(0, proto.GCGActionType_GCG_ACTION_TYPE_NONE, g.GCGMsgUpdateController()) + } +} + +// SetControllerAllow 设置操控者是否允许操作 +func (g *GCGGame) SetControllerAllow(controller *GCGController, isAllow bool, isAddMsg bool) { + // allow 0 -> 不允许 1 -> 允许 + // 当然这是我个人理解 可能有出入 + if isAllow { + controller.allow = 1 + } else { + controller.allow = 0 + } + // 是否添加消息包 + if isAddMsg { + // 更新客户端操控者允许状态消息包 + g.AddMsgPack(0, proto.GCGActionType_GCG_ACTION_TYPE_NONE, g.GCGMsgUpdateController()) + } } // ControllerSelectChar 操控者选择角色卡牌 @@ -294,11 +410,41 @@ func (g *GCGGame) ControllerSelectChar(controller *GCGController, cardInfo *GCGC // TODO 消耗骰子点数 // 设置角色卡牌 controller.selectedCharCard = cardInfo - // 设置操控者禁止操作 - controller.allow = 0 + + // 设置玩家禁止操作 + g.SetControllerAllow(controller, false, true) + // 广播选择的角色卡牌消息包 - g.AddMsgPack(0, proto.GCGActionType_GCG_ACTION_TYPE_NONE, g.GCGMsgUpdateController()) g.AddMsgPack(controller.controllerId, proto.GCGActionType_GCG_ACTION_TYPE_SELECT_ONSTAGE, g.GCGMsgSelectOnStage(controller.controllerId, cardInfo.guid, proto.GCGReason_GCG_REASON_DEFAULT)) + + // 该阶段确保每位玩家都选择了角色牌 + isAllSelectedChar := true + for _, controller := range g.controllerMap { + if controller.selectedCharCard == nil { + isAllSelectedChar = false + } + } + // 如果有玩家未选择角色牌不同处理 + if isAllSelectedChar { + // 回合信息 + g.AddMsgPack(0, proto.GCGActionType_GCG_ACTION_TYPE_SEND_MESSAGE, g.GCGMsgDuelDataChange()) + // 游戏投掷骰子阶段 + g.ChangePhase(proto.GCGPhaseType_GCG_PHASE_TYPE_DICE) + } else { + // 跳过该阶段 官服是这样的我也不知道为什么 + g.AddMsgPack(0, proto.GCGActionType_GCG_ACTION_TYPE_SEND_MESSAGE, g.GCGMsgPhaseContinue()) + + // 立刻发送消息包 模仿官服效果 + g.SendAllMsgPack() + } +} + +// ControllerReRollDice 操控者确认重投骰子 +func (g *GCGGame) ControllerReRollDice(controller *GCGController, diceIndexList []uint32) { + // 玩家禁止操作 + g.SetAllControllerAllow(false, true) + // 游戏战斗开始阶段 + g.ChangePhase(proto.GCGPhaseType_GCG_PHASE_TYPE_PRE_MAIN) } // onTick 游戏的Tick @@ -321,16 +467,6 @@ func (g *GCGGame) onTick() { GAME_MANAGER.SendMsg(cmd.GCGHeartBeatNotify, controller.player.PlayerID, controller.player.ClientSeq, gcgHeartBeatNotify) } } - // 仅在游戏处于运行状态时执行阶段处理 - if g.gameState == GCGGameState_Running { - phaseFunc, ok := phaseFuncMap[g.roundInfo.phaseType] - // 确保该阶段有进行处理的函数 - if ok { - phaseFunc(g) // 进行该阶段的处理 - // 发送阶段处理后的消息包 - g.SendAllMsgPack(false) - } - } g.gameTick++ } @@ -343,34 +479,27 @@ func (g *GCGGame) InitGame(playerList []*model.Player) { // 添加AI g.AddAI() - // // 先手允许操作 - // controller, ok := g.controllerMap[g.roundInfo.firstController] - // if !ok { - // logger.Error("controller is nil, controllerId: %v", g.roundInfo.firstController) - // return - // } - // controller.allow = 1 - - // TODO 验证玩家人数是否符合 - // 预开始游戏 - g.GameChangePhase(proto.GCGPhaseType_GCG_PHASE_TYPE_START) - g.AddMsgPack(0, proto.GCGActionType_GCG_ACTION_TYPE_NONE, g.GCGMsgUpdateController()) - g.AddMsgPack(0, proto.GCGActionType_GCG_ACTION_TYPE_SEND_MESSAGE, g.GCGMsgPhaseContinue()) - g.SendAllMsgPack(true) - // 游戏状态更改为等待玩家加载 g.gameState = GCGGameState_Waiting + + // TODO 验证玩家人数是否符合 + // 游戏开始阶段 + g.ChangePhase(proto.GCGPhaseType_GCG_PHASE_TYPE_START) } // StartGame 开始GCG游戏 func (g *GCGGame) StartGame() { - // 游戏开始设置所有玩家不允许操作 - // for _, c := range g.controllerMap { - // c.allow = 0 - // } - // 游戏状态更改为游戏运行中 g.gameState = GCGGameState_Running + + logger.Error("game running") + + // 游戏开始设置所有玩家不允许操作 + g.SetAllControllerAllow(false, true) + // 分配先手 + g.AddMsgPack(0, proto.GCGActionType_GCG_ACTION_TYPE_PHASE_EXIT, g.GCGMsgClientPerform(proto.GCGClientPerformType_GCG_CLIENT_PERFORM_TYPE_FIRST_HAND, []uint32{g.roundInfo.firstController})) + // 游戏抽取手牌阶段 + g.ChangePhase(proto.GCGPhaseType_GCG_PHASE_TYPE_DRAW) } // CheckAllInitFinish 检查所有玩家是否加载完成 @@ -407,13 +536,13 @@ func (g *GCGGame) AddMsgPack(controllerId uint32, actionType proto.GCGActionType } // SendAllMsgPack 发送所有待发送区的消息包 -func (g *GCGGame) SendAllMsgPack(recordOnly bool) { +func (g *GCGGame) SendAllMsgPack() { // 不发送空的消息包 if len(g.msgPackList) == 0 { return } - // 是否仅记录历史消息包 - if !recordOnly { + // 游戏不处于运行状态仅记录历史消息包 + if g.gameState == GCGGameState_Running { g.serverSeqCounter++ for _, controller := range g.controllerMap { GAME_MANAGER.SendGCGMessagePackNotify(controller, g.serverSeqCounter, g.msgPackList) @@ -428,21 +557,11 @@ func (g *GCGGame) SendAllMsgPack(recordOnly bool) { } // GCGMsgPhaseChange GCG消息阶段改变 -func (g *GCGGame) GCGMsgPhaseChange(beforePhase proto.GCGPhaseType, afterPhase proto.GCGPhaseType) *proto.GCGMessage { +func (g *GCGGame) GCGMsgPhaseChange(beforePhase proto.GCGPhaseType, afterPhase proto.GCGPhaseType, allowControllerMap []*proto.Uint32Pair) *proto.GCGMessage { gcgMsgPhaseChange := &proto.GCGMsgPhaseChange{ BeforePhase: beforePhase, AfterPhase: afterPhase, - AllowControllerMap: make([]*proto.Uint32Pair, 0, len(g.controllerMap)), - } - if gcgMsgPhaseChange.AfterPhase != proto.GCGPhaseType_GCG_PHASE_TYPE_DRAW && gcgMsgPhaseChange.AfterPhase != proto.GCGPhaseType_GCG_PHASE_TYPE_PRE_MAIN { - // 操控者的是否允许操作 - for _, controller := range g.controllerMap { - pair := &proto.Uint32Pair{ - Key: controller.controllerId, - Value: controller.allow, - } - gcgMsgPhaseChange.AllowControllerMap = append(gcgMsgPhaseChange.AllowControllerMap, pair) - } + AllowControllerMap: allowControllerMap, } gcgMessage := &proto.GCGMessage{ Message: &proto.GCGMessage_PhaseChange{ @@ -559,13 +678,13 @@ func (g *GCGGame) GCGMsgDiceRoll(controllerId uint32, diceNum uint32, diceSideLi // GCGMsgUseSkill GCG消息使用技能 func (g *GCGGame) GCGMsgUseSkill(cardGuid uint32, skillId uint32) *proto.GCGMessage { - gcgMsgMsgUseSkill := &proto.GCGMsgUseSkill{ + gcgMsgUseSkill := &proto.GCGMsgUseSkill{ SkillId: skillId, CardGuid: cardGuid, } gcgMessage := &proto.GCGMessage{ Message: &proto.GCGMessage_UseSkill{ - UseSkill: gcgMsgMsgUseSkill, + UseSkill: gcgMsgUseSkill, }, } return gcgMessage @@ -573,13 +692,64 @@ func (g *GCGGame) GCGMsgUseSkill(cardGuid uint32, skillId uint32) *proto.GCGMess // GCGMsgUseSkillEnd GCG消息使用技能结束 func (g *GCGGame) GCGMsgUseSkillEnd(cardGuid uint32, skillId uint32) *proto.GCGMessage { - gcgMsgMsgUseSkillEnd := &proto.GCGMsgUseSkillEnd{ + gcgMsgUseSkillEnd := &proto.GCGMsgUseSkillEnd{ SkillId: skillId, CardGuid: cardGuid, } gcgMessage := &proto.GCGMessage{ Message: &proto.GCGMessage_UseSkillEnd{ - UseSkillEnd: gcgMsgMsgUseSkillEnd, + UseSkillEnd: gcgMsgUseSkillEnd, + }, + } + return gcgMessage +} + +// GCGMsgCostRevise GCG消息消耗信息修改 +func (g *GCGGame) GCGMsgCostRevise(controller *GCGController) *proto.GCGMessage { + gcgMsgCostRevise := &proto.GCGMsgCostRevise{ + CostRevise: &proto.GCGCostReviseInfo{ + CanUseHandCardIdList: nil, + SelectOnStageCostList: make([]*proto.GCGSelectOnStageCostInfo, 0, 1), + PlayCardCostList: nil, + // 技能攻击消耗 + AttackCostList: make([]*proto.GCGAttackCostInfo, 0, len(controller.selectedCharCard.skillIdList)), + IsCanAttack: true, + }, + ControllerId: controller.controllerId, + } + for _, skillId := range controller.selectedCharCard.skillIdList { + gcgAttackCostInfo := &proto.GCGAttackCostInfo{ + CostMap: []*proto.Uint32Pair{ + { + Key: 10, + Value: 2, + }, + { + Key: 13, + Value: 1, + }, + }, + SkillId: skillId, + } + gcgMsgCostRevise.CostRevise.AttackCostList = append(gcgMsgCostRevise.CostRevise.AttackCostList, gcgAttackCostInfo) + } + for _, info := range controller.cardList { + if info.guid != controller.selectedCharCard.guid { + gcgSelectOnStageCostInfo := &proto.GCGSelectOnStageCostInfo{ + CardGuid: info.guid, + CostMap: []*proto.Uint32Pair{ + { + Key: 10, + Value: 1, + }, + }, + } + gcgMsgCostRevise.CostRevise.SelectOnStageCostList = append(gcgMsgCostRevise.CostRevise.SelectOnStageCostList, gcgSelectOnStageCostInfo) + } + } + gcgMessage := &proto.GCGMessage{ + Message: &proto.GCGMessage_CostRevise{ + CostRevise: gcgMsgCostRevise, }, } return gcgMessage diff --git a/gs/game/user_gcg.go b/gs/game/user_gcg.go index bf3e8fc0..774a713c 100644 --- a/gs/game/user_gcg.go +++ b/gs/game/user_gcg.go @@ -147,8 +147,7 @@ func (g *GameManager) GCGAskDuelReq(player *model.Player, payloadMsg pb.Message) TotalProgress: 0, }, }, - // TODO 创建完卡牌都记录到历史卡牌内 - HistoryCardList: nil, + HistoryCardList: make([]*proto.GCGCard, 0, len(game.historyCardList)), Round: game.roundInfo.roundNum, ControllerId: gameController.controllerId, HistoryMsgPackList: game.historyMsgPackList, @@ -163,8 +162,8 @@ func (g *GameManager) GCGAskDuelReq(player *model.Player, payloadMsg pb.Message) }, }, } + // 玩家信息列表 for _, controller := range game.controllerMap { - // 玩家信息列表 gcgControllerShowInfo := &proto.GCGControllerShowInfo{ ControllerId: controller.controllerId, ProfilePicture: &proto.ProfilePicture{}, @@ -175,6 +174,9 @@ func (g *GameManager) GCGAskDuelReq(player *model.Player, payloadMsg pb.Message) gcgControllerShowInfo.ProfilePicture.AvatarId = player.AvatarMap[player.HeadImage].Costume } gcgAskDuelRsp.Duel.ShowInfoList = append(gcgAskDuelRsp.Duel.ShowInfoList) + } + // 玩家牌盒信息 卡牌显示相关 + for _, controller := range game.controllerMap { // FieldList 玩家牌盒信息 卡牌显示相关 playerField := &proto.GCGPlayerField{ Unk3300_IKJMGAHCFPM: 0, @@ -208,50 +210,38 @@ func (g *GameManager) GCGAskDuelReq(player *model.Player, payloadMsg pb.Message) DeckCardNum: 0, Unk3300_GLNIFLOKBPM: 0, } - // 卡牌信息 for _, info := range controller.cardList { - gcgCard := &proto.GCGCard{ - TagList: info.tagList, - Guid: info.guid, - IsShow: info.isShow, - TokenList: make([]*proto.GCGToken, 0, 0), - FaceType: info.faceType, - SkillIdList: info.skillIdList, - SkillLimitsList: make([]*proto.GCGSkillLimitsInfo, 0, 0), - Id: info.cardId, - ControllerId: controller.controllerId, - } - // Token - for k, v := range info.tokenMap { - gcgCard.TokenList = append(gcgCard.TokenList, &proto.GCGToken{ - Value: v, - Key: k, - }) - } - // TODO SkillLimitsList - for _, skillId := range info.skillLimitList { - gcgCard.SkillLimitsList = append(gcgCard.SkillLimitsList, &proto.GCGSkillLimitsInfo{ - SkillId: skillId, - LimitsList: nil, // TODO 技能限制列表 - }) - } - gcgAskDuelRsp.Duel.CardList = append(gcgAskDuelRsp.Duel.CardList, gcgCard) - gcgAskDuelRsp.Duel.CardIdList = append(gcgAskDuelRsp.Duel.CardIdList, info.cardId) - // Field playerField.ModifyZoneMap[info.guid] = &proto.GCGZone{CardList: []uint32{}} playerField.Unk3300_INDJNJJJNKL.CardList = append(playerField.Unk3300_INDJNJJJNKL.CardList, info.guid) - // Phase - gcgAskDuelRsp.Duel.Phase.AllowControllerMap[controller.controllerId] = controller.allow } - // 添加完所有卡牌的位置之类的信息 添加这个牌盒 + // 添加完所有卡牌的位置之类的信息后添加这个牌盒 gcgAskDuelRsp.Duel.FieldList = append(gcgAskDuelRsp.Duel.FieldList, playerField) - // Unk3300_CDCMBOKBLAK + } + // 历史卡牌信息 + for _, cardInfo := range game.historyCardList { + gcgAskDuelRsp.Duel.HistoryCardList = append(gcgAskDuelRsp.Duel.HistoryCardList, cardInfo.ToProto()) + } + // 卡牌信息 + for _, controller := range game.controllerMap { + for _, cardInfo := range controller.cardList { + gcgAskDuelRsp.Duel.CardList = append(gcgAskDuelRsp.Duel.CardList, cardInfo.ToProto()) + gcgAskDuelRsp.Duel.CardIdList = append(gcgAskDuelRsp.Duel.CardIdList, cardInfo.cardId) + } + } + // 阶段信息 + for _, controller := range game.controllerMap { + // 操控者是否允许操作 + gcgAskDuelRsp.Duel.Phase.AllowControllerMap[controller.controllerId] = controller.allow + } + // Unk3300_CDCMBOKBLAK 你问我这是啥? 我也不知道 + for _, controller := range game.controllerMap { gcgAskDuelRsp.Duel.Unk3300_CDCMBOKBLAK = append(gcgAskDuelRsp.Duel.Unk3300_CDCMBOKBLAK, &proto.Unk3300_ADHENCIFKNI{ BeginTime: 0, TimeStamp: 0, ControllerId: controller.controllerId, }) } + GAME_MANAGER.SendMsg(cmd.GCGAskDuelRsp, player.PlayerID, player.ClientSeq, gcgAskDuelRsp) } @@ -323,16 +313,12 @@ func (g *GameManager) GCGOperationReq(player *model.Player, payloadMsg pb.Messag return } } - // 客户端更新操控者 - game.AddMsgPack(0, proto.GCGActionType_GCG_ACTION_TYPE_NONE, game.GCGMsgUpdateController()) - // 游戏行动阶段 - game.GameChangePhase(proto.GCGPhaseType_GCG_PHASE_TYPE_PRE_MAIN) + // 操控者确认重投骰子 + game.ControllerReRollDice(gameController, op.DiceIndexList) default: logger.Error("gcg op is not handle, op: %T", req.Op.Op) - g.CommonRetError(cmd.GCGOperationRsp, player, &proto.GCGOperationRsp{}, proto.Retcode_RET_GCG_OPERATION_PARAM_ERROR) return } - // PacketGCGOperationRsp gcgOperationRsp := &proto.GCGOperationRsp{ OpSeq: req.OpSeq, @@ -340,6 +326,27 @@ func (g *GameManager) GCGOperationReq(player *model.Player, payloadMsg pb.Messag GAME_MANAGER.SendMsg(cmd.GCGOperationRsp, player.PlayerID, player.ClientSeq, gcgOperationRsp) } +// PacketGCGSkillPreviewNotify GCG游戏技能栏展示通知 +func (g *GameManager) PacketGCGSkillPreviewNotify(controller *GCGController) *proto.GCGSkillPreviewNotify { + // 确保玩家选择了角色牌 + if controller.selectedCharCard == nil { + logger.Error("selected char is nil, controllerId: %v", controller.controllerId) + return new(proto.GCGSkillPreviewNotify) + } + // PacketGCGSkillPreviewNotify + gcgSkillPreviewNotify := &proto.GCGSkillPreviewNotify{ + ControllerId: controller.controllerId, + // 当前角色牌拥有的技能信息 + SkillPreviewList: make([]*proto.GCGSkillPreviewInfo, 0, len(controller.selectedCharCard.skillIdList)), + // 切换到其他角色牌的所需消耗信息 + ChangeOnstagePreviewList: make([]*proto.GCGChangeOnstageInfo, 0, 2), // 暂时写死 + PlayCardList: make([]*proto.GCGSkillPreviewPlayCardInfo, 0, 0), + OnstageCardGuid: controller.selectedCharCard.guid, // 当前被选择的角色牌guid + } + return gcgSkillPreviewNotify +} + +// SendGCGMessagePackNotify 发送GCG游戏消息包通知 func (g *GameManager) SendGCGMessagePackNotify(controller *GCGController, serverSeq uint32, msgPackList []*proto.GCGMessagePack) { // 确保加载完成 if controller.loadState != ControllerLoadState_InitFinish { diff --git a/protocol/cmd/cmd_id_proto_obj_map.go b/protocol/cmd/cmd_id_proto_obj_map.go index 8807cc80..e47c0ac8 100644 --- a/protocol/cmd/cmd_id_proto_obj_map.go +++ b/protocol/cmd/cmd_id_proto_obj_map.go @@ -268,6 +268,7 @@ func (c *CmdProtoMap) registerAllMessage() { c.registerMessage(GCGHeartBeatNotify, &proto.GCGHeartBeatNotify{}) // GCG游戏心跳包通知 c.registerMessage(GCGOperationReq, &proto.GCGOperationReq{}) // GCG游戏客户端操作请求 c.registerMessage(GCGOperationRsp, &proto.GCGOperationRsp{}) // GCG游戏客户端操作响应 + c.registerMessage(GCGSkillPreviewNotify, &proto.GCGSkillPreviewNotify{}) // GCG游戏技能栏展示通知 // // TODO 客户端开始GCG游戏 // c.registerMessage(GCGStartChallengeByCheckRewardReq, &proto.GCGStartChallengeByCheckRewardReq{}) // GCG开始挑战来自检测奖励请求