GCG流程至投掷骰子

This commit is contained in:
UnKownOwO
2023-01-07 00:38:06 +08:00
parent b6512b58c2
commit 8e11ec5f9f
9 changed files with 741 additions and 317 deletions

View File

@@ -21,6 +21,7 @@ type GameDataConfig struct {
AvatarSkillDataMap map[int32]*AvatarSkillData // 角色技能
AvatarSkillDepotDataMap map[int32]*AvatarSkillDepotData // 角色技能库
DropGroupDataMap map[int32]*DropGroupData // 掉落组
GCGCharDataMap map[int32]*GCGCharData // 角色卡牌
}
func InitGameDataConfig() {
@@ -62,6 +63,7 @@ func (g *GameDataConfig) load() {
g.loadAvatarSkillData() // 角色技能
g.loadAvatarSkillDepotData() // 角色技能库
g.loadDropGroupData() // 掉落组
g.loadGCGCharData() // 角色卡牌
}
func (g *GameDataConfig) readCsvFileData(fileName string) []byte {

View File

@@ -0,0 +1,75 @@
CharId,,TagId1,TagId2,TagId3,TagId4,TagId5,,,,,SkillListStr,,HPBase,MaxElemVal,,
int32,,int32,int32,int32,int32,int32,,,,,string,,int32,int32,,
ID,卡牌类型,[卡牌标签列表]1,[卡牌标签列表]2,[卡牌标签列表]3,[卡牌标签列表]4,[卡牌标签列表]5,[卡牌变量]1类型,[卡牌变量]1,[卡牌变量]2类型,[卡牌变量]2,卡牌技能列表,可进构筑,角色生命值,角色充能上限,最大行动意图数,死亡后移出游戏
1001,1,303,503,502,201,,,,,,80,,50,1,1,
1002,1,301,503,502,204,,,,,,80,,50,1,1,1
1003,1,303,503,502,201,,,,,,80#1130211,,50,1,1,
1101,1,301,204,402,,,16,0,,,11011# 11012# 11013# 11014,1,10,2,2,
1102,1,301,204,401,,,,,,,11021# 11022# 11023,1,10,3,2,
1103,1,301,201,401,,,,,,,11031# 11032# 11033,1,10,2,2,
1104,1,301,203,402,,,,,,,11041# 11042# 11043,1,10,3,2,
1105,1,301,201,403,,,,,,,11051# 11052# 11053# 11054,1,10,3,2,
1201,1,302,202,401,,,,,,,12011# 12012# 12013,1,10,3,2,
1202,1,302,201,402,,,,,,,12021# 12022# 12023,1,10,2,2,
1203,1,302,202,401,,,,,,,12031# 12032# 12033# 12034,1,10,3,2,
1301,1,303,203,401,,,,,,,13011# 13012# 13013,1,10,3,2,
1302,1,303,205,402,,,,,,,13021# 13022# 13023,1,10,2,2,
1303,1,303,201,401,,,,,,,13031# 13032# 13033,1,10,2,2,
1305,1,303,204,403,,,,,,,13051# 13052# 13053,1,10,2,2,
1401,1,304,204,401,,,,,,,14011# 14012# 14013,1,10,3,2,
1402,1,304,203,401,,,,,,,14021# 14022# 14023,1,10,3,2,
1403,1,304,201,402,,,,,,,14031# 14032# 14033,1,10,3,2,
1404,1,304,205,404,,,,,,,14041# 14042# 14043# 14044,1,10,2,2,
1501,1,307,202,401,,,,,,,15011# 15012# 15013,1,10,2,2,
1502,1,307,201,401,,,,,,,15021# 15022# 15023,1,10,3,2,
1601,1,305,202,402,,,,,,,16011# 16012# 16013,1,10,3,2,
1602,1,305,203,401,,,,,,,16021# 16022# 16023,1,10,2,2,
1701,1,306,204,404,,,,,,,17011# 17012# 17013,1,10,2,2,
2201,1,302,200,503,,,,,,,22011# 22012# 22013# 22014,1,10,3,2,
2202,1,302,200,501,,,,,,,22021# 22022# 22023,1,10,2,2,
2301,1,303,200,501,,,,,,,23011# 23012# 23013# 23014,1,10,2,2,
2501,1,307,200,503,,,,,,,25011# 25012# 25013# 25014,1,10,3,2,
2601,1,305,200,503,502,,,,,,26011# 26012# 26013# 26014,1,8,2,2,
2701,1,306,200,503,,,,,,,27011# 27012# 27013# 27014,1,10,2,2,
3001,1,300,503,502,,,,,,,30011# 30012# 30013,,4,2,1,1
3002,1,300,503,502,,,,,,,30021# 30022# 30023,,3,2,1,1
3003,1,300,504,,,,,,,,30031# 30032# 30033# 30034# 30035# 30036,,6,2,1,1
3101,1,301,503,502,,,,,,,31011# 31012# 31013# 31014# 31015,,8,2,2,1
3102,1,301,503,502,,,,,,,31021# 31022# 31023,,3,2,1,1
3103,1,301,503,,,,,,,,31031# 31032# 31033# 31034# 31035,,4,2,2,1
3201,1,302,503,,,,,,,,32011# 32012# 32013# 32014# 32015,,4,3,2,1
3202,1,302,503,502,,,,,,,32021# 32022# 32023,,4,2,2,1
3203,1,302,501,,,,,,,,32031# 32032# 32033# 32034# 32035,,10,3,2,1
3204,1,302,503,,,,,,,,32041# 32042# 32043,,4,2,1,1
3301,1,303,503,,,,,,,,33011# 33012# 33013# 33014# 33015,,4,3,2,1
3302,1,303,503,502,,,,,,,33021# 33022# 33023# 33024,,8,2,2,1
3303,1,303,504,,,,,,,,33031# 33032# 33033# 33034# 33035# 33036# 33037,,10,2,2,1
3304,1,303,503,502,,,,,,,33041# 33042# 33043,,4,2,1,1
3305,1,303,504,,,,,,,,33051# 33052# 30033# 30034# 30035# 30036,,6,2,1,1
3401,1,304,504,,,,,,,,34011# 34012# 34013# 34014# 34015# 34016# 34017,,10,2,2,1
3402,1,304,503,502,,,,,,,34021# 34022# 34023,,3,2,1,1
3403,1,304,503,502,,,,,,,34031# 34032# 34033# 34034,,8,2,2,1
3405,1,304,504,,,,,,,,34051# 34052# 30033# 30034# 30035# 30036,,6,2,1,1
3406,1,304,503,,,,,,,,34061# 34062# 34063,,4,2,1,1
3501,1,307,503,502,,,,,,,35011# 35012# 35013,,4,2,2,1
3502,1,307,501,,,,,,,,35021# 35022# 35023# 35024# 35025,,10,2,2,1
3701,1,306,503,,,,,,,,37011# 37012# 37013# 37014,,6,2,2,1
3703,1,306,503,502,,,,,,,37031# 37032# 37033,,4,2,1,1
4601,1,305,200,503,502,,,,,,26011# 26012 # 26013,,12,2,2,1
4602,1,305,200,503,502,,,,,,26011# 26012 # 26013# 26014,,16,2,3,1
4603,1,305,200,503,502,,,,,,26011# 26012 # 26013# 26014,,24,2,3,1
4604,1,305,200,503,502,,,,,,26011# 26012 # 26013# 26014,,8,2,2,1
4201,1,302,200,503,,,,,,,22011# 22012 # 22013# 22014# 32043,,30,3,4,1
4202,1,302,200,503,,,,,,,22011# 22012 # 22013# 22014# 32043,,45,3,4,1
4203,1,302,200,501,,,,,,,22021# 22022 # 22023,,10,2,3,1
4204,1,302,200,501,,,,,,,22021# 22022 # 22023,,10,2,2,1
4301,1,303,200,501,,,,,,,23011# 23012 # 23013# 23014,,10,2,3,1
4302,1,303,200,501,,,,,,,23011# 23012 # 23013# 23014,,10,2,2,1
4501,1,307,200,503,,,,,,,25011# 25012 # 25013# 25014,,20,3,2,1
4502,1,307,200,503,,,,,,,25011# 25012 # 25013# 25014,,28,3,2,1
4701,1,306,200,503,,,,,,,27011# 27012 # 27013# 27014,,36,2,4,1
4702,1,306,200,503,,,,,,,27011# 27012 # 27013# 27014,,48,2,4,1
4703,1,306,200,503,,,,,,,27011# 27012 # 27013# 27014,,10,2,2,1
4704,1,306,200,503,,,,,,,27011# 27012 # 27013# 27014,,30,2,3,1
5201,1,302,503,,,,,,,,32011# 32012# 32013# 32014# 32015# 32016,,4,3,2,1
5301,1,303,503,,,,,,,,33011# 33012# 33013# 33014# 33015# 33016,,4,3,2,1
1 CharId TagId1 TagId2 TagId3 TagId4 TagId5 SkillListStr HPBase MaxElemVal
2 int32 int32 int32 int32 int32 int32 string int32 int32
3 ID 卡牌类型 [卡牌标签列表]1 [卡牌标签列表]2 [卡牌标签列表]3 [卡牌标签列表]4 [卡牌标签列表]5 [卡牌变量]1类型 [卡牌变量]1值 [卡牌变量]2类型 [卡牌变量]2值 卡牌技能列表 可进构筑 角色生命值 角色充能上限 最大行动意图数 死亡后移出游戏
4 1001 1 303 503 502 201 80 50 1 1
5 1002 1 301 503 502 204 80 50 1 1 1
6 1003 1 303 503 502 201 80#1130211 50 1 1
7 1101 1 301 204 402 16 0 11011# 11012# 11013# 11014 1 10 2 2
8 1102 1 301 204 401 11021# 11022# 11023 1 10 3 2
9 1103 1 301 201 401 11031# 11032# 11033 1 10 2 2
10 1104 1 301 203 402 11041# 11042# 11043 1 10 3 2
11 1105 1 301 201 403 11051# 11052# 11053# 11054 1 10 3 2
12 1201 1 302 202 401 12011# 12012# 12013 1 10 3 2
13 1202 1 302 201 402 12021# 12022# 12023 1 10 2 2
14 1203 1 302 202 401 12031# 12032# 12033# 12034 1 10 3 2
15 1301 1 303 203 401 13011# 13012# 13013 1 10 3 2
16 1302 1 303 205 402 13021# 13022# 13023 1 10 2 2
17 1303 1 303 201 401 13031# 13032# 13033 1 10 2 2
18 1305 1 303 204 403 13051# 13052# 13053 1 10 2 2
19 1401 1 304 204 401 14011# 14012# 14013 1 10 3 2
20 1402 1 304 203 401 14021# 14022# 14023 1 10 3 2
21 1403 1 304 201 402 14031# 14032# 14033 1 10 3 2
22 1404 1 304 205 404 14041# 14042# 14043# 14044 1 10 2 2
23 1501 1 307 202 401 15011# 15012# 15013 1 10 2 2
24 1502 1 307 201 401 15021# 15022# 15023 1 10 3 2
25 1601 1 305 202 402 16011# 16012# 16013 1 10 3 2
26 1602 1 305 203 401 16021# 16022# 16023 1 10 2 2
27 1701 1 306 204 404 17011# 17012# 17013 1 10 2 2
28 2201 1 302 200 503 22011# 22012# 22013# 22014 1 10 3 2
29 2202 1 302 200 501 22021# 22022# 22023 1 10 2 2
30 2301 1 303 200 501 23011# 23012# 23013# 23014 1 10 2 2
31 2501 1 307 200 503 25011# 25012# 25013# 25014 1 10 3 2
32 2601 1 305 200 503 502 26011# 26012# 26013# 26014 1 8 2 2
33 2701 1 306 200 503 27011# 27012# 27013# 27014 1 10 2 2
34 3001 1 300 503 502 30011# 30012# 30013 4 2 1 1
35 3002 1 300 503 502 30021# 30022# 30023 3 2 1 1
36 3003 1 300 504 30031# 30032# 30033# 30034# 30035# 30036 6 2 1 1
37 3101 1 301 503 502 31011# 31012# 31013# 31014# 31015 8 2 2 1
38 3102 1 301 503 502 31021# 31022# 31023 3 2 1 1
39 3103 1 301 503 31031# 31032# 31033# 31034# 31035 4 2 2 1
40 3201 1 302 503 32011# 32012# 32013# 32014# 32015 4 3 2 1
41 3202 1 302 503 502 32021# 32022# 32023 4 2 2 1
42 3203 1 302 501 32031# 32032# 32033# 32034# 32035 10 3 2 1
43 3204 1 302 503 32041# 32042# 32043 4 2 1 1
44 3301 1 303 503 33011# 33012# 33013# 33014# 33015 4 3 2 1
45 3302 1 303 503 502 33021# 33022# 33023# 33024 8 2 2 1
46 3303 1 303 504 33031# 33032# 33033# 33034# 33035# 33036# 33037 10 2 2 1
47 3304 1 303 503 502 33041# 33042# 33043 4 2 1 1
48 3305 1 303 504 33051# 33052# 30033# 30034# 30035# 30036 6 2 1 1
49 3401 1 304 504 34011# 34012# 34013# 34014# 34015# 34016# 34017 10 2 2 1
50 3402 1 304 503 502 34021# 34022# 34023 3 2 1 1
51 3403 1 304 503 502 34031# 34032# 34033# 34034 8 2 2 1
52 3405 1 304 504 34051# 34052# 30033# 30034# 30035# 30036 6 2 1 1
53 3406 1 304 503 34061# 34062# 34063 4 2 1 1
54 3501 1 307 503 502 35011# 35012# 35013 4 2 2 1
55 3502 1 307 501 35021# 35022# 35023# 35024# 35025 10 2 2 1
56 3701 1 306 503 37011# 37012# 37013# 37014 6 2 2 1
57 3703 1 306 503 502 37031# 37032# 37033 4 2 1 1
58 4601 1 305 200 503 502 26011# 26012 # 26013 12 2 2 1
59 4602 1 305 200 503 502 26011# 26012 # 26013# 26014 16 2 3 1
60 4603 1 305 200 503 502 26011# 26012 # 26013# 26014 24 2 3 1
61 4604 1 305 200 503 502 26011# 26012 # 26013# 26014 8 2 2 1
62 4201 1 302 200 503 22011# 22012 # 22013# 22014# 32043 30 3 4 1
63 4202 1 302 200 503 22011# 22012 # 22013# 22014# 32043 45 3 4 1
64 4203 1 302 200 501 22021# 22022 # 22023 10 2 3 1
65 4204 1 302 200 501 22021# 22022 # 22023 10 2 2 1
66 4301 1 303 200 501 23011# 23012 # 23013# 23014 10 2 3 1
67 4302 1 303 200 501 23011# 23012 # 23013# 23014 10 2 2 1
68 4501 1 307 200 503 25011# 25012 # 25013# 25014 20 3 2 1
69 4502 1 307 200 503 25011# 25012 # 25013# 25014 28 3 2 1
70 4701 1 306 200 503 27011# 27012 # 27013# 27014 36 2 4 1
71 4702 1 306 200 503 27011# 27012 # 27013# 27014 48 2 4 1
72 4703 1 306 200 503 27011# 27012 # 27013# 27014 10 2 2 1
73 4704 1 306 200 503 27011# 27012 # 27013# 27014 30 2 3 1
74 5201 1 302 503 32011# 32012# 32013# 32014# 32015# 32016 4 3 2 1
75 5301 1 303 503 33011# 33012# 33013# 33014# 33015# 33016 4 3 2 1

66
gdconf/gcg_char_data.go Normal file
View File

@@ -0,0 +1,66 @@
package gdconf
import (
"fmt"
"strconv"
"strings"
"hk4e/pkg/logger"
"github.com/jszwec/csvutil"
)
// 角色卡牌配置表
type GCGCharData struct {
CharId int32 `csv:"CharId"` // ID
TagId1 int32 `csv:"TagId1,omitempty"` // 卡牌标签列表1
TagId2 int32 `csv:"TagId2,omitempty"` // 卡牌标签列表2
TagId3 int32 `csv:"TagId3,omitempty"` // 卡牌标签列表3
TagId4 int32 `csv:"TagId4,omitempty"` // 卡牌标签列表4
TagId5 int32 `csv:"TagId5,omitempty"` // 卡牌标签列表5
SkillListStr string `csv:"SkillListStr,omitempty"` // 卡牌技能列表文本
HPBase int32 `csv:"HPBase,omitempty"` // 角色生命值
MaxElemVal int32 `csv:"MaxElemVal,omitempty"` // 角色充能上限
TagList []uint32 // 卡牌标签列表
SkillList []uint32 // 卡牌技能列表
}
func (g *GameDataConfig) loadGCGCharData() {
g.GCGCharDataMap = make(map[int32]*GCGCharData)
data := g.readCsvFileData("GCGCharData.csv")
var gcgCharDataList []*GCGCharData
err := csvutil.Unmarshal(data, &gcgCharDataList)
if err != nil {
info := fmt.Sprintf("parse file error: %v", err)
panic(info)
}
for _, gcgCharData := range gcgCharDataList {
// 将TagId整合进TagList
gcgCharData.TagList = make([]uint32, 0, 5)
tempTagList := make([]int32, 0, 5)
tempTagList = append(tempTagList, gcgCharData.TagId1, gcgCharData.TagId2, gcgCharData.TagId3, gcgCharData.TagId4, gcgCharData.TagId5)
for _, tagId := range tempTagList {
// 跳过为0的tag
if tagId == 0 {
continue
}
gcgCharData.TagList = append(gcgCharData.TagList, uint32(tagId))
}
// 技能列表读取转换
tempSkillList := strings.Split(strings.ReplaceAll(gcgCharData.SkillListStr, " ", ""), "#")
gcgCharData.SkillList = make([]uint32, 0, len(tempSkillList))
for _, s := range tempSkillList {
skillId, err := strconv.Atoi(s)
if err != nil {
logger.Error("skill id to i err, %v", err)
return
}
gcgCharData.SkillList = append(gcgCharData.SkillList, uint32(skillId))
}
// list -> map
g.GCGCharDataMap[gcgCharData.CharId] = gcgCharData
}
logger.Info("GCGCharData count: %v", len(g.GCGCharDataMap))
}

43
gs/game/gcg_ai.go Normal file
View File

@@ -0,0 +1,43 @@
package game
import (
"hk4e/pkg/logger"
"hk4e/protocol/proto"
"time"
)
type GCGAi struct {
game *GCGGame // 所在的游戏
controllerId uint32 // 操控者Id
}
// ReceiveGCGMessagePackNotify 接收GCG消息包通知
func (g *GCGAi) ReceiveGCGMessagePackNotify(notify *proto.GCGMessagePackNotify) {
// 获取玩家的操控者对象
gameController := g.game.controllerMap[g.controllerId]
if gameController == nil {
logger.Error("ai 角色 nil")
return
}
for _, pack := range notify.MsgPackList {
for _, message := range pack.MsgList {
switch message.Message.(type) {
case *proto.GCGMessage_PhaseChange:
// 阶段改变
msg := message.GetPhaseChange()
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{})
}()
}
}
}
}
}

View File

@@ -1,17 +1,23 @@
package game
import (
"hk4e/gdconf"
"hk4e/gs/model"
"hk4e/pkg/logger"
"hk4e/pkg/random"
"hk4e/protocol/cmd"
"hk4e/protocol/proto"
"math/rand"
"time"
)
// ControllerType 操控者类型
type ControllerType uint8
const (
ControllerType_Player ControllerType = iota // 玩家
ControllerType_AI // AI
ControllerType_None ControllerType = iota
ControllerType_Player // 玩家
ControllerType_AI // AI
)
// GCGCardInfo 游戏对局内卡牌
@@ -36,12 +42,24 @@ const (
// GCGController 操控者
type GCGController struct {
controllerId uint32 // 操控者Id
cardMap map[uint32]*GCGCardInfo // 卡牌列表
loadState ControllerLoadState // 加载状态
controllerType ControllerType // 操控者的类型
player *model.Player
ai uint32 // 暂时不写
controllerId uint32 // 操控者Id
cardList []*GCGCardInfo // 卡牌列表
loadState ControllerLoadState // 加载状态
allow uint32 // 是否允许操控 0 -> 不允许 1 -> 允许
selectedCharCard *GCGCardInfo // 选择的角色卡牌
controllerType ControllerType // 操控者的类型
player *model.Player // 玩家对象
ai *GCGAi // AI对象
}
// GetCardByGuid 通过卡牌的Guid获取卡牌
func (g *GCGController) GetCardByGuid(cardGuid uint32) *GCGCardInfo {
for _, info := range g.cardList {
if info.guid == cardGuid {
return info
}
}
return nil
}
// GCGManager 七圣召唤管理器
@@ -63,14 +81,14 @@ func (g *GCGManager) CreateGame(gameId uint32, playerList []*model.Player) *GCGG
guid: g.gameGuidCounter,
gameId: gameId,
roundInfo: &GCGRoundInfo{
roundNum: 1, // 默认以第一回合开始
allowControllerMap: make(map[uint32]uint32, 0),
firstController: 1, // 1号操控者为先手
roundNum: 1, // 默认以第一回合开始
firstController: 1, // 1号操控者为先手
diceSideMap: make(map[uint32][]proto.GCGDiceSideType, 2),
},
controllerMap: make(map[uint32]*GCGController, 0),
controllerMsgPackMap: make(map[uint32][]*proto.GCGMessagePack),
historyCardList: make([]*proto.GCGCard, 0, 0),
historyMsgPackList: make([]*proto.GCGMessagePack, 0, 0),
controllerMap: make(map[uint32]*GCGController, 2),
msgPackList: make([]*proto.GCGMessagePack, 0, 10),
historyMsgPackList: make([]*proto.GCGMessagePack, 0, 50),
historyCardList: make([]*GCGCardInfo, 0, 100),
}
// 初始化游戏
game.InitGame(playerList)
@@ -79,20 +97,351 @@ func (g *GCGManager) CreateGame(gameId uint32, playerList []*model.Player) *GCGG
return game
}
// GCGMsgPhaseChange GCG消息阶段改变
func (g *GCGManager) GCGMsgPhaseChange(game *GCGGame, afterPhase proto.GCGPhaseType) *proto.GCGMessage {
gcgMsgPhaseChange := &proto.GCGMsgPhaseChange{
BeforePhase: game.roundInfo.phaseType,
AfterPhase: afterPhase,
AllowControllerMap: make([]*proto.Uint32Pair, 0, len(game.controllerMap)),
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,
}
// 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)
// 游戏选择角色卡牌阶段
game.GameChangePhase(proto.GCGPhaseType_GCG_PHASE_TYPE_ON_STAGE)
}
// PhaseSelectChar 阶段选择角色卡牌
func PhaseSelectChar(game *GCGGame) {
// 该阶段确保每位玩家都选择了角色
for _, controller := range game.controllerMap {
if controller.selectedCharCard == nil {
// 如果有没选择角色卡牌的则不执行后面
return
}
}
// 开始阶段所有玩家允许操作
if afterPhase == proto.GCGPhaseType_GCG_PHASE_TYPE_START || afterPhase == proto.GCGPhaseType_GCG_PHASE_TYPE_ON_STAGE || afterPhase == proto.GCGPhaseType_GCG_PHASE_TYPE_MAIN {
for controllerId := range game.controllerMap {
gcgMsgPhaseChange.AllowControllerMap = append(gcgMsgPhaseChange.AllowControllerMap, &proto.Uint32Pair{
Key: controllerId,
Value: 1,
})
// 回合信息
game.AddMsgPack(0, proto.GCGActionType_GCG_ACTION_TYPE_SEND_MESSAGE, game.GCGMsgDuelDataChange())
// 游戏投掷骰子阶段
game.GameChangePhase(proto.GCGPhaseType_GCG_PHASE_TYPE_DICE)
}
// PhaseRollDice 阶段投掷骰子
func PhaseRollDice(game *GCGGame) {
// 给每位玩家投掷骰子
for _, controller := range game.controllerMap {
diceSideList := make([]proto.GCGDiceSideType, 0, 8)
rand.Seed(time.Now().UnixNano()) // 随机数种子
// 玩家需要8个骰子
for i := 0; i < 8; i++ {
diceSide := proto.GCGDiceSideType(random.GetRandomInt32(1, 8))
diceSideList = append(diceSideList, diceSide)
}
// 存储该回合玩家的骰子
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) {
game.AddMsgPack(0, proto.GCGActionType_GCG_ACTION_TYPE_TRIGGER_SKILL, game.GCGMsgUseSkill(195, 33024), game.GCGMsgUseSkillEnd(195, 33024))
}
// GCGRoundInfo 游戏对局回合信息
type GCGRoundInfo struct {
roundNum uint32 // 游戏当前回合数
phaseType proto.GCGPhaseType // 现在所处的阶段类型
firstController uint32 // 当前回合先手的操控者
diceSideMap map[uint32][]proto.GCGDiceSideType // 操控者骰子列表 uint32 -> controllerId
}
// GCGGame 游戏对局
type GCGGame struct {
guid uint32 // 唯一Id
gameId uint32 // 游戏Id
gameState GCGGameState // 游戏运行状态
gameTick uint32 // 游戏tick
serverSeqCounter uint32 // 请求序列生成计数器
controllerIdCounter uint32 // 操控者Id生成器
cardGuidCounter uint32 // 卡牌guid生成计数器
roundInfo *GCGRoundInfo // 游戏回合信息
controllerMap map[uint32]*GCGController // 操控者列表 uint32 -> controllerId
msgPackList []*proto.GCGMessagePack // 消息包待发送区
historyMsgPackList []*proto.GCGMessagePack // 历史消息包列表
historyCardList []*GCGCardInfo // 历史卡牌列表
}
// AddPlayer GCG游戏添加玩家
func (g *GCGGame) AddPlayer(player *model.Player) {
// 创建操控者
g.controllerIdCounter++
controller := &GCGController{
controllerId: g.controllerIdCounter,
cardList: make([]*GCGCardInfo, 0, 50),
loadState: ControllerLoadState_None,
controllerType: ControllerType_Player,
player: player,
}
// 生成卡牌信息
g.GiveCharCard(controller, 1301)
g.GiveCharCard(controller, 1103)
// 记录操控者
g.controllerMap[g.controllerIdCounter] = controller
player.GCGCurGameGuid = g.guid
}
// AddAI GCG游戏添加AI
func (g *GCGGame) AddAI() {
// 创建操控者
g.controllerIdCounter++
controller := &GCGController{
controllerId: g.controllerIdCounter,
cardList: make([]*GCGCardInfo, 0, 50),
loadState: ControllerLoadState_InitFinish,
controllerType: ControllerType_AI,
ai: &GCGAi{
game: g,
controllerId: g.controllerIdCounter,
},
}
// 生成卡牌信息
g.GiveCharCard(controller, 3001)
g.GiveCharCard(controller, 3302)
// 记录操控者
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) {
// 读取角色卡牌配置表
gcgCharConfig, ok := gdconf.CONF.GCGCharDataMap[int32(charId)]
if !ok {
logger.Error("gcg char config error, charId: %v", charId)
return
}
// 生成卡牌信息
g.cardGuidCounter++
controller.cardList = append(controller.cardList, &GCGCardInfo{
cardId: charId,
guid: g.cardGuidCounter,
faceType: 0, // 1为金卡
tagList: gcgCharConfig.TagList,
tokenMap: map[uint32]uint32{
1: uint32(gcgCharConfig.HPBase), // 血量
2: uint32(gcgCharConfig.HPBase), // 最大血量(不确定)
4: 0, // 充能
5: uint32(gcgCharConfig.MaxElemVal), // 充能条
},
skillIdList: gcgCharConfig.SkillList,
skillLimitList: []uint32{},
isShow: true,
})
}
// ControllerSelectChar 操控者选择角色卡牌
func (g *GCGGame) ControllerSelectChar(controller *GCGController, cardInfo *GCGCardInfo, costDiceIndexList []uint32) {
// 判断选择角色卡牌消耗的点数是否正确
if controller.selectedCharCard != nil && len(costDiceIndexList) == 0 {
// 首次选择角色牌不消耗点数
return
}
// TODO 消耗骰子点数
// 设置角色卡牌
controller.selectedCharCard = cardInfo
// 设置操控者禁止操作
controller.allow = 0
// 广播选择的角色卡牌消息包
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))
}
// onTick 游戏的Tick
func (g *GCGGame) onTick() {
// 判断游戏是否运行中
if g.gameState != GCGGameState_Running {
return
}
// 每10s触发
if g.gameTick%10 == 0 {
// GCG游戏心跳包
for _, controller := range g.controllerMap {
// 跳过AI
if controller.player == nil {
continue
}
gcgHeartBeatNotify := &proto.GCGHeartBeatNotify{
ServerSeq: g.serverSeqCounter,
}
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++
}
// InitGame 初始化GCG游戏
func (g *GCGGame) InitGame(playerList []*model.Player) {
// 初始化玩家
for _, player := range playerList {
g.AddPlayer(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
}
// StartGame 开始GCG游戏
func (g *GCGGame) StartGame() {
// 游戏开始设置所有玩家不允许操作
// for _, c := range g.controllerMap {
// c.allow = 0
// }
// 游戏状态更改为游戏运行中
g.gameState = GCGGameState_Running
}
// CheckAllInitFinish 检查所有玩家是否加载完成
func (g *GCGGame) CheckAllInitFinish() {
// 判断游戏是否已开始
if g.gameState == GCGGameState_Running {
logger.Error("gcg game is running")
return
}
// 检查所有玩家是否加载完成
for _, controller := range g.controllerMap {
if controller.loadState != ControllerLoadState_InitFinish {
return
}
}
// TODO 可能会玩家中途退了 超时结束游戏
// 正式开始游戏
g.StartGame()
}
// AddMsgPack 添加GCG消息包至待发送区
func (g *GCGGame) AddMsgPack(controllerId uint32, actionType proto.GCGActionType, msgList ...*proto.GCGMessage) {
pack := &proto.GCGMessagePack{
ActionType: actionType,
MsgList: make([]*proto.GCGMessage, 0, len(msgList)),
ControllerId: controllerId,
}
// 将每个GCG消息添加进消息包中
for _, message := range msgList {
pack.MsgList = append(pack.MsgList, message)
}
// 将消息包添加进待发送区
g.msgPackList = append(g.msgPackList, pack)
}
// SendAllMsgPack 发送所有待发送区的消息包
func (g *GCGGame) SendAllMsgPack(recordOnly bool) {
// 不发送空的消息包
if len(g.msgPackList) == 0 {
return
}
// 是否仅记录历史消息包
if !recordOnly {
g.serverSeqCounter++
for _, controller := range g.controllerMap {
GAME_MANAGER.SendGCGMessagePackNotify(controller, g.serverSeqCounter, g.msgPackList)
}
}
// 记录发送的历史消息包
for _, pack := range g.msgPackList {
g.historyMsgPackList = append(g.historyMsgPackList, pack)
}
// 清空待发送区消息包
g.msgPackList = make([]*proto.GCGMessagePack, 0, 10)
}
// GCGMsgPhaseChange GCG消息阶段改变
func (g *GCGGame) GCGMsgPhaseChange(beforePhase proto.GCGPhaseType, afterPhase proto.GCGPhaseType) *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)
}
}
gcgMessage := &proto.GCGMessage{
@@ -100,13 +449,11 @@ func (g *GCGManager) GCGMsgPhaseChange(game *GCGGame, afterPhase proto.GCGPhaseT
PhaseChange: gcgMsgPhaseChange,
},
}
// 修改游戏的阶段状态
game.roundInfo.phaseType = afterPhase
return gcgMessage
}
// GCGMsgPhaseContinue GCG消息阶段跳过
func (g *GCGManager) GCGMsgPhaseContinue() *proto.GCGMessage {
func (g *GCGGame) GCGMsgPhaseContinue() *proto.GCGMessage {
gcgMsgPhaseContinue := &proto.GCGMsgPhaseContinue{}
gcgMessage := &proto.GCGMessage{
Message: &proto.GCGMessage_PhaseContinue{
@@ -117,16 +464,17 @@ func (g *GCGManager) GCGMsgPhaseContinue() *proto.GCGMessage {
}
// GCGMsgUpdateController GCG消息更新操控者
func (g *GCGManager) GCGMsgUpdateController(game *GCGGame) *proto.GCGMessage {
func (g *GCGGame) GCGMsgUpdateController() *proto.GCGMessage {
gcgMsgUpdateController := &proto.GCGMsgUpdateController{
AllowControllerMap: make([]*proto.Uint32Pair, 0, len(game.controllerMap)),
AllowControllerMap: make([]*proto.Uint32Pair, 0, len(g.controllerMap)),
}
// 操控者的允许次数
for controllerId, _ := range game.roundInfo.allowControllerMap {
gcgMsgUpdateController.AllowControllerMap = append(gcgMsgUpdateController.AllowControllerMap, &proto.Uint32Pair{
Key: controllerId,
Value: 0,
})
// 操控者的是否允许操作
for _, controller := range g.controllerMap {
pair := &proto.Uint32Pair{
Key: controller.controllerId,
Value: controller.allow,
}
gcgMsgUpdateController.AllowControllerMap = append(gcgMsgUpdateController.AllowControllerMap, pair)
}
gcgMessage := &proto.GCGMessage{
Message: &proto.GCGMessage_UpdateController{
@@ -137,7 +485,7 @@ func (g *GCGManager) GCGMsgUpdateController(game *GCGGame) *proto.GCGMessage {
}
// GCGMsgClientPerform GCG消息客户端执行
func (g *GCGManager) GCGMsgClientPerform(performType proto.GCGClientPerformType, paramList []uint32) *proto.GCGMessage {
func (g *GCGGame) GCGMsgClientPerform(performType proto.GCGClientPerformType, paramList []uint32) *proto.GCGMessage {
gcgMsgClientPerform := &proto.GCGMsgClientPerform{
ParamList: paramList,
PerformType: performType,
@@ -150,278 +498,92 @@ func (g *GCGManager) GCGMsgClientPerform(performType proto.GCGClientPerformType,
return gcgMessage
}
// GCGRoundInfo 游戏对局回合信息
type GCGRoundInfo struct {
roundNum uint32 // 游戏当前回合数
phaseType proto.GCGPhaseType // 现在所处的阶段类型
allowControllerMap map[uint32]uint32 // 回合内操控者允许的次数
firstController uint32 // 当前回合先手的操控者
}
// GCGGame 游戏对局
type GCGGame struct {
guid uint32 // 唯一Id
gameId uint32 // 游戏Id
serverSeqCounter uint32 // 请求序列生成计数器
controllerIdCounter uint32 // 操控者Id生成器
cardGuidCounter uint32 // 卡牌guid生成计数器
roundInfo *GCGRoundInfo // 游戏回合信息
controllerMap map[uint32]*GCGController // 操控者列表 uint32 -> controllerId
controllerMsgPackMap map[uint32][]*proto.GCGMessagePack // 操控者消息包待发送区 0代表全局
// TODO 游戏重连
historyCardList []*proto.GCGCard // 历史发送的卡牌
historyMsgPackList []*proto.GCGMessagePack // 历史发送的消息包
}
// AddPlayer GCG游戏添加玩家
func (g *GCGGame) AddPlayer(player *model.Player) {
// 创建操控者
g.controllerIdCounter++
controller := &GCGController{
controllerId: g.controllerIdCounter,
cardMap: make(map[uint32]*GCGCardInfo, 0),
loadState: ControllerLoadState_None,
controllerType: ControllerType_Player,
player: player,
}
// 生成卡牌信息
g.cardGuidCounter++
controller.cardMap[1301] = &GCGCardInfo{
cardId: 1301,
guid: g.cardGuidCounter,
faceType: 0,
tagList: []uint32{203, 303, 401},
tokenMap: map[uint32]uint32{
1: 10,
2: 10,
4: 0,
5: 3,
},
skillIdList: []uint32{
13011,
13012,
13013,
},
skillLimitList: []uint32{},
isShow: true,
}
g.cardGuidCounter++
controller.cardMap[1103] = &GCGCardInfo{
cardId: 1103,
guid: g.cardGuidCounter,
faceType: 0,
tagList: []uint32{201, 301, 401},
tokenMap: map[uint32]uint32{
1: 10, // 血量
2: 10, // 最大血量(不确定)
4: 0, // 充能
5: 2, // 充能条
},
skillIdList: []uint32{
11031,
11032,
11033,
},
skillLimitList: []uint32{},
isShow: true,
}
g.cardGuidCounter++
controller.cardMap[3001] = &GCGCardInfo{
cardId: 3001,
guid: g.cardGuidCounter,
faceType: 0,
tagList: []uint32{200, 300, 502, 503},
tokenMap: map[uint32]uint32{
1: 4,
2: 4,
4: 0,
5: 2,
},
skillIdList: []uint32{
30011,
30012,
30013,
},
skillLimitList: []uint32{},
isShow: true,
}
// g.cardGuidCounter++
// controller.cardMap[1301011] = &GCGCardInfo{
// cardId: 1301011,
// guid: g.cardGuidCounter,
// faceType: 0,
// skillIdList: []uint32{
// 13010111,
// },
// skillLimitList: []uint32{},
// isShow: true,
// }
// 记录操控者
g.controllerMap[g.controllerIdCounter] = controller
player.GCGCurGameGuid = g.guid
}
// AddAI GCG游戏添加AI
func (g *GCGGame) AddAI() {
// 创建操控者
g.controllerIdCounter++
controller := &GCGController{
controllerId: g.controllerIdCounter,
cardMap: make(map[uint32]*GCGCardInfo, 0),
loadState: ControllerLoadState_InitFinish,
controllerType: ControllerType_AI,
ai: 233,
}
// 生成卡牌信息
g.cardGuidCounter++
controller.cardMap[3001] = &GCGCardInfo{
cardId: 3001,
guid: g.cardGuidCounter,
faceType: 0,
tagList: []uint32{200, 300, 502, 503},
tokenMap: map[uint32]uint32{
1: 4,
2: 4,
4: 0,
5: 2,
},
skillIdList: []uint32{
30011,
30012,
30013,
},
skillLimitList: []uint32{},
isShow: true,
}
g.cardGuidCounter++
controller.cardMap[3302] = &GCGCardInfo{
cardId: 3302,
guid: g.cardGuidCounter,
faceType: 0,
tagList: []uint32{200, 303, 502, 503},
tokenMap: map[uint32]uint32{
1: 8,
2: 8,
4: 0,
5: 2,
},
skillIdList: []uint32{
33021,
33022,
33023,
33024,
},
skillLimitList: []uint32{},
isShow: true,
}
// 记录操控者
g.controllerMap[g.controllerIdCounter] = controller
}
// InitGame 初始化GCG游戏
func (g *GCGGame) InitGame(playerList []*model.Player) {
// 初始化玩家
for _, player := range playerList {
g.AddPlayer(player)
}
// 添加AI
g.AddAI()
// 初始化每个操控者的次数
for controllerId := range g.controllerMap {
g.roundInfo.allowControllerMap[controllerId] = 0
}
// 先手者操控数为1
g.roundInfo.allowControllerMap[g.roundInfo.firstController] = 1
// TODO 验证玩家人数是否符合
// 预开始游戏
g.AddMessagePack(0, proto.GCGActionType_GCG_ACTION_TYPE_NONE, GCG_MANAGER.GCGMsgPhaseChange(g, proto.GCGPhaseType_GCG_PHASE_TYPE_START), GCG_MANAGER.GCGMsgUpdateController(g))
g.AddMessagePack(0, proto.GCGActionType_GCG_ACTION_TYPE_SEND_MESSAGE, GCG_MANAGER.GCGMsgPhaseContinue())
g.SendMessagePack(0)
// 预开始游戏后 ServerSeq 会跟官服不同 这里重置一下
g.serverSeqCounter = 0
logger.Error("gcg init")
}
// StartGame 开始GCG游戏
func (g *GCGGame) StartGame() {
// 开始游戏消息包
g.AddMessagePack(0, proto.GCGActionType_GCG_ACTION_TYPE_NONE, GCG_MANAGER.GCGMsgUpdateController(g))
g.AddMessagePack(0, proto.GCGActionType_GCG_ACTION_TYPE_PHASE_EXIT, GCG_MANAGER.GCGMsgClientPerform(proto.GCGClientPerformType_GCG_CLIENT_PERFORM_TYPE_FIRST_HAND, []uint32{g.roundInfo.firstController}))
g.AddMessagePack(0, proto.GCGActionType_GCG_ACTION_TYPE_NEXT_PHASE, GCG_MANAGER.GCGMsgPhaseChange(g, proto.GCGPhaseType_GCG_PHASE_TYPE_DRAW))
g.AddMessagePack(0, proto.GCGActionType_GCG_ACTION_TYPE_NEXT_PHASE, GCG_MANAGER.GCGMsgPhaseChange(g, proto.GCGPhaseType_GCG_PHASE_TYPE_ON_STAGE))
g.SendMessagePack(0)
logger.Error("gcg start")
}
// CheckAllInitFinish 检查所有玩家是否加载完成
func (g *GCGGame) CheckAllInitFinish() {
// 检查所有玩家是否加载完成
for _, controller := range g.controllerMap {
if controller.loadState != ControllerLoadState_InitFinish {
return
}
}
// TODO 可能会玩家中途退了 超时结束游戏
// 正式开始游戏
g.StartGame()
}
// AddMessagePack 添加操控者的待发送区GCG消息
func (g *GCGGame) AddMessagePack(controllerId uint32, actionType proto.GCGActionType, msgList ...*proto.GCGMessage) {
_, ok := g.controllerMsgPackMap[controllerId]
if !ok {
g.controllerMsgPackMap[controllerId] = make([]*proto.GCGMessagePack, 0, len(msgList)*5)
}
pack := &proto.GCGMessagePack{
ActionType: actionType,
MsgList: make([]*proto.GCGMessage, 0, len(msgList)),
// GCGMsgSelectOnStage GCG消息切换角色卡牌
func (g *GCGGame) GCGMsgSelectOnStage(controllerId uint32, cardGuid uint32, reason proto.GCGReason) *proto.GCGMessage {
gcgMsgClientPerform := &proto.GCGMsgSelectOnStage{
Reason: reason,
ControllerId: controllerId,
CardGuid: cardGuid,
}
// 将每个GCG消息添加进消息包中
for _, message := range msgList {
pack.MsgList = append(pack.MsgList, message)
gcgMessage := &proto.GCGMessage{
Message: &proto.GCGMessage_SelectOnStage{
SelectOnStage: gcgMsgClientPerform,
},
}
// 将消息包添加进待发送区
g.controllerMsgPackMap[controllerId] = append(g.controllerMsgPackMap[controllerId], pack)
return gcgMessage
}
// SendMessagePack 发送操控者的待发送区GCG消息
func (g *GCGGame) SendMessagePack(controllerId uint32) {
msgPackList, ok := g.controllerMsgPackMap[controllerId]
if !ok {
logger.Error("msg pack list error, controllerId: %v", controllerId)
return
// GCGMsgPVEIntention GCG消息PVE意向
func (g *GCGGame) GCGMsgPVEIntention(pveIntentionList ...*proto.GCGMsgPVEIntention) *proto.GCGMessage {
gcgMsgPVEIntention := &proto.GCGMsgPVEIntentionInfo{
IntentionMap: make(map[uint32]*proto.GCGMsgPVEIntention),
}
// 0代表广播给全体玩家
if controllerId == 0 {
g.serverSeqCounter++
for _, controller := range g.controllerMap {
GAME_MANAGER.SendGCGMessagePackNotify(controller, g.serverSeqCounter, msgPackList)
}
} else {
// 获取指定的操控者
controller, ok := g.controllerMap[controllerId]
if !ok {
logger.Error("controller is nil, controllerId: %v", controllerId)
return
}
g.serverSeqCounter++
GAME_MANAGER.SendGCGMessagePackNotify(controller, g.serverSeqCounter, msgPackList)
for _, intention := range pveIntentionList {
gcgMsgPVEIntention.IntentionMap[intention.CardGuid] = intention
}
// 记录发送的历史消息包
for _, pack := range msgPackList {
g.historyMsgPackList = append(g.historyMsgPackList, pack)
gcgMessage := &proto.GCGMessage{
Message: &proto.GCGMessage_PveIntentionInfo{
PveIntentionInfo: gcgMsgPVEIntention,
},
}
// 清空待发送区的数据
g.controllerMsgPackMap[controllerId] = make([]*proto.GCGMessagePack, 0, len(msgPackList))
return gcgMessage
}
// // CreateGameCardInfo 生成操控者卡牌信息
// func (g *GCGManager) CreateGameCardInfo(controller *GCGController, gcgDeck *model.GCGDeck) *GCGCardInfo {
//
// }
// GCGMsgDuelDataChange GCG消息切换回合
func (g *GCGGame) GCGMsgDuelDataChange() *proto.GCGMessage {
gcgMsgDuelDataChange := &proto.GCGMsgDuelDataChange{
Round: g.roundInfo.roundNum,
}
gcgMessage := &proto.GCGMessage{
Message: &proto.GCGMessage_DuelDataChange{
DuelDataChange: gcgMsgDuelDataChange,
},
}
return gcgMessage
}
// GCGMsgDiceRoll GCG消息摇骰子
func (g *GCGGame) GCGMsgDiceRoll(controllerId uint32, diceNum uint32, diceSideList []proto.GCGDiceSideType) *proto.GCGMessage {
gcgMsgDiceRoll := &proto.GCGMsgDiceRoll{
ControllerId: controllerId,
DiceNum: diceNum,
DiceSideList: diceSideList,
}
gcgMessage := &proto.GCGMessage{
Message: &proto.GCGMessage_DiceRoll{
DiceRoll: gcgMsgDiceRoll,
},
}
return gcgMessage
}
// GCGMsgUseSkill GCG消息使用技能
func (g *GCGGame) GCGMsgUseSkill(cardGuid uint32, skillId uint32) *proto.GCGMessage {
gcgMsgMsgUseSkill := &proto.GCGMsgUseSkill{
SkillId: skillId,
CardGuid: cardGuid,
}
gcgMessage := &proto.GCGMessage{
Message: &proto.GCGMessage_UseSkill{
UseSkill: gcgMsgMsgUseSkill,
},
}
return gcgMessage
}
// GCGMsgUseSkillEnd GCG消息使用技能结束
func (g *GCGGame) GCGMsgUseSkillEnd(cardGuid uint32, skillId uint32) *proto.GCGMessage {
gcgMsgMsgUseSkillEnd := &proto.GCGMsgUseSkillEnd{
SkillId: skillId,
CardGuid: cardGuid,
}
gcgMessage := &proto.GCGMessage{
Message: &proto.GCGMessage_UseSkillEnd{
UseSkillEnd: gcgMsgMsgUseSkillEnd,
},
}
return gcgMessage
}
// GetControllerByUserId 通过玩家Id获取GCGController对象
func (g *GCGGame) GetControllerByUserId(userId uint32) *GCGController {

View File

@@ -126,6 +126,7 @@ func (r *RouteManager) initRoute() {
r.registerRouter(cmd.GetOnlinePlayerInfoReq, GAME_MANAGER.GetOnlinePlayerInfoReq)
r.registerRouter(cmd.GCGAskDuelReq, GAME_MANAGER.GCGAskDuelReq)
r.registerRouter(cmd.GCGInitFinishReq, GAME_MANAGER.GCGInitFinishReq)
r.registerRouter(cmd.GCGOperationReq, GAME_MANAGER.GCGOperationReq)
}
func (r *RouteManager) RouteHandle(netMsg *mq.NetMsg) {

View File

@@ -363,6 +363,10 @@ func (t *TickManager) onTickSecond(now int64) {
}
}
}
// GCG游戏Tick
for _, game := range GCG_MANAGER.gameMap {
game.onTick()
}
}
func (t *TickManager) onTick200MilliSecond(now int64) {

View File

@@ -3,6 +3,7 @@ package game
import (
"hk4e/common/constant"
"hk4e/gs/model"
"hk4e/pkg/logger"
"hk4e/protocol/cmd"
"hk4e/protocol/proto"
@@ -158,7 +159,7 @@ func (g *GameManager) GCGAskDuelReq(player *model.Player, payloadMsg pb.Message)
// 阶段数据
Phase: &proto.GCGPhase{
PhaseType: game.roundInfo.phaseType,
AllowControllerMap: game.roundInfo.allowControllerMap,
AllowControllerMap: make(map[uint32]uint32),
},
},
}
@@ -178,7 +179,7 @@ func (g *GameManager) GCGAskDuelReq(player *model.Player, payloadMsg pb.Message)
playerField := &proto.GCGPlayerField{
Unk3300_IKJMGAHCFPM: 0,
// 卡牌图片
ModifyZoneMap: make(map[uint32]*proto.GCGZone, len(controller.cardMap)),
ModifyZoneMap: make(map[uint32]*proto.GCGZone, len(controller.cardList)),
Unk3300_GGHKFFADEAL: 0,
Unk3300_AOPJIOHMPOF: &proto.GCGZone{
CardList: []uint32{},
@@ -196,7 +197,7 @@ func (g *GameManager) GCGAskDuelReq(player *model.Player, payloadMsg pb.Message)
ControllerId: controller.controllerId,
// 卡牌位置
Unk3300_INDJNJJJNKL: &proto.GCGZone{
CardList: make([]uint32, 0, len(controller.cardMap)),
CardList: make([]uint32, 0, len(controller.cardList)),
},
Unk3300_EFNAEFBECHD: &proto.GCGZone{
CardList: []uint32{},
@@ -208,7 +209,7 @@ func (g *GameManager) GCGAskDuelReq(player *model.Player, payloadMsg pb.Message)
Unk3300_GLNIFLOKBPM: 0,
}
// 卡牌信息
for _, info := range controller.cardMap {
for _, info := range controller.cardList {
gcgCard := &proto.GCGCard{
TagList: info.tagList,
Guid: info.guid,
@@ -239,6 +240,8 @@ func (g *GameManager) GCGAskDuelReq(player *model.Player, payloadMsg pb.Message)
// 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)
@@ -276,12 +279,68 @@ func (g *GameManager) GCGInitFinishReq(player *model.Player, payloadMsg pb.Messa
game.CheckAllInitFinish()
}
// SendGCGMessagePackNotify 发送GCG消息包通知
func (g *GameManager) SendGCGMessagePackNotify(controller *GCGController, serverSeq uint32, msgPackList []*proto.GCGMessagePack) {
// 确保为玩家
if controller.player == nil {
// GCGOperationReq GCG游戏客户端操作请求
func (g *GameManager) GCGOperationReq(player *model.Player, payloadMsg pb.Message) {
req := payloadMsg.(*proto.GCGOperationReq)
// 获取玩家所在的游戏
game, ok := GCG_MANAGER.gameMap[player.GCGCurGameGuid]
if !ok {
g.CommonRetError(cmd.GCGOperationRsp, player, &proto.GCGOperationRsp{}, proto.Retcode_RET_GCG_GAME_NOT_RUNNING)
return
}
// 获取玩家的操控者对象
gameController := game.GetControllerByUserId(player.PlayerID)
if gameController == nil {
g.CommonRetError(cmd.GCGOperationRsp, player, &proto.GCGOperationRsp{}, proto.Retcode_RET_GCG_NOT_IN_GCG_DUNGEON)
return
}
switch req.Op.Op.(type) {
case *proto.GCGOperation_OpSelectOnStage:
// 选择角色卡牌
op := req.Op.GetOpSelectOnStage()
// 操作者是否拥有该卡牌
cardInfo := gameController.GetCardByGuid(op.CardGuid)
if cardInfo == nil {
GAME_MANAGER.CommonRetError(cmd.GCGOperationRsp, player, &proto.GCGOperationRsp{}, proto.Retcode_RET_GCG_SELECT_HAND_CARD_GUID_ERROR)
return
}
// 操控者选择角色牌
game.ControllerSelectChar(gameController, cardInfo, op.CostDiceIndexList)
case *proto.GCGOperation_OpReroll:
// 确认骰子不重投
op := req.Op.GetOpReroll()
diceSideList, ok := game.roundInfo.diceSideMap[gameController.controllerId]
if !ok {
g.CommonRetError(cmd.GCGOperationRsp, player, &proto.GCGOperationRsp{}, proto.Retcode_RET_GCG_DICE_INDEX_INVALID)
return
}
// 判断骰子索引是否有效
for _, diceIndex := range op.DiceIndexList {
if diceIndex > uint32(len(diceSideList)) {
g.CommonRetError(cmd.GCGOperationRsp, player, &proto.GCGOperationRsp{}, proto.Retcode_RET_GCG_DICE_INDEX_INVALID)
return
}
}
// 客户端更新操控者
game.AddMsgPack(0, proto.GCGActionType_GCG_ACTION_TYPE_NONE, game.GCGMsgUpdateController())
// 游戏行动阶段
game.GameChangePhase(proto.GCGPhaseType_GCG_PHASE_TYPE_PRE_MAIN)
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,
}
GAME_MANAGER.SendMsg(cmd.GCGOperationRsp, player.PlayerID, player.ClientSeq, gcgOperationRsp)
}
func (g *GameManager) SendGCGMessagePackNotify(controller *GCGController, serverSeq uint32, msgPackList []*proto.GCGMessagePack) {
// 确保加载完成
if controller.loadState != ControllerLoadState_InitFinish {
return
@@ -291,7 +350,16 @@ func (g *GameManager) SendGCGMessagePackNotify(controller *GCGController, server
ServerSeq: serverSeq,
MsgPackList: msgPackList,
}
GAME_MANAGER.SendMsg(cmd.GCGMessagePackNotify, controller.player.PlayerID, controller.player.ClientSeq, gcgMessagePackNotify)
// 根据操控者的类型发送消息包
switch controller.controllerType {
case ControllerType_Player:
GAME_MANAGER.SendMsg(cmd.GCGMessagePackNotify, controller.player.PlayerID, controller.player.ClientSeq, gcgMessagePackNotify)
case ControllerType_AI:
controller.ai.ReceiveGCGMessagePackNotify(gcgMessagePackNotify)
default:
logger.Error("controller type error, %v", controller.controllerType)
return
}
}
// PacketGCGGameBriefDataNotify GCG游戏简要数据通知
@@ -309,7 +377,7 @@ func (g *GameManager) PacketGCGGameBriefDataNotify(player *model.Player, busines
gcgPlayerBriefData := &proto.GCGPlayerBriefData{
ControllerId: controller.controllerId,
ProfilePicture: new(proto.ProfilePicture),
CardIdList: make([]uint32, 0, len(controller.cardMap)),
CardIdList: make([]uint32, 0, len(controller.cardList)),
}
// 玩家信息
if controller.player != nil {

View File

@@ -260,11 +260,14 @@ func (c *CmdProtoMap) registerAllMessage() {
c.registerMessage(GCGTCTavernInfoNotify, &proto.GCGTCTavernInfoNotify{}) // GCG酒馆信息通知
c.registerMessage(GCGTavernNpcInfoNotify, &proto.GCGTavernNpcInfoNotify{}) // GCG酒馆NPC信息通知
c.registerMessage(GCGGameBriefDataNotify, &proto.GCGGameBriefDataNotify{}) // GCG游戏简要数据通知
c.registerMessage(GCGAskDuelReq, &proto.GCGAskDuelReq{}) // GCG决斗请求
c.registerMessage(GCGAskDuelRsp, &proto.GCGAskDuelRsp{}) // GCG决斗响应
c.registerMessage(GCGInitFinishReq, &proto.GCGInitFinishReq{}) // GCG初始化完成请求
c.registerMessage(GCGInitFinishRsp, &proto.GCGInitFinishRsp{}) // GCG初始化完成响应
c.registerMessage(GCGMessagePackNotify, &proto.GCGMessagePackNotify{}) // GCG消息包通知
c.registerMessage(GCGAskDuelReq, &proto.GCGAskDuelReq{}) // GCG游戏对局信息请求
c.registerMessage(GCGAskDuelRsp, &proto.GCGAskDuelRsp{}) // GCG游戏对局信息响应
c.registerMessage(GCGInitFinishReq, &proto.GCGInitFinishReq{}) // GCG游戏初始化完成请求
c.registerMessage(GCGInitFinishRsp, &proto.GCGInitFinishRsp{}) // GCG游戏初始化完成响应
c.registerMessage(GCGMessagePackNotify, &proto.GCGMessagePackNotify{}) // GCG游戏消息包通知
c.registerMessage(GCGHeartBeatNotify, &proto.GCGHeartBeatNotify{}) // GCG游戏心跳包通知
c.registerMessage(GCGOperationReq, &proto.GCGOperationReq{}) // GCG游戏客户端操作请求
c.registerMessage(GCGOperationRsp, &proto.GCGOperationRsp{}) // GCG游戏客户端操作响应
// // TODO 客户端开始GCG游戏
// c.registerMessage(GCGStartChallengeByCheckRewardReq, &proto.GCGStartChallengeByCheckRewardReq{}) // GCG开始挑战来自检测奖励请求