feat(capture): 重构捕捉系统,实现状态倍率计算和保底机制

This commit is contained in:
1
2025-09-21 14:56:37 +00:00
parent bb9b0510ce
commit 691cfc878b
17 changed files with 563 additions and 539 deletions

View File

@@ -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)

View File

@@ -10,29 +10,31 @@ type Items struct {
Items []Item `xml:"Item"` // 包含所有物品配置
}
// 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 精灵属性子节点,对应<pet>标签

View File

@@ -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

View File

@@ -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

View File

@@ -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
})

View File

@@ -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)

View File

@@ -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)
}

View File

@@ -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)
}

View File

@@ -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
}

View File

@@ -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 {

View File

@@ -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)
// }
// }

View File

@@ -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)
}

View File

@@ -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

View File

@@ -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 {

View File

@@ -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
}

View File

@@ -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
}

View File

@@ -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,
}