From 691cfc878baa81a9adf086ff3af12f5e7d28da66 Mon Sep 17 00:00:00 2001 From: 1 <1@72wo.cn> Date: Sun, 21 Sep 2025 14:56:37 +0000 Subject: [PATCH] =?UTF-8?q?feat(capture):=20=E9=87=8D=E6=9E=84=E6=8D=95?= =?UTF-8?q?=E6=8D=89=E7=B3=BB=E7=BB=9F=EF=BC=8C=E5=AE=9E=E7=8E=B0=E7=8A=B6?= =?UTF-8?q?=E6=80=81=E5=80=8D=E7=8E=87=E8=AE=A1=E7=AE=97=E5=92=8C=E4=BF=9D?= =?UTF-8?q?=E5=BA=95=E6=9C=BA=E5=88=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- common/data/xmlres/file.go | 11 +- common/data/xmlres/item.go | 26 +- logic/controller/CreatePlayer.go | 4 +- logic/controller/fight.go | 14 +- logic/controller/login.go | 4 +- logic/service/common/playeri.go | 7 +- logic/service/fight/Capture.go | 340 --------------------------- logic/service/fight/fightc.go | 63 ++--- logic/service/fight/info/ctx.go | 50 ++++ logic/service/fight/info/info.go | 53 +++-- logic/service/fight/input/Capture.go | 129 ++++++++++ logic/service/fight/input/input.go | 157 ++++++++++++- logic/service/fight/playeraction.go | 2 +- logic/service/player/ai.go | 40 +--- logic/service/player/base.go | 50 ++++ logic/service/player/new.go | 54 +++++ logic/service/player/player.go | 98 ++------ 17 files changed, 563 insertions(+), 539 deletions(-) delete mode 100644 logic/service/fight/Capture.go create mode 100644 logic/service/fight/info/ctx.go create mode 100644 logic/service/fight/input/Capture.go create mode 100644 logic/service/player/base.go create mode 100644 logic/service/player/new.go diff --git a/common/data/xmlres/file.go b/common/data/xmlres/file.go index 4318b0476..9873f2f98 100644 --- a/common/data/xmlres/file.go +++ b/common/data/xmlres/file.go @@ -36,6 +36,7 @@ var ( NatureRootMap map[int]NatureItem EffectMAP map[int]NewSeIdx PlayerEffectMAP map[int]NewSeIdx + ItemsMAP map[int]Item ) func initfile() { @@ -43,6 +44,12 @@ func initfile() { path = path1 + "/public/config/" MapConfig = getXml[Maps](path + "210.xml") ItemsConfig = getXml[Items](path + "43.xml") + + ItemsMAP = utils.ToMap[Item, int](ItemsConfig.Items, func(m Item) int { + return m.ID + + }) + TalkConfig = getXml[TalkCount](path + "talk.xml") Monster := getXml[MonsterRoot](path + "地图配置野怪.xml") @@ -52,11 +59,7 @@ func initfile() { }) Skill := getXml[MovesTbl](path + "227.xml") - // SkillMap = utils.ToMap[Move, int](Skill.Moves, func(m Move) int { - // return m.ID - - // }) SkillMap = make(map[int]Move, len(Skill.Moves)) for _, v := range Skill.Moves { v.SideEffectS = ParseSideEffectArgs(v.SideEffect) diff --git a/common/data/xmlres/item.go b/common/data/xmlres/item.go index eddd11f77..6f56fc4a3 100644 --- a/common/data/xmlres/item.go +++ b/common/data/xmlres/item.go @@ -10,29 +10,31 @@ type Items struct { Items []Item `xml:"Item"` // 包含所有物品配置 } -// Item 单个物品配置,对应标签 type Item struct { ID int `xml:"ID,attr"` // 物品ID(与items.xml一致) Name string `xml:"Name,attr"` // 物品名称 - Rarity int `xml:"Rarity,attr,omitempty"` // 稀有度(可选) + Rarity int `xml:"Rarity,attr,omitempty"` // 稀有度 + ItemType int `xml:"ItemType,attr"` // 物品类型 + Max int `xml:"Max,attr"` // 最大堆叠数量 Price int `xml:"Price,attr"` // 价格 + Bonus string `xml:"Bonus,attr"` // 倍率(如捕捉胶囊的加成倍数) Tradability int `xml:"Tradability,attr"` // 可交易性(0/1) VipTradability int `xml:"VipTradability,attr"` // VIP可交易性(0/1) - DailyKey int `xml:"DailyKey,attr,omitempty"` // 每日限制键值(可选) - DailyOutMax int `xml:"DailyOutMax,attr,omitempty"` // 每日最大产出(可选) - Wd int `xml:"wd,attr"` // 未知属性(根据原XML保留) + DailyKey int `xml:"DailyKey,attr,omitempty"` // 每日限制键值 + DailyOutMax int `xml:"DailyOutMax,attr,omitempty"` // 每日最大产出 + Wd int `xml:"wd,attr"` // 未知属性 UseMax int `xml:"UseMax,attr"` // 最大使用次数 LifeTime int `xml:"LifeTime,attr"` // 生命周期(0为永久) Purpose int `xml:"purpose,attr"` // 用途标识 - Bean int `xml:"Bean,attr,omitempty"` // 豆子数量(可选) + Bean int `xml:"Bean,attr,omitempty"` // 豆子数量 Hide int `xml:"Hide,attr"` // 是否隐藏(0/1) - Sort int `xml:"Sort,attr,omitempty"` // 排序序号(可选) - Des string `xml:"des,attr"` // 物品用途(根据说明补充) - Color string `xml:"color,attr,omitempty"` // 装备名字颜色(可选) - Level int `xml:"level,attr,omitempty"` // 装备等级(星星,可选) + Sort int `xml:"Sort,attr,omitempty"` // 排序序号 + Des string `xml:"des,attr"` // 物品用途 + Color string `xml:"color,attr,omitempty"` // 装备名字颜色 + Level int `xml:"level,attr,omitempty"` // 装备等级(星星) - Pet *Pet `xml:"pet,omitempty"` // 精灵属性子节点(可选) - TeamPK *TeamPK `xml:"teamPK,omitempty"` // 要塞保卫战子节点(可选) + Pet *Pet `xml:"pet,omitempty"` // 精灵属性子节点 + TeamPK *TeamPK `xml:"teamPK,omitempty"` // 要塞保卫战子节点 } // Pet 精灵属性子节点,对应标签 diff --git a/logic/controller/CreatePlayer.go b/logic/controller/CreatePlayer.go index 6e39148c3..42a2cf388 100644 --- a/logic/controller/CreatePlayer.go +++ b/logic/controller/CreatePlayer.go @@ -25,9 +25,9 @@ func (h *Controller) ChangePlayerName(data *login.ChangePlayerNameInboundInfo, c c.Info.Nick = newnice result = &login.ChangePlayerNameOutboundInfo{ Nickname: newnice, - UserID: c.ID(), + UserID: c.Info.UserID, } - space.GetSpace(c.MapID()).Range(func(playerID uint32, player common.PlayerI) bool { + space.GetSpace(c.Info.MapID).Range(func(playerID uint32, player common.PlayerI) bool { player.SendPack(data.Head.Pack(&result)) return true diff --git a/logic/controller/fight.go b/logic/controller/fight.go index 5e9bba42d..caa3f3604 100644 --- a/logic/controller/fight.go +++ b/logic/controller/fight.go @@ -1,6 +1,7 @@ package controller import ( + "blazing/common/data/xmlres" "blazing/common/socket/errorcode" "blazing/logic/service/fight" @@ -12,7 +13,7 @@ import ( func (h Controller) PlayerFightBoss(data *fight.ChallengeBossInboundInfo, c *player.Player) (result *fight.NullOutboundInfo, err errorcode.ErrorCode) { var petid int var mo *model.PetInfo - if c.MapID() == 515 && data.BossId == 0 { //说明是新手,随机生成 + if c.Info.MapID == 515 && data.BossId == 0 { //说明是新手,随机生成 switch c.Info.PetList[0].ID { case 1: @@ -36,7 +37,10 @@ func (h Controller) PlayerFightBoss(data *fight.ChallengeBossInboundInfo, c *pla if c.FightC != nil { return nil, errorcode.ErrorCodes.ErrOnlineOver6HoursCannotFight } - ai := player.NewAI_player(model.PlayerInfo{}, *mo) + moinfo := &model.PlayerInfo{} + moinfo.Nick = xmlres.PetMAP[int(mo.ID)].DefName + moinfo.PetList = append(moinfo.PetList, *mo) + ai := player.NewAI_player(moinfo) fight.NewFight(info.BattleMode.PVE, c, ai) return nil, -1 @@ -60,7 +64,11 @@ func (h Controller) OnPlayerFightNpcMonster(data *fight.FightNpcMonsterInboundIn if c.FightC != nil { return nil, errorcode.ErrorCodes.ErrOnlineOver6HoursCannotFight } - ai := player.NewAI_player(model.PlayerInfo{}, *mo) + moinfo := &model.PlayerInfo{} + moinfo.Nick = xmlres.PetMAP[int(mo.ID)].DefName + moinfo.PetList = append(moinfo.PetList, *mo) + ai := player.NewAI_player(moinfo) + fight.NewFight(info.BattleMode.PVE, c, ai) return nil, -1 diff --git a/logic/controller/login.go b/logic/controller/login.go index 93ca82e73..f5f42125a 100644 --- a/logic/controller/login.go +++ b/logic/controller/login.go @@ -60,7 +60,7 @@ func (h *Controller) Login(data *login.InInfo, c *player.Conn) (result *login.Ou }() } - if t.MapID() > 10000 { + if t.Info.MapID > 10000 { t.Info.MapID = 1 } @@ -82,7 +82,7 @@ func (h *Controller) Login(data *login.InInfo, c *player.Conn) (result *login.Ou defer space.GetSpace(t.Info.MapID).Set(t.Info.UserID, t).Range(func(playerID uint32, player common.PlayerI) bool { player.SendPack(t1.Pack(&tt)) - + return true }) diff --git a/logic/service/common/playeri.go b/logic/service/common/playeri.go index 6ee47eb81..c4f8a999b 100644 --- a/logic/service/common/playeri.go +++ b/logic/service/common/playeri.go @@ -6,15 +6,14 @@ import ( ) type PlayerI interface { - ID() uint32 - MapID() uint32 GetAction() - GetPetInfo() []model.PetInfo + GetPlayerCaptureContext() *info.PlayerCaptureContext + Roll(int, int) (bool, float64, float64) SendPack(b []byte) error SendReadyToFightInfo(info.FightStartOutboundInfo) SendNoteReadyToFightInfo(info.NoteReadyToFightInfo) SendFightEndInfo(info.FightOverInfo) - GetInfo() model.PlayerInfo + GetInfo() *model.PlayerInfo SendAttackValue(info.AttackValueS) SendChangePet(info.ChangePetInfo) SetFightC(FightI) diff --git a/logic/service/fight/Capture.go b/logic/service/fight/Capture.go deleted file mode 100644 index 768876add..000000000 --- a/logic/service/fight/Capture.go +++ /dev/null @@ -1,340 +0,0 @@ -package fight - -import ( - "fmt" - "math" - "math/rand" - "time" -) - -// 异常状态类型 -const ( - StatusNormal = iota - StatusParalysis - StatusPoison - StatusSleep - StatusFreeze -) - -// 异常状态倍率 -var statusBonuses = map[int]float64{ - StatusNormal: 1.0, - StatusParalysis: 1.5, - StatusPoison: 1.5, - StatusSleep: 2.0, - StatusFreeze: 2.0, -} - -// 胶囊配置(ItemID -> Bonus) -var capsuleBonuses = map[int]float64{ - 300001: 1.0, // 普通精灵胶囊 - 300002: 1.5, // 中级精灵胶囊 - 300003: 2.0, // 高级精灵胶囊 - 300004: 3.0, // 超级精灵胶囊 - 300005: 4.0, // 特级精灵胶囊 - 300006: 256.0, // 无敌精灵胶囊 - 300007: 256.0, // 超能胶囊 - 300008: 2.0, // 赫星精灵胶囊 - 300009: 256.0, // 时空精灵胶囊 - 300010: 256.0, // 无敌精灵胶囊Ω -} - -// PlayerCaptureContext 用户捕捉上下文(每次登录创建) -type PlayerCaptureContext struct { - random *rand.Rand - denominator int // 统一分母=1000 - decayFactor float64 // 衰减系数 - minDecayNum int // 衰减分子最小值 - guarantees map[int]int // 每种精灵的保底分子 map[petId]numerator -} - -// CaptureParams 捕捉参数 -type CaptureParams struct { - PetID int // 精灵ID - MaxHP int // 目标最大HP - CurrentHP int // 目标当前HP - CatchRate int // 目标捕获率 - ItemID int // 使用的道具ID - StatusType int // 状态类型 - OwnedCount int // 已拥有数量(-1=保底,0=锁定,≥1=衰减) -} - -// NewPlayerCaptureContext 创建用户捕捉上下文(每次登录调用) -func NewPlayerCaptureContext(decayFactor float64, minDecayNum int) *PlayerCaptureContext { - if minDecayNum <= 0 { - minDecayNum = 1 - } - if decayFactor <= 0 || decayFactor >= 1 { - decayFactor = 0.3 - } - return &PlayerCaptureContext{ - random: rand.New(rand.NewSource(time.Now().UnixNano())), - denominator: 1000, // 统一分母=1000 - decayFactor: decayFactor, - minDecayNum: minDecayNum, - guarantees: make(map[int]int), // 初始化map - } -} - -// getItemBonus 获取道具倍率 -func getItemBonus(itemID int) float64 { - if bonus, ok := capsuleBonuses[itemID]; ok { - return bonus - } - return 1.0 // 默认普通胶囊倍率 -} - -// GetStatusBonus 获取状态倍率 -func GetStatusBonus(statusType int) float64 { - if bonus, ok := statusBonuses[statusType]; ok { - return bonus - } - return statusBonuses[StatusNormal] -} - -// calcBaseA 按公式计算a值 -func (c *PlayerCaptureContext) calcBaseA(params CaptureParams) int { - catchRate := params.CatchRate - if catchRate < 3 { - catchRate = 3 - } - if params.MaxHP <= 0 { - params.MaxHP = 1 - } - currentHP := params.CurrentHP - if currentHP <= 0 { - currentHP = 1 - } - - hpRatio := (3.0*float64(params.MaxHP) - 2.0*float64(currentHP)) / (3.0 * float64(params.MaxHP)) - if hpRatio < 0 { - hpRatio = 0 - } - - itemBonus := getItemBonus(params.ItemID) - statusBonus := GetStatusBonus(params.StatusType) - - return int(hpRatio * float64(catchRate) * itemBonus * statusBonus) -} - -// calcBaseRate 按公式计算基础成功率 -func (c *PlayerCaptureContext) calcBaseRate(params CaptureParams) float64 { - // Bonus >= 255 必定成功 - if getItemBonus(params.ItemID) >= 255 { - return 1.0 - } - - a := c.calcBaseA(params) - - if a >= 255 { - return 1.0 - } - - g := int(1048560.0 / math.Floor(math.Sqrt(math.Floor(math.Sqrt(math.Floor(16711680.0/float64(a))))))) - - return math.Pow(float64(g)/65536.0, 4.0) -} - -// getGuaranteeNumerator 获取指定精灵的保底分子(默认1) -func (c *PlayerCaptureContext) getGuaranteeNumerator(petID int) int { - if num, ok := c.guarantees[petID]; ok { - return num - } - return 1 -} - -// setGuaranteeNumerator 设置指定精灵的保底分子 -func (c *PlayerCaptureContext) setGuaranteeNumerator(petID, numerator int) { - c.guarantees[petID] = numerator -} - -// applyModeModifier 应用模式修正(保底/衰减/锁定) -func (c *PlayerCaptureContext) applyModeModifier(baseRate float64, params CaptureParams) (float64, string, float64) { - guaranteeBonus := 0.0 - switch { - case params.OwnedCount == 0: - // 锁定模式:成功率0 - return 0.0, "锁定模式(ownedCount=0)", 0.0 - case params.OwnedCount > 0: - // 衰减模式:已拥有数量越多,成功率越低 - decay := math.Pow(1-c.decayFactor, float64(params.OwnedCount)) - modified := baseRate * decay - if modified < 0 { - modified = 0 - } - return modified, "衰减模式", 0.0 - default: // params.OwnedCount == -1 - // 保底模式:失败叠加分子(按精灵ID独立计算) - num := c.getGuaranteeNumerator(params.PetID) - guaranteeBonus = float64(num) / float64(c.denominator) // 分母=1000 - modified := baseRate + guaranteeBonus - if modified > 1.0 { - modified = 1.0 - } - return modified, "保底模式", guaranteeBonus * 100 // 百分比形式 - } -} - -// Capture 执行捕捉 -func (c *PlayerCaptureContext) Capture(params CaptureParams) (bool, CaptureDetails) { - // 记录当前保底值(用于显示) - currentGuarantee := c.getGuaranteeNumerator(params.PetID) - - // 特殊胶囊必定成功(Bonus>=255) - if getItemBonus(params.ItemID) >= 255 { - // 若是保底模式,成功后重置该精灵的保底分子 - if params.OwnedCount == -1 { - c.setGuaranteeNumerator(params.PetID, 1) - } - return true, CaptureDetails{ - Success: true, - Mode: "特殊胶囊必定成功", - BaseRate: 100.0, - ModifiedRate: 100.0, - GuaranteeBonus: float64(currentGuarantee) / float64(c.denominator) * 100, - Details: fmt.Sprintf("道具ID=%d,必定成功", params.ItemID), - } - } - - // 锁定模式直接失败 - if params.OwnedCount == 0 { - return false, CaptureDetails{ - Success: false, - Mode: "锁定模式(ownedCount=0)", - BaseRate: 0.0, - ModifiedRate: 0.0, - GuaranteeBonus: 0.0, - Details: "已拥有数量为0,无法捕捉", - } - } - - baseRate := c.calcBaseRate(params) - modifiedRate, mode, guaranteeBonus := c.applyModeModifier(baseRate, params) - - // 随机判定 - success := c.random.Float64() < modifiedRate - - // 更新保底状态(仅对保底模式) - if params.OwnedCount == -1 { - if success { - // 成功:重置该精灵的保底分子为1 - c.setGuaranteeNumerator(params.PetID, 1) - } else { - // 失败:该精灵的保底分子+1 - c.setGuaranteeNumerator(params.PetID, currentGuarantee+1) - } - } - - return success, CaptureDetails{ - Success: success, - Mode: mode, - BaseRate: baseRate * 100, - ModifiedRate: modifiedRate * 100, - GuaranteeBonus: guaranteeBonus, - Details: fmt.Sprintf("a=%d, g=%.0f, 分母=1000, 当前保底分子=%d", - c.calcBaseA(params), - math.Floor(math.Sqrt(math.Floor(math.Sqrt(math.Floor(16711680.0/float64(c.calcBaseA(params))))))), - currentGuarantee), - } -} - -// CaptureDetails 捕捉详情 -type CaptureDetails struct { - Success bool - Mode string - BaseRate float64 - ModifiedRate float64 - GuaranteeBonus float64 // 百分比 - Details string -} - -func main() { - // 每次登录创建用户捕捉上下文 - ctx := NewPlayerCaptureContext(0.1, 1) - - fmt.Println("=== 测试三种模式 ===") - - // 测试1:保底模式(ownedCount=-1)- 不同精灵独立计算 - fmt.Println("\n--- 保底模式测试(不同精灵独立计算) ---") - paramsA := CaptureParams{ - PetID: 1001, // 精灵A - MaxHP: 20, - CurrentHP: 1, - CatchRate: 45, - ItemID: 300003, // 高级精灵胶囊 - StatusType: StatusNormal, - OwnedCount: -1, // 保底模式 - } - - paramsB := CaptureParams{ - PetID: 1002, // 精灵B - MaxHP: 50, - CurrentHP: 5, - CatchRate: 60, - ItemID: 300002, // 中级精灵胶囊 - StatusType: StatusNormal, - OwnedCount: -1, // 保底模式 - } - - // 交替捕捉精灵A和精灵B,验证保底分子独立 - for i := 1; i <= 10; i++ { - successA, detailsA := ctx.Capture(paramsA) - fmt.Printf("第%d轮-精灵A:%v | 模式=%s | 基础=%.2f%% | 实际=%.2f%% | 保底加成=%.2f%%\n", - i, successA, detailsA.Mode, detailsA.BaseRate, detailsA.ModifiedRate, detailsA.GuaranteeBonus) - - successB, detailsB := ctx.Capture(paramsB) - fmt.Printf("第%d轮-精灵B:%v | 模式=%s | 基础=%.2f%% | 实际=%.2f%% | 保底加成=%.2f%%\n", - i, successB, detailsB.Mode, detailsB.BaseRate, detailsB.ModifiedRate, detailsB.GuaranteeBonus) - } - - // 测试2:衰减模式(ownedCount递增) - fmt.Println("\n--- 衰减模式测试(ownedCount递增) ---") - paramsDecay := CaptureParams{ - PetID: 2001, - MaxHP: 100, - CurrentHP: 10, - CatchRate: 3, - ItemID: 300003, // 高级精灵胶囊 - StatusType: StatusSleep, - OwnedCount: 1, // 衰减模式起始 - } - - for i := 1; i <= 50; i++ { - paramsDecay.OwnedCount = 2 - success, details := ctx.Capture(paramsDecay) - fmt.Printf("拥有%d只:%v | 模式=%s | 基础=%.2f%% | 实际=%.2f%%\n", - paramsDecay.OwnedCount, success, details.Mode, details.BaseRate, details.ModifiedRate) - } - - // 测试3:锁定模式(ownedCount=0) - fmt.Println("\n--- 锁定模式测试 ---") - paramsLock := CaptureParams{ - PetID: 3001, - MaxHP: 100, - CurrentHP: 1, - CatchRate: 255, - ItemID: 300004, // 超级精灵胶囊 - StatusType: StatusFreeze, - OwnedCount: 0, // 锁定模式 - } - - successLock, detailsLock := ctx.Capture(paramsLock) - fmt.Printf("捕捉结果:%v | 模式=%s | 基础=%.2f%% | 实际=%.2f%%\n", - successLock, detailsLock.Mode, detailsLock.BaseRate, detailsLock.ModifiedRate) - - // 测试4:特殊胶囊必定成功 - fmt.Println("\n--- 特殊胶囊测试 ---") - paramsSpecial := CaptureParams{ - PetID: 4001, - MaxHP: 1000, - CurrentHP: 1000, - CatchRate: 1, - ItemID: 300006, // 无敌精灵胶囊 - StatusType: StatusNormal, - OwnedCount: -1, // 保底模式 - } - - successSpecial, detailsSpecial := ctx.Capture(paramsSpecial) - fmt.Printf("捕捉结果:%v | 模式=%s | 基础=%.2f%% | 实际=%.2f%% | 保底加成=%.2f%%\n", - successSpecial, detailsSpecial.Mode, detailsSpecial.BaseRate, detailsSpecial.ModifiedRate, detailsSpecial.GuaranteeBonus) -} diff --git a/logic/service/fight/fightc.go b/logic/service/fight/fightc.go index 1cf0b3e82..1bfd7ff5c 100644 --- a/logic/service/fight/fightc.go +++ b/logic/service/fight/fightc.go @@ -50,7 +50,7 @@ func (f *FightC) GetInputByPlayer(c common.PlayerI, isOpposite bool) *input.Inpu func (f *FightC) GetInputByAction(c BattleActionI, isOpposite bool) *input.Input { // 判断动作所属玩家是否为我方 - isOurAction := c.GetPlayerID() == f.Our.Player.ID() + isOurAction := c.GetPlayerID() == f.Our.Player.GetInfo().UserID // 根据isOpposite决定是否返回相反方向的输入 if isOurAction == !isOpposite { @@ -61,7 +61,7 @@ func (f *FightC) GetInputByAction(c BattleActionI, isOpposite bool) *input.Input // 玩家使用技能 func (f *FightC) GetCurrPET(c common.PlayerI) *info.BattlePetEntity { - if f.Our.Player.ID() == c.ID() { + if f.Our.Player.GetInfo().UserID == c.GetInfo().UserID { return f.Our.CurrentPet } else { return f.Opp.CurrentPet @@ -81,10 +81,10 @@ func (f *FightC) initplayer(c common.PlayerI, opp bool) { temp := input.NewInput(f, c) temp.AllPet = make([]*info.BattlePetEntity, 0) - for i := 0; i < len(c.GetPetInfo()); i++ { + for i := 0; i < len(c.GetInfo().PetList); i++ { if f.Info.MAXPET == 0 || i < int(f.Info.MAXPET) { - temp.AllPet = append(temp.AllPet, info.CreateBattlePetEntity(&c.GetPetInfo()[i], f.rand)) + temp.AllPet = append(temp.AllPet, info.CreateBattlePetEntity(&c.GetInfo().PetList[i], f.rand)) } } @@ -141,7 +141,7 @@ func (f *FightC) initplayer(c common.PlayerI, opp bool) { // 创建新战斗,邀请方和被邀请方,或者玩家和野怪方 func NewFight(i info.EnumBattleMode, p1 common.PlayerI, p2 common.PlayerI) *FightC { f := &FightC{} - f.ownerID = p1.ID() + f.ownerID = p1.GetInfo().UserID f.Info.FightId = i //房主 switch i { case info.BattleMode.PVP_6V6: @@ -149,7 +149,7 @@ func NewFight(i info.EnumBattleMode, p1 common.PlayerI, p2 common.PlayerI) *Figh case info.BattleMode.PVP_1V1: f.Info.MAXPET = 1 } - seed := f.StartTime.UnixNano() ^ int64(p1.ID()) ^ int64(p2.ID()) // ^ int64(f.Round) // 用异或运算混合多维度信息 + seed := f.StartTime.UnixNano() ^ int64(p1.GetInfo().UserID) ^ int64(p2.GetInfo().UserID) // ^ int64(f.Round) // 用异或运算混合多维度信息 f.rand = rand.New(rand.NewSource(seed)) f.Info = info.NoteReadyToFightInfo{ @@ -179,7 +179,7 @@ func (b *FightC) IsWin(c *input.Input, cache uint32) bool { var tt []*info.BattlePetEntity bbb := b.Our.AllPet - if c.Player.ID() == b.ownerID { //如果是房主 + if c.Player.GetInfo().UserID == b.ownerID { //如果是房主 bbb = b.Opp.AllPet } else { bbb = b.Our.AllPet @@ -214,7 +214,7 @@ func (f *FightC) Broadcast(t func(ff *input.Input)) { func (f *FightC) battleLoop() { f.StartTime = time.Now() f.actionChan = make(chan BattleActionI, 2) // 初始化全局操作通道 - fmt.Println("战斗开始精灵", f.Our.Player.GetPetInfo()[0].CatchTime) + fmt.Println("战斗开始精灵", f.Our.Player.GetInfo().PetList[0].CatchTime) //战斗开始前操作 for { @@ -240,14 +240,14 @@ func (f *FightC) battleLoop() { break } - if action.GetPlayerID() != f.Our.Player.ID() && action.GetPlayerID() != f.Opp.Player.ID() { + if action.GetPlayerID() != f.Our.Player.GetInfo().UserID && action.GetPlayerID() != f.Opp.Player.GetInfo().UserID { continue } if a, isExpelled := action.(*ActiveSwitchAction); isExpelled { f.Broadcast(func(ff *input.Input) { - if ff.Player.ID() == a.PlayerID { //先给自身广播 + if ff.Player.GetInfo().UserID == a.PlayerID { //先给自身广播 ff.Player.SendChangePet(a.Reason) } @@ -279,13 +279,13 @@ func (f *FightC) battleLoop() { case <-timeout: - if _, exists := actions[f.Our.Player.ID()]; !exists { - actions[f.Our.Player.ID()] = &SystemGiveUpAction{ - BaseAction: NewBaseAction(f.Our.Player.ID())} //系统选择出手 + if _, exists := actions[f.Our.Player.GetInfo().UserID]; !exists { + actions[f.Our.Player.GetInfo().UserID] = &SystemGiveUpAction{ + BaseAction: NewBaseAction(f.Our.Player.GetInfo().UserID)} //系统选择出手 } - if _, exists := actions[f.Opp.Player.ID()]; !exists { + if _, exists := actions[f.Opp.Player.GetInfo().UserID]; !exists { - actions[f.Opp.Player.ID()] = &SystemGiveUpAction{BaseAction: NewBaseAction(f.Opp.Player.ID())} //系统选择出手 + actions[f.Opp.Player.GetInfo().UserID] = &SystemGiveUpAction{BaseAction: NewBaseAction(f.Opp.Player.GetInfo().UserID)} //系统选择出手 } } @@ -293,8 +293,8 @@ func (f *FightC) battleLoop() { // 双方动作齐了,取出来结算 //todo 如果一方没有选择,实际上就是后端判断PP是否还有,前端是直接发的 - p1Action := actions[f.Our.Player.ID()] - p2Action := actions[f.Opp.Player.ID()] + p1Action := actions[f.Our.Player.GetInfo().UserID] + p2Action := actions[f.Opp.Player.GetInfo().UserID] fmt.Println("开始结算回合") var BattleActionI [2]BattleActionI @@ -331,16 +331,21 @@ func (f *FightC) battleLoop() { if ok { //如果获取玩家 if ism && mo.CanCapture { //如果获取到IA - tt.Service.PetAdd(*f.Opp.CurrentPet.Info) - tt.CatchPetInfo(info.CatchMonsterOutboundInfo{ - CatchTime: uint32(f.Opp.CurrentPet.Info.CatchTime), - PetId: uint32(f.Opp.CurrentPet.ID), - }) - ff.Player.SendFightEndInfo(info.FightOverInfo{ - WinnerId: f.ownerID, - }) - f.closefight = true + ok, _ := f.Our.Capture(f.Opp.CurrentPet, faction.ItemID, -1) + if ok { //todo 待补充 + tt.Service.PetAdd(*f.Opp.CurrentPet.Info) + tt.CatchPetInfo(info.CatchMonsterOutboundInfo{ + CatchTime: uint32(f.Opp.CurrentPet.Info.CatchTime), + PetId: uint32(f.Opp.CurrentPet.ID), + }) + ff.Player.SendFightEndInfo(info.FightOverInfo{ + + WinnerId: f.ownerID, + }) + f.closefight = true + } + } else { //说明不是可以捕捉的 tt.CatchPetInfo(info.CatchMonsterOutboundInfo{}) } @@ -520,9 +525,9 @@ func (f *FightC) enterturn(fattack, sattack BattleActionI) { if f.IsWin(attacker, defender.CurrentPet.Info.CatchTime) { //然后检查是否战斗结束 var WinnerId uint32 if i == 0 { - WinnerId = f.First.Player.ID() + WinnerId = f.First.Player.GetInfo().UserID } else { - WinnerId = f.Second.Player.ID() + WinnerId = f.Second.Player.GetInfo().UserID } defer f.Broadcast(func(ff *input.Input) { //todo 将血量和技能pp传回enterturn @@ -568,7 +573,7 @@ func (f *FightC) enterturn(fattack, sattack BattleActionI) { f.Broadcast(func(ff *input.Input) { for _, v := range f.Switch { - if ff.Player.ID() != v.PlayerID { + if ff.Player.GetInfo().UserID != v.PlayerID { ff.Player.SendChangePet(v.Reason) } diff --git a/logic/service/fight/info/ctx.go b/logic/service/fight/info/ctx.go new file mode 100644 index 000000000..642bdcbc7 --- /dev/null +++ b/logic/service/fight/info/ctx.go @@ -0,0 +1,50 @@ +package info + +import "math/rand" + +// PlayerCaptureContext 用户捕捉上下文(每次登录创建) +type PlayerCaptureContext struct { + Random *rand.Rand + Denominator int // 统一分母=1000 + DecayFactor float64 // 衰减系数 + MinDecayNum int // 衰减分子最小值 + Guarantees map[int]int // 按分母分组的保底分子 map[denominator]numerator +} + +// Roll 通用概率判定(带共享保底) +func (c *PlayerCaptureContext) Roll(numerator, denominator int) (bool, float64, float64) { + if denominator <= 0 { + return false, 0, 0 + } + + base := float64(numerator) / float64(denominator) + bonus := float64(c.getGuaranteeNumerator(denominator)) / float64(denominator) + total := base + bonus + if total > 1.0 { + total = 1.0 + } + + success := c.Random.Float64() < total + + if success { + c.resetGuarantee(denominator) + } else { + c.increaseGuarantee(denominator) + } + + return success, base * 100, bonus * 100 +} + +// 保底操作 +func (c *PlayerCaptureContext) getGuaranteeNumerator(denominator int) int { + if num, ok := c.Guarantees[denominator]; ok { + return num + } + return 0 +} +func (c *PlayerCaptureContext) increaseGuarantee(denominator int) { + c.Guarantees[denominator]++ +} +func (c *PlayerCaptureContext) resetGuarantee(denominator int) { + c.Guarantees[denominator] = 0 +} diff --git a/logic/service/fight/info/info.go b/logic/service/fight/info/info.go index 15c698e19..dcf271024 100644 --- a/logic/service/fight/info/info.go +++ b/logic/service/fight/info/info.go @@ -3,6 +3,8 @@ package info import ( "blazing/modules/blazing/model" "fmt" + + "github.com/tnnmigga/enum" ) type ChangePetInfo struct { @@ -119,29 +121,34 @@ type WeakenedS struct { Stack int8 `struc:"skip"` Round int8 } -type StatusDict struct { - Paralysis_0 int8 // 0: 麻痹 - Poisoned_1 int8 // 1: 中毒 - Burned_2 int8 // 2: 烧伤 - DrainHP_3 int8 // 3: 吸取对方的体力 - DrainedHP_4 int8 // 4: 被对方吸取体力 - Frozen_5 int8 // 5: 冻伤 - Fear_6 int8 // 6: 害怕 - Tired_7 int8 // 7: 疲惫 - Sleep_8 int8 // 8: 睡眠 - Petrified_9 int8 // 9: 石化 - Confused_10 int8 // 10: 混乱 - Weakened_11 WeakenedS // 11: 衰弱 - MountainGodGuard_12 int8 // 12: 山神守护 - Flammable_13 int8 // 13: 易燃 - Berserk_14 int8 // 14: 狂暴 - IceBound_15 int8 // 15: 冰封 - Bleeding_16 int8 // 16: 流血 - ImmuneToStatDrop_17 int8 // 17: 免疫能力下降 - ImmuneToAbnormal_18 int8 // 18: 免疫异常状态 - Paralyzed_19 int8 // 19: 瘫痪 - //Blind_20 byte // 20: 失明 -} + +// 定义战斗状态枚举 +var BattleStatus = enum.New[struct { + Paralysis EnumBattleStatus `enum:"0"` // 麻痹 + Poisoned EnumBattleStatus `enum:"1"` // 中毒 + Burned EnumBattleStatus `enum:"2"` // 烧伤 + DrainHP EnumBattleStatus `enum:"3"` // 吸取对方的体力 + DrainedHP EnumBattleStatus `enum:"4"` // 被对方吸取体力 + Frozen EnumBattleStatus `enum:"5"` // 冻伤 + Fear EnumBattleStatus `enum:"6"` // 害怕 + Tired EnumBattleStatus `enum:"7"` // 疲惫 + Sleep EnumBattleStatus `enum:"8"` // 睡眠 + Petrified EnumBattleStatus `enum:"9"` // 石化 + Confused EnumBattleStatus `enum:"10"` // 混乱 + Weakened EnumBattleStatus `enum:"11"` // 衰弱 + MountainGodGuard EnumBattleStatus `enum:"12"` // 山神守护 + Flammable EnumBattleStatus `enum:"13"` // 易燃 + Berserk EnumBattleStatus `enum:"14"` // 狂暴 + IceBound EnumBattleStatus `enum:"15"` // 冰封 + Bleeding EnumBattleStatus `enum:"16"` // 流血 + ImmuneToStatDrop EnumBattleStatus `enum:"17"` // 免疫能力下降 + ImmuneToAbnormal EnumBattleStatus `enum:"18"` // 免疫异常状态 + Paralyzed EnumBattleStatus `enum:"19"` // 瘫痪 + // Blind EnumBattleStatus `enum:"20"` // 失明(预留) +}]() + +// 枚举类型别名(根据实际枚举库要求定义) +type EnumBattleStatus = byte // 精灵的能力提升 type PropDict struct { diff --git a/logic/service/fight/input/Capture.go b/logic/service/fight/input/Capture.go new file mode 100644 index 000000000..c9d58da50 --- /dev/null +++ b/logic/service/fight/input/Capture.go @@ -0,0 +1,129 @@ +package input + +import ( + "blazing/common/data/xmlres" + + "github.com/gogf/gf/v2/util/gconv" +) + +// 异常状态常量定义(对应数组索引) +const ( + StatusParalysis = 0 // 麻痹 + StatusPoison = 1 // 中毒 + StatusSleep = 8 // 睡眠 + StatusFreeze = 3 // 冰冻 + // 预留其他状态到19 +) + +// getItemBonus 获取道具倍率 +func getItemBonus(itemID uint32) float64 { + if bonus, ok := xmlres.ItemsMAP[int(itemID)]; ok { + return gconv.Float64(bonus.Bonus) + } + return 1.0 +} + +// CaptureDetails 捕捉详情 +type CaptureDetails struct { + Success bool + Mode string + BaseRate float64 + ModifiedRate float64 + GuaranteeBonus float64 // 百分比 + StatusBonus float64 // 状态倍率 + Details string +} + +// func main() { +// ctx := NewPlayerCaptureContext() +// fmt.Println("=== 精灵捕捉系统测试(状态数组版) ===") + +// // 测试1:不同异常状态组合 +// fmt.Println("\n--- 异常状态测试 ---") +// // 无异常状态 +// var noStatus [20]byte +// // 中毒状态 +// var poisonStatus [20]byte +// poisonStatus[StatusPoison] = 1 +// // 睡眠+冰冻状态(取最高倍率2.0) +// var multipleStatus [20]byte +// multipleStatus[StatusSleep] = 1 +// multipleStatus[StatusFreeze] = 1 + +// statusTests := []struct { +// name string +// status [20]byte +// }{ +// {"无异常状态", noStatus}, +// {"中毒状态", poisonStatus}, +// {"睡眠+冰冻状态", multipleStatus}, +// } + +// for _, test := range statusTests { +// params := CaptureParams{ +// PetID: 1001, +// MaxHP: 100, +// CurrentHP: 10, +// CatchRate: 50, +// CatchDenom: 100, +// ItemID: 300001, +// Statuses: test.status, +// OwnedCount: -1, +// } +// _, details := ctx.Capture(params) +// fmt.Printf("%s: 状态倍率=%.1f | 基础成功率=%.2f%%\n", +// test.name, details.StatusBonus, details.BaseRate) +// } + +// // 测试2:无敌胶囊 +// fmt.Println("\n--- 无敌胶囊测试 ---") +// invincibleParams := CaptureParams{ +// PetID: 9999, +// MaxHP: 1000, +// CurrentHP: 1000, +// CatchRate: 1, +// CatchDenom: 1000, +// ItemID: 300006, +// Statuses: noStatus, +// OwnedCount: -1, +// } +// success, details := ctx.Capture(invincibleParams) +// fmt.Printf("捕捉结果: %v | 模式: %s | 成功率: %.2f%%\n", +// success, details.Mode, details.ModifiedRate) + +// // 测试3:锁定模式 +// fmt.Println("\n--- 锁定模式测试 ---") +// lockParams := CaptureParams{ +// PetID: 1002, +// MaxHP: 50, +// CurrentHP: 1, +// CatchRate: 100, +// CatchDenom: 100, +// ItemID: 300003, +// Statuses: poisonStatus, +// OwnedCount: 0, +// } +// success, details = ctx.Capture(lockParams) +// fmt.Printf("捕捉结果: %v | 模式: %s | 成功率: %.2f%%\n", +// success, details.Mode, details.ModifiedRate) + +// // 测试4:衰减模式 +// fmt.Println("\n--- 衰减模式测试 ---") +// decayParams := CaptureParams{ +// PetID: 1003, +// MaxHP: 100, +// CurrentHP: 10, +// CatchRate: 50, +// CatchDenom: 100, +// ItemID: 300002, +// Statuses: multipleStatus, +// OwnedCount: 1, +// } + +// for i := 1; i <= 5; i++ { +// decayParams.OwnedCount = i +// success, details := ctx.Capture(decayParams) +// fmt.Printf("拥有%d只 - 结果: %v | 基础成功率: %.2f%% | 实际成功率: %.2f%%\n", +// i, success, details.BaseRate, details.ModifiedRate) +// } +// } diff --git a/logic/service/fight/input/input.go b/logic/service/fight/input/input.go index 6dfe691b7..75958d49f 100644 --- a/logic/service/fight/input/input.go +++ b/logic/service/fight/input/input.go @@ -3,7 +3,10 @@ package input import ( "blazing/logic/service/common" "blazing/logic/service/fight/info" + "fmt" + "math" + "github.com/gogf/gf/v2/util/gconv" "github.com/jinzhu/copier" "github.com/mohae/deepcopy" "github.com/shopspring/decimal" @@ -39,14 +42,14 @@ func (i *Input) GetPetInfo() *info.BattlePetEntity { // 这个每回合都会调用 func (i *Input) InitAttackValue() { - i.AttackValue = info.NewAttackValue(i.Player.ID()) + i.AttackValue = info.NewAttackValue(i.Player.GetInfo().UserID) } func (i *Input) GetPet(id uint32) (ii *info.BattlePetEntity, Reason info.ChangePetInfo) { for _, v := range i.AllPet { if v.Info.CatchTime == uint32(id) { copier.Copy(&Reason, &v.Info) - Reason.UserId = i.Player.ID() + Reason.UserId = i.Player.GetInfo().UserID ii = v } @@ -55,3 +58,153 @@ func (i *Input) GetPet(id uint32) (ii *info.BattlePetEntity, Reason info.ChangeP return } + +// GetStatusBonus 获取最高的状态倍率 +// 遍历状态数组,返回存在的状态中最高的倍率(无状态则返回1.0) +func (i *Input) GetStatusBonus() float64 { + // 异常状态倍率映射表(状态索引 -> 倍率) + var statusBonuses = map[info.EnumBattleStatus]float64{ + info.BattleStatus.Paralysis: 1.5, + info.BattleStatus.Poisoned: 1.5, + info.BattleStatus.Sleep: 2.0, + // /info.BattleStatus.Frozen: 2.0, + } + maxBonus := 1.0 // 默认无状态倍率 + + for statusIdx := 0; statusIdx < 20; statusIdx++ { + t, ok := i.GetStatusEffect(statusIdx) + + // 检查状态是否存在(数组中值为1表示存在该状态) + if ok && t.Stack(0) > 0 { + if bonus, exists := statusBonuses[info.EnumBattleStatus(statusIdx)]; exists && bonus > maxBonus { + maxBonus = bonus + } + } + } + + return maxBonus +} + +// 特殊胶囊必定成功 +// CaptureParams 捕捉参数 +// type CaptureParams struct { +// PetID int // 精灵ID +// MaxHP int // 目标最大HP +// CurrentHP int // 目标当前HP +// CatchRate int // 目标捕获率分子 +// CatchDenom int // 捕获率分母(如100、1000) +// ItemID int // 使用的道具ID +// Statuses [20]byte // 异常状态数组,存在的状态对应位置为1 +// OwnedCount int // 已拥有数量(-1=保底,0=锁定,≥1=衰减) +// } +// 胶囊配置(ItemID -> Bonus) +// var capsuleBonuses = map[int]float64{ +// 300001: 1.0, // 普通精灵胶囊 +// 300002: 1.5, // 中级精灵胶囊 +// 300003: 2.0, // 高级精灵胶囊 +// 300004: 3.0, // 超级精灵胶囊 +// 300005: 4.0, // 特级精灵胶囊 +// 300006: 256.0, // 无敌精灵胶囊 +// 300007: 256.0, // 超能胶囊 +// 300008: 2.0, // 赫星精灵胶囊 +// 300009: 256.0, // 时空精灵胶囊 +// 300010: 256.0, // 无敌精灵胶囊Ω +// } +// + +// -1是保底模式,0是锁定模式,》0是衰减模式 +// Capture 执行捕捉 ,捕捉精灵,使用的道具,模式 +func (c *Input) Capture(pet *info.BattlePetEntity, ItemID uint32, ownerpet int) (bool, CaptureDetails) { + + if getItemBonus(ItemID) >= 255 { + return true, CaptureDetails{ + Success: true, + Mode: "特殊胶囊必定成功", + BaseRate: 100.0, + ModifiedRate: 100.0, + GuaranteeBonus: 0, + StatusBonus: c.GetStatusBonus(), + Details: fmt.Sprintf("道具ID=%d,必定成功", ItemID), + } + } + + // 锁定模式 + if ownerpet == 0 { + return false, CaptureDetails{ + Success: false, + Mode: "锁定模式", + BaseRate: 0, + ModifiedRate: 0, + GuaranteeBonus: 0, + StatusBonus: c.GetStatusBonus(), + Details: "已拥有数量为0,无法捕捉", + } + } + + // 计算基础捕捉率 + baseRate := c.calcBaseRate(pet, ItemID) + denominator := c.Player.GetPlayerCaptureContext().Denominator + numerator := int(baseRate * float64(denominator)) + + // 衰减模式 + if ownerpet > 0 { + decay := math.Pow(1-c.Player.GetPlayerCaptureContext().DecayFactor, float64(ownerpet)) + baseRate *= decay + if baseRate < 0.01 { + baseRate = 0.01 // 最低1%成功率 + } + numerator = int(baseRate * float64(denominator)) + } + + // 走统一保底判定 + success, basePct, bonusPct := c.Player.Roll(numerator, denominator) + + return success, CaptureDetails{ + Success: success, + Mode: map[int]string{-1: "保底模式", 0: "锁定模式", 1: "衰减模式"}[ownerpet], + BaseRate: basePct, + ModifiedRate: basePct + bonusPct, + GuaranteeBonus: bonusPct, + StatusBonus: c.GetStatusBonus(), + Details: fmt.Sprintf("a=%d, 分子=%d, 分母=%d", c.calcBaseA(pet, ItemID), numerator, denominator), + } +} + +// calcBaseA 按公式计算a值 +func (c *Input) calcBaseA(pet *info.BattlePetEntity, ItemID uint32) int { + catchRate := gconv.Int(pet.CatchRate) + catchRate = (catchRate * c.Player.GetPlayerCaptureContext().Denominator) / 1000 // 归一化到1000分母 + if catchRate < 3 { + catchRate = 3 + } + + currentHP := pet.Info.Hp + if currentHP <= 0 { + currentHP = 1 + } + + hpRatio := (3.0*float64(pet.Info.MaxHp) - 2.0*float64(currentHP)) / (3.0 * float64(pet.Info.MaxHp)) + if hpRatio < 0 { + hpRatio = 0 + } + + itemBonus := getItemBonus(ItemID) + statusBonus := c.GetStatusBonus() + + return int(hpRatio * float64(catchRate) * itemBonus * statusBonus) +} + +// calcBaseRate 按公式计算基础成功率 +func (c *Input) calcBaseRate(pet *info.BattlePetEntity, ItemID uint32) float64 { + if getItemBonus(ItemID) >= 255 { + return 1.0 + } + + a := c.calcBaseA(pet, ItemID) + if a >= 255 { + return 1.0 + } + + g := int(1048560.0 / math.Floor(math.Sqrt(math.Floor(math.Sqrt(math.Floor(16711680.0/float64(a))))))) + return math.Pow(float64(g)/65536.0, 4.0) +} diff --git a/logic/service/fight/playeraction.go b/logic/service/fight/playeraction.go index 98a8d86c4..02736b399 100644 --- a/logic/service/fight/playeraction.go +++ b/logic/service/fight/playeraction.go @@ -58,7 +58,7 @@ func (f *FightC) UseSkill(c common.PlayerI, id int32) { return } ret := &SelectSkillAction{ - PlayerID: c.ID(), + PlayerID: c.GetInfo().UserID, } if c.GetInfo().UserID == f.ownerID { ret.PetInfo = f.GetInputByPlayer(c, false).CurrentPet diff --git a/logic/service/player/ai.go b/logic/service/player/ai.go index 8be244c61..14b24eb5a 100644 --- a/logic/service/player/ai.go +++ b/logic/service/player/ai.go @@ -1,48 +1,22 @@ package player import ( - "blazing/common/data/xmlres" - "blazing/logic/service/common" "blazing/logic/service/fight/info" "blazing/modules/blazing/model" "fmt" ) type AI_player struct { - FightC common.FightI //绑定战斗标识 替代本身的是否战斗标记 //IsFighting bool - petinfo []model.PetInfo //精灵信息 - info model.PlayerInfo + baseplayer + petinfo []model.PetInfo //精灵信息 + CanCapture bool } -func NewAI_player(i model.PlayerInfo, m model.PetInfo) *AI_player { - ret := &AI_player{} - ret.petinfo = make([]model.PetInfo, 0) - ret.petinfo = append(ret.petinfo, m) - ret.info = i - ret.info.Nick = xmlres.PetMAP[int(m.ID)].DefName - return ret - -} -func (f *AI_player) SetFightC(ff common.FightI) { - f.FightC = ff -} - -func (f *AI_player) ID() uint32 { - return 0 -} - -func (f *AI_player) MapID() uint32 { - panic("not implemented") // TODO: Implement -} - -// func (f *AI_player) GetInfo() model.PlayerInfo { -// return f.FightC.Opp. - -// } func (f *AI_player) SendPack(b []byte) error { - panic("not implemented") // TODO: Implement + return nil + } func (f *AI_player) SendReadyToFightInfo(gg info.FightStartOutboundInfo) { @@ -69,10 +43,6 @@ func (p *AI_player) End() { return } -func (p *AI_player) GetInfo() model.PlayerInfo { - - return p.info -} func (p *AI_player) GetPetInfo() []model.PetInfo { diff --git a/logic/service/player/base.go b/logic/service/player/base.go new file mode 100644 index 000000000..1d39e6a31 --- /dev/null +++ b/logic/service/player/base.go @@ -0,0 +1,50 @@ +package player + +import ( + "blazing/logic/service/common" + "blazing/logic/service/fight/info" + "blazing/modules/blazing/model" + "math/rand" + "time" +) + +type baseplayer struct { + Info *model.PlayerInfo + FightC common.FightI //绑定战斗标识 替代本身的是否战斗标记 //IsFighting bool + *info.PlayerCaptureContext +} + +// NewPlayerCaptureContext 创建用户捕捉上下文(每次登录调用) +func newbaseplayer() baseplayer { + rng := rand.New(rand.NewSource(time.Now().UnixNano() + int64(rand.Intn(1000000)))) + ret := baseplayer{} + ret.PlayerCaptureContext = &info.PlayerCaptureContext{ + rng, + 1000, + 0.10, // 15%衰减率 + 1, + make(map[int]int), + } + return ret +} +func (p *baseplayer) GetInfo() *model.PlayerInfo { + + return p.Info +} +func (f *baseplayer) SetFightC(ff common.FightI) { + f.FightC = ff +} + +func (f *baseplayer) GetPlayerCaptureContext() *info.PlayerCaptureContext { + return f.PlayerCaptureContext +} + +// 计算整数的二进制1的个数(Integer.bitCount) +func bitsCount(n int) int { + count := 0 + for n > 0 { + count += n & 1 + n >>= 1 + } + return count +} diff --git a/logic/service/player/new.go b/logic/service/player/new.go new file mode 100644 index 000000000..323069747 --- /dev/null +++ b/logic/service/player/new.go @@ -0,0 +1,54 @@ +package player + +import ( + "blazing/modules/blazing/model" + "time" +) + +// NewPlayer 使用 Options 模式创建 Player 实例 +func NewPlayer(opts ...PlayerOption) *Player { + p := &Player{ + loginChan: make(chan struct{}), + HavePVPinfo: make([]*Player, 0), + baseplayer: newbaseplayer(), + } + p.monsters = generateThreeUniqueNumbers() + + p.StopChan = make(chan struct{}) + + // 启动刷怪协程 + go func(stopChan chan struct{}, currentMap int) { + + ticker := time.NewTicker(10 * time.Second) + defer ticker.Stop() + + for { + select { + case <-stopChan: + // 收到停止信号,退出协程 + return + case <-ticker.C: + // 刷新当前地图的怪物 + if p.Canmon && p.FightC == nil { //已经进入地图或者没在战斗中,就可以刷新怪 + p.SpawnMonsters() + } + + } + } + }(p.StopChan, int(p.Info.MapID)) + for _, opt := range opts { + opt(p) + } + return p +} + +func NewAI_player(i *model.PlayerInfo) *AI_player { + ret := &AI_player{ + baseplayer: newbaseplayer(), + } + + ret.Info = i + //ret.Info.Nick = xmlres.PetMAP[int(m.ID)].DefName + return ret + +} diff --git a/logic/service/player/player.go b/logic/service/player/player.go index 1ccceb094..a8eddcc08 100644 --- a/logic/service/player/player.go +++ b/logic/service/player/player.go @@ -92,19 +92,19 @@ type OgrePetInfo struct { type Player struct { MainConn *Conn - + baseplayer IsLogin bool //是否登录 mu sync.Mutex loginChan chan struct{} // 登录完成通知通道 - Info *model.PlayerInfo - StopChan chan struct{} //停止刷怪协程 + + StopChan chan struct{} //停止刷怪协程 context.Context - PVPinfo *info.PVPinfo //当前邀请的玩家ID - Onlinetime uint32 //当前登录时间 - OgreInfo *OgreInfo - FightC common.FightI //绑定战斗标识 替代本身的是否战斗标记 //IsFighting bool + PVPinfo *info.PVPinfo //当前邀请的玩家ID + Onlinetime uint32 //当前登录时间 + OgreInfo *OgreInfo + Service *blservice.UserService HavePVPinfo []*Player monsters [3]int @@ -120,46 +120,7 @@ func WithConn(c *Conn) PlayerOption { } } -// NewPlayer 使用 Options 模式创建 Player 实例 -func NewPlayer(opts ...PlayerOption) *Player { - p := &Player{ - loginChan: make(chan struct{}), - HavePVPinfo: make([]*Player, 0), - } - p.monsters = generateThreeUniqueNumbers() - p.StopChan = make(chan struct{}) - - // 启动刷怪协程 - go func(stopChan chan struct{}, currentMap int) { - - ticker := time.NewTicker(10 * time.Second) - defer ticker.Stop() - - for { - select { - case <-stopChan: - // 收到停止信号,退出协程 - return - case <-ticker.C: - // 刷新当前地图的怪物 - if p.Canmon && p.FightC == nil { //已经进入地图或者没在战斗中,就可以刷新怪 - p.SpawnMonsters() - } - - } - } - }(p.StopChan, int(p.Info.MapID)) - for _, opt := range opts { - opt(p) - } - return p -} - -func (p *Player) GetInfo() model.PlayerInfo { - - return *p.Info -} func (p *Player) GetAction() { @@ -221,16 +182,6 @@ func (p *Player) genMonster(mapid uint32) { p.OgreInfo = &t2 } -// 计算整数的二进制1的个数(Integer.bitCount) -func bitsCount(n int) int { - count := 0 - for n > 0 { - count += n & 1 - n >>= 1 - } - return count -} - // 生成0-9之间三个不重复的随机数 进地图5s func generateThreeUniqueNumbers() [3]int { rand.Seed(time.Now().UnixNano()) @@ -297,15 +248,6 @@ func (p *Player) ItemAdd(t []model.SingleItemInfo) { return } -func (p *Player) ID() uint32 { - - return p.Info.UserID -} -func (p *Player) MapID() uint32 { - - return p.Info.MapID -} - func (p *Player) SendPack(b []byte) error { // fmt.Println("发送数据包", len(b)) err := p.MainConn.SendPack(b) @@ -348,10 +290,6 @@ func (p *Player) SendFightEndInfo(b info.FightOverInfo) { p.SendPack(t1.Pack(&b)) p.FightC = nil } -func (p *Player) GetPetInfo() []model.PetInfo { - - return p.Info.PetList -} func (p *Player) CatchPetInfo(b info.CatchMonsterOutboundInfo) { t1 := NewTomeeHeader(2409, p.Info.UserID) @@ -359,21 +297,17 @@ func (p *Player) CatchPetInfo(b info.CatchMonsterOutboundInfo) { } -func (f *Player) SetFightC(ff common.FightI) { - f.FightC = ff -} - func LeaveMap(c common.PlayerI) { - t := NewTomeeHeader(2002, c.ID()) + t := NewTomeeHeader(2002, c.GetInfo().UserID) - space.GetSpace(c.MapID()).Range(func(playerID uint32, player common.PlayerI) bool { - if playerID != c.ID() { - player.SendPack(t.Pack(&space.LeaveMapOutboundInfo{UserID: c.ID()})) + space.GetSpace(c.GetInfo().MapID).Range(func(playerID uint32, player common.PlayerI) bool { + if playerID != c.GetInfo().UserID { + player.SendPack(t.Pack(&space.LeaveMapOutboundInfo{UserID: c.GetInfo().UserID})) } return true }) - space.GetSpace(c.MapID()).Delete(c.ID()) + space.GetSpace(c.GetInfo().MapID).Delete(c.GetInfo().UserID) } // Save 保存玩家数据 @@ -483,7 +417,7 @@ func (lw *Player) InvitePlayerToBattle(pinfo *info.PVPinfo) { value.HavePVPinfo = append([]*Player{value}, value.HavePVPinfo...) t1 := NewTomeeHeader(2501, value.Info.UserID) t := info.NoteInviteToFightOutboundInfo{ - UserID: lw.ID(), + UserID: lw.Info.UserID, Nick: lw.Info.Nick, Mode: pinfo.Mode, } @@ -506,7 +440,7 @@ func (lw *Player) CancelBattle() { if key == uint32(lw.PVPinfo.PlayerID) { for idx, v := range value.HavePVPinfo { - if v != nil && v.ID() == lw.PVPinfo.PlayerID { + if v != nil && v.GetInfo().UserID == lw.PVPinfo.PlayerID { value.HavePVPinfo = append(value.HavePVPinfo[:idx], value.HavePVPinfo[idx+1:]...) } } @@ -540,13 +474,13 @@ func (lw *Player) AgreeBattle(userid, flag, mode uint32) (bool, common.PlayerI) }(lw) //删除对方的邀请信息 for _, v := range lw.HavePVPinfo { - if v == nil || v.ID() != userid || v.PVPinfo == nil { + if v == nil || v.Info.UserID != userid || v.PVPinfo == nil { continue } t1 := NewTomeeHeader(2502, v.Info.UserID) ret := &info.S2C_NOTE_HANDLE_FIGHT_INVITE{ - UserID: lw.ID(), + UserID: lw.Info.UserID, Nick: lw.Info.Nick, }