feat(fight): 优化战斗系统命中率计算和捕捉逻辑

- 新增 AI_player 结构体的 CanCapture 字段,用于判断是否可捕捉
- 优化 BattlePetEntity 的 Accuracy 方法,增加对负强化等级的处理
- 修改 BattleSkillEntity 的 AttackTime 方法,增加必中判断
- 更新 FightC 中的捕捉逻辑,支持 AI 玩家的捕捉判断
- 重构战斗流程中的技能攻击逻辑,优化命中率计算和效果执行
This commit is contained in:
2025-09-12 00:27:49 +08:00
parent 0ca743a592
commit 4ab4f04a97
7 changed files with 151 additions and 77 deletions

View File

@@ -7,9 +7,9 @@ import (
)
type AI_player struct {
FightC *FightC //绑定战斗标识 替代本身的是否战斗标记 //IsFighting bool
petinfo []model.PetInfo //精灵信息
FightC *FightC //绑定战斗标识 替代本身的是否战斗标记 //IsFighting bool
petinfo []model.PetInfo //精灵信息
CanCapture bool
}
func NewAI_player(m model.PetInfo) *AI_player {

View File

@@ -67,3 +67,8 @@ func (this *EffectNode) GetArgSize() int {
return this.ArgSize
}
func (this *EffectNode) AttackTime() bool {
return true
}

View File

@@ -59,9 +59,50 @@ func (a *BattlePetEntity) SDefense() uint32 {
return uint32(calculateRealValue(int64(a.Info.SpecialDefence), int(a.Prop.SpecialDefence)))
}
func (a *BattlePetEntity) Accuracy(b int64) uint32 {
return uint32(calculateRealValue(b, int(a.Prop.Accuracy)))
// Accuracy 优化版命中率计算(用绝对值和正负判断处理等级)
func (a *BattlePetEntity) Accuracy(b int64) uint32 {
// 基础参数校验
if b <= 0 {
return 0
}
if b >= 100 {
return 100
}
level := a.Prop.Accuracy
if level > 6 || level == 0 { //强化等级
return uint32(calculateRealValue(int64(b), int(a.Prop.Accuracy)))
}
var temp float64
switch level {
case -1:
temp = 0.85
case -2:
temp = 0.7
case -3:
temp = 0.55
case -4:
temp = 0.45
case -5:
temp = 0.35
case -6:
temp = 0.25
}
return uint32(
decimal.NewFromInt(int64(b)). // 将b转为decimal类型
Mul(decimal.NewFromFloat(temp)). // 精确乘以0.85
Round(0). // 四舍五入到整数
IntPart(), // 转为int64
)
}
// 辅助函数:取整数绝对值(处理负等级)
func abs(x int8) int8 {
if x < 0 {
return -x
}
return x
}
type BattlePetEntity struct {

View File

@@ -164,6 +164,9 @@ var DamageC = enum.New[struct {
// 计算是否命中
func (s *BattleSkillEntity) AttackTime() uint32 {
if s.MustHit != 0 {
return 1
}
if int64(s.Pet.Accuracy(int64(s.Accuracy))) > s.Rand.Int63n(100) {
return 1

View File

@@ -91,48 +91,48 @@ type AttackValue struct {
}
type WeakenedS struct {
Stack byte `struc:"skip"`
Round byte
Stack int8 `struc:"skip"`
Round int8
}
type StatusDict struct {
Paralysis_0 byte // 0: 麻痹
Poisoned_1 byte // 1: 中毒
Burned_2 byte // 2: 烧伤
DrainHP_3 byte // 3: 吸取对方的体力
DrainedHP_4 byte // 4: 被对方吸取体力
Frozen_5 byte // 5: 冻伤
Fear_6 byte // 6: 害怕
Tired_7 byte // 7: 疲惫
Sleep_8 byte // 8: 睡眠
Petrified_9 byte // 9: 石化
Confused_10 byte // 10: 混乱
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 byte // 12: 山神守护
Flammable_13 byte // 13: 易燃
Berserk_14 byte // 14: 狂暴
IceBound_15 byte // 15: 冰封
Bleeding_16 byte // 16: 流血
ImmuneToStatDrop_17 byte // 17: 免疫能力下降
ImmuneToAbnormal_18 byte // 18: 免疫异常状态
Paralyzed_19 byte // 19: 瘫痪
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: 失明
}
// 精灵的能力提升
type PropDict struct {
// 攻击(@UInt long → uint32
Attack byte
Attack int8
// 防御(@UInt long → uint32
Defence byte
Defence int8
// 特攻(@UInt long → uint32
SpecialAttack byte
SpecialAttack int8
// 特防(@UInt long → uint32
SpecialDefence byte
SpecialDefence int8
// 速度(@UInt long → uint32
Speed byte
Speed int8
// 命中(@UInt long → uint32
Accuracy byte
Accuracy int8
}
// BattleLevels 战斗属性等级结构体对应原6字节数组

View File

@@ -19,9 +19,9 @@ type Effect interface {
OnDamage() bool // 造成伤害时触发
SetArgs(param []int) //设置参数
Shield() bool // 护盾值变化时触发
PostDamage() bool // 伤害结算后触发(血量扣除后)
Shield() bool // 护盾值变化时触发
PostDamage() bool // 伤害结算后触发(血量扣除后)
AttackTime() bool //闪避率计算,,实际上是修改命中的判断
OnCritPostDamage() bool // 暴击伤害结算后触发
OnHit() bool // 技能命中时触发

View File

@@ -175,6 +175,9 @@ func (f *FightC) ReadyFight(c PlayerI) {
//判断捕捉率大于0
if gconv.Int(xmlres.PetMAP[int(f.Info.OpponentPetList[0].ID)].CatchRate) > 0 {
rett.Info2.Catchable = 1
t, _ := f.Opp.Player.(*AI_player)
t.CanCapture = true
}
rrsult()
@@ -347,12 +350,19 @@ func (f *FightC) battleLoop() {
f.Broadcast(func(ff *Input) {
//todo 将血量和技能pp传回enterturn
tt, ok := ff.Player.(*Player)
if ok {
tt.Service.PetAdd(*f.Opp.CurrentPet.Info)
tt.CatchPetInfo(info.CatchMonsterOutboundInfo{
CatchTime: uint32(f.Opp.CurrentPet.Info.CatchTime),
PetId: uint32(f.Opp.CurrentPet.ID),
})
mo, ism := f.Opp.Player.(*AI_player)
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),
})
} else { //说明不是可以捕捉的
tt.CatchPetInfo(info.CatchMonsterOutboundInfo{})
}
}
@@ -483,49 +493,64 @@ func (f *FightC) initAttackers(fattack, sattack info.BattleActionI) {
// 处理技能攻击逻辑
func (f *FightC) processSkillAttack(attacker, defender *BPET, skill *info.SelectSkillAction) {
f.parseskill(skill)
spower := skill.Skill.CalculatePower(defender.BattlePetEntity)
attacker.Damage = spower
f.parseskill(skill) //解析effect
// 记录技能信息
attacker.AttackValue.SkillID = uint32(skill.Skill.ID)
attacker.AttackValue.AttackTime = skill.Skill.AttackTime()
attacker.AttackValue.SkillID = uint32(skill.Skill.ID) //获取技能ID
attacker.AttackValue.AttackTime = skill.Skill.AttackTime() //计算命中
CritRate := utils.Max(skill.Skill.CritRate, 1)
CritRateR := f.rand.Int31n(16)
//CritAtkFirst: 先出手时必定致命一击; 默认: 0
if skill.Skill.CritAtkFirst != 0 && attacker == f.First {
CritRate = 16
}
//CritAtkSecond: 后出手时必定致命一击; 默认: 0
if skill.Skill.CritAtkSecond != 0 && attacker != f.First {
CritRate = 16
}
// CritSelfHalfHp: 自身体力低于一半时必定致命一击; 默认: 0
if skill.Skill.CritSelfHalfHp != 0 && (attacker.CurrentPet.HP < int(attacker.CurrentPet.Info.MaxHp)/2) {
CritRate = 16
}
// CritFoeHalfHp: 对方体力低于一半时必定致命一击; 默认: 0
if skill.Skill.CritSelfHalfHp != 0 && (defender.CurrentPet.HP < int(defender.CurrentPet.Info.MaxHp)/2) {
CritRate = 16
}
f.EffectS.Exec(func(t info.Effect) bool { //计算闪避 闪避就是命中率重新计算的结果
//闪避本质上是算对方的命中重写函数?
//todo 暴击伤害
if CritRateR <= int32(CritRate) {
attacker.AttackValue.IsCritical = 1
}
if attacker.UserID == f.Our.Player.ID() {
if attacker.AttackValue.IsCritical == 1 {
attacker.Damage.Mul(decimal.NewFromInt(2)) //暴击翻倍
}
}
if !t.GetOwner() { //先获取对方的
return t.AttackTime()
}
return true
})
if uint32(attacker.Damage.IntPart()) > defender.CurrentPet.Info.Hp {
defender.CurrentPet.Info.Hp = 0
} else {
defender.CurrentPet.Info.Hp = defender.CurrentPet.Info.Hp - uint32(attacker.Damage.IntPart())
}
if attacker.AttackValue.AttackTime == 1 { //如果命中
// 扣减防御方血量
spower := skill.Skill.CalculatePower(defender.BattlePetEntity)
attacker.Damage = spower
CritRate := utils.Max(skill.Skill.CritRate, 1)
CritRateR := f.rand.Int31n(16)
//CritAtkFirst: 先出手时必定致命一击; 默认: 0
if skill.Skill.CritAtkFirst != 0 && attacker == f.First {
CritRate = 16
}
//CritAtkSecond: 后出手时必定致命一击; 默认: 0
if skill.Skill.CritAtkSecond != 0 && attacker != f.First {
CritRate = 16
}
// CritSelfHalfHp: 自身体力低于一半时必定致命一击; 默认: 0
if skill.Skill.CritSelfHalfHp != 0 && (attacker.CurrentPet.HP < int(attacker.CurrentPet.Info.MaxHp)/2) {
CritRate = 16
}
// CritFoeHalfHp: 对方体力低于一半时必定致命一击; 默认: 0
if skill.Skill.CritSelfHalfHp != 0 && (defender.CurrentPet.HP < int(defender.CurrentPet.Info.MaxHp)/2) {
CritRate = 16
}
//todo 暴击伤害
if CritRateR <= int32(CritRate) {
attacker.AttackValue.IsCritical = 1
}
if attacker.AttackValue.IsCritical == 1 {
attacker.Damage.Mul(decimal.NewFromInt(2)) //暴击翻倍
}
if uint32(attacker.Damage.IntPart()) > defender.CurrentPet.Info.Hp {
defender.CurrentPet.Info.Hp = 0
} else {
defender.CurrentPet.Info.Hp = defender.CurrentPet.Info.Hp - uint32(attacker.Damage.IntPart())
}
// 扣减防御方血量
} //todo 处理未命中效果
}