feat(capture): 重构捕捉系统,实现状态倍率计算和保底机制
This commit is contained in:
@@ -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)
|
||||
|
||||
@@ -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>标签
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
|
||||
50
logic/service/fight/info/ctx.go
Normal file
50
logic/service/fight/info/ctx.go
Normal 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
|
||||
}
|
||||
@@ -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 {
|
||||
|
||||
129
logic/service/fight/input/Capture.go
Normal file
129
logic/service/fight/input/Capture.go
Normal 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)
|
||||
// }
|
||||
// }
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 {
|
||||
|
||||
|
||||
50
logic/service/player/base.go
Normal file
50
logic/service/player/base.go
Normal 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
|
||||
}
|
||||
54
logic/service/player/new.go
Normal file
54
logic/service/player/new.go
Normal 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
|
||||
|
||||
}
|
||||
@@ -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,
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user