Files
bl/logic/service/fight/fightc.go

657 lines
17 KiB
Go
Raw Normal View History

package fight
import (
"blazing/common/data/xmlres"
"blazing/logic/service/common"
2025-09-28 08:13:42 +00:00
"blazing/logic/service/fight/action"
"blazing/logic/service/fight/info"
"blazing/logic/service/fight/input"
"blazing/logic/service/player"
"fmt"
"math/rand"
"sort"
"time"
"github.com/barkimedes/go-deepcopy"
"github.com/jinzhu/copier"
feat(player): 新增玩家累计经验查询接口 新增 PlayerExp 控制器方法,用于返回玩家的累计经验值。同时调整了经验池字段类型为 uint32 并修复相关使用逻辑。 feat(pet): 实现宠物经验增加与升级逻辑 在 Player 结构体中新增 AddPetExp 方法,支持宠物经验增长、自动升级及进化判断。升级后会重新计算面板属性并推送更新包。 feat(fight): 重构战斗伤害计算与效果系统 引入 DamageZone 和 EnumDamageType 类型,统一红伤处理流程;移除旧有的 Pet/Skill/Prop 属性获取临时修改机制,改为直接访问真实属性。更新多个技能效果实现以适配新结构。 refactor(effect): 优化技能效果初始化和生命周期方法 统一技能效果初始化方式,明确各阶段回调函数职责,如 PreActionStart、PreAttacked 等。删除已废弃的属性修改钩子函数,并更新状态类效果实现。 refactor(input): 移除 deepcopy 依赖并替换为 go-deepcopy 将原先使用的 mohae/deepcopy 替换为 barkimedes/go-deepcopy,用于战斗节点中的 effect 拷贝逻辑,提升性能和安全性。 refactor(model): 调整玩家信息字段类型 将 PlayerInfo 中的 GoldBean 字段由 int32 改为 uint32,ExpPool 字段由 int64 改为 uint32,确保数据类型一致性与合理性。 feat(nono): 增加 Nono 跟随/收回协议结构定义 新增 NonoFollowOrHomeInInfo 和 NonoFollowOutInfo 结构体,用于处理 Nono 宠物的跟随与收回操作指令。 chore(deps): 添加 go-deepcopy 依赖 在 go.mod 中引入 github.com/barkimedes/go-deepcopy 依赖库,用于替代原有的 deepcopy 工具。
2025-09-26 13:33:55 +08:00
"github.com/shopspring/decimal"
)
type FightC struct {
Info info.NoteReadyToFightInfo
2025-09-07 05:58:47 +08:00
ownerID uint32 // 战斗发起者ID
Our *input.Input //始终等于房主ID
Opp *input.Input //对手ID
2025-09-28 08:13:42 +00:00
Switch []*action.ActiveSwitchAction
rand *rand.Rand
StartTime time.Time
2025-09-28 08:13:42 +00:00
actionChan chan action.BattleActionI // 所有操作统一从这里进入
Round int //回合数
First *input.Input
Second *input.Input
closefight bool
}
func (f *FightC) Ownerid() uint32 {
return f.ownerID
}
func (f *FightC) GetInputByPlayer(c common.PlayerI, isOpposite bool) *input.Input {
// 判断当前玩家是否为我方玩家
``` refactor(effectarg): 移动EffectArgs初始化逻辑 将EffectArgs的初始化从effectarg.go中的init函数移动到file.go的initfile函数中, 确保在使用前正确加载配置并初始化映射。 refactor(login): 更新Login方法中的Person调用 修改Login方法中对Person函数的调用,传递UserID参数以获取正确的用户信息。 refactor(user): 统一使用Person方法替代PersonOther 在UserSimInfo和UserMoreInfo方法中,将原先调用的PersonOther方法统一替换为 Person方法,保持代码一致性。 refactor(effect_damage): 简化属性获取和伤害计算逻辑 移除deepcopy相关逻辑,简化Effect0的OnSkill方法中的属性获取和伤害计算流程, 直接通过输入参数进行计算。 refactor(fightc): 优化玩家输入处理和战斗逻辑 更新GetInputByPlayer方法中的玩家判断逻辑,使用UserID比较代替对象比较; 在initplayer中添加InitAttackValue调用; 修复battleLoop中打印语句的格式问题; 调整技能攻击处理流程,增加SkillUseEnd回调调用。 refactor(attr): 改进属性获取方法和伤害计算逻辑 将GetProp方法重命名为Prop,并支持传入对方输入参数; 更新CalculatePower方法签名,使用Input类型代替BattlePetEntity; 在属性获取和伤害计算中正确处理双方属性影响。 refactor(playeraction): 简化技能使用逻辑 简化UseSkill方法中获取当前宠物信息的逻辑,去除冗余的条件判断; 在找到对应技能后添加break语句,提高执行效率。 refactor(reg): 更新Person方法实现 合并Person和PersonOther方法为统一的Person方法; 在数据库查询失败时添加错误处理,避免潜在的空指针异常。 ```
2025-09-24 12:40:13 +08:00
isOurPlayer := c.GetInfo().UserID == f.ownerID
// 当isOurPlayer与isOpposite值不同时返回我方相同时返回对方
if isOurPlayer != isOpposite {
return f.Our
}
return f.Opp
}
2025-09-28 08:13:42 +00:00
func (f *FightC) GetInputByAction(c action.BattleActionI, isOpposite bool) *input.Input {
// 判断动作所属玩家是否为我方
isOurAction := c.GetPlayerID() == f.Our.Player.GetInfo().UserID
// 根据isOpposite决定是否返回相反方向的输入
if isOurAction == !isOpposite {
return f.Our
}
return f.Opp
}
// 玩家使用技能
func (f *FightC) GetCurrPET(c common.PlayerI) *info.BattlePetEntity {
if f.Our.Player.GetInfo().UserID == c.GetInfo().UserID {
return f.Our.CurrentPet
} else {
return f.Opp.CurrentPet
}
}
// 获取随机数
func (f *FightC) GetRand() *rand.Rand {
return f.rand
}
// 加载进度
func (f *FightC) LoadPercent(c common.PlayerI, percent int32) {
f.GetInputByPlayer(c, true).Player.SendLoadPercent(info.LoadPercentOutboundInfo{
Id: c.GetInfo().UserID,
Percent: uint32(percent),
})
}
func (f *FightC) initplayer(c common.PlayerI, opp bool) {
temp := input.NewInput(f, c)
temp.AllPet = make([]*info.BattlePetEntity, 0)
``` refactor(effectarg): 移动EffectArgs初始化逻辑 将EffectArgs的初始化从effectarg.go中的init函数移动到file.go的initfile函数中, 确保在使用前正确加载配置并初始化映射。 refactor(login): 更新Login方法中的Person调用 修改Login方法中对Person函数的调用,传递UserID参数以获取正确的用户信息。 refactor(user): 统一使用Person方法替代PersonOther 在UserSimInfo和UserMoreInfo方法中,将原先调用的PersonOther方法统一替换为 Person方法,保持代码一致性。 refactor(effect_damage): 简化属性获取和伤害计算逻辑 移除deepcopy相关逻辑,简化Effect0的OnSkill方法中的属性获取和伤害计算流程, 直接通过输入参数进行计算。 refactor(fightc): 优化玩家输入处理和战斗逻辑 更新GetInputByPlayer方法中的玩家判断逻辑,使用UserID比较代替对象比较; 在initplayer中添加InitAttackValue调用; 修复battleLoop中打印语句的格式问题; 调整技能攻击处理流程,增加SkillUseEnd回调调用。 refactor(attr): 改进属性获取方法和伤害计算逻辑 将GetProp方法重命名为Prop,并支持传入对方输入参数; 更新CalculatePower方法签名,使用Input类型代替BattlePetEntity; 在属性获取和伤害计算中正确处理双方属性影响。 refactor(playeraction): 简化技能使用逻辑 简化UseSkill方法中获取当前宠物信息的逻辑,去除冗余的条件判断; 在找到对应技能后添加break语句,提高执行效率。 refactor(reg): 更新Person方法实现 合并Person和PersonOther方法为统一的Person方法; 在数据库查询失败时添加错误处理,避免潜在的空指针异常。 ```
2025-09-24 12:40:13 +08:00
temp.InitAttackValue()
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.GetInfo().PetList[i], f.rand))
}
}
sort.Slice(temp.AllPet, func(i, j int) bool {
x, y := temp.AllPet[i], temp.AllPet[j]
// 若x血量>0且y血量=0则x排在前
if x.Info.Hp > 0 && y.Info.Hp <= 0 {
return true
}
// 若x血量=0且y血量>0则x排在后
if x.Info.Hp <= 0 && y.Info.Hp > 0 {
return false
}
// 同类型(都>0或都=0保持原有顺序
return i < j
})
if opp {
f.Opp = temp //这里是对方的
copier.Copy(&f.Info.OpponentInfo, f.Opp.Player.GetInfo())
f.Info.OpponentPetList = make([]info.ReadyFightPetInfo, len(temp.AllPet))
for i := 0; i < len(temp.AllPet); i++ {
err := copier.CopyWithOption(&f.Info.OpponentPetList[i], &temp.AllPet[i].Info, copier.Option{IgnoreEmpty: true, DeepCopy: true})
if err != nil {
panic(err)
}
}
} else {
f.Our = temp
copier.Copy(&f.Info.OurInfo, f.Our.Player.GetInfo())
f.Info.OurPetList = make([]info.ReadyFightPetInfo, len(temp.AllPet))
for i := 0; i < len(temp.AllPet); i++ {
err := copier.CopyWithOption(&f.Info.OurPetList[i], &temp.AllPet[i].Info, copier.Option{IgnoreEmpty: true, DeepCopy: true})
if err != nil {
panic(err)
}
}
2025-09-07 05:58:47 +08:00
}
for _, v := range temp.AllPet {
if v.Info.Hp == 0 {
v.NotAlive = true
}
}
temp.CurrentPet = temp.AllPet[0]
}
// 创建新战斗,邀请方和被邀请方,或者玩家和野怪方
func NewFight(mode, status info.EnumBattleMode, p1 common.PlayerI, p2 common.PlayerI) *FightC {
f := &FightC{}
f.ownerID = p1.GetInfo().UserID
f.Info.Status = status //房主
f.Info.Mode = mode
switch mode {
case info.BattleMode.SINGLE_MODE:
f.Info.MAXPET = 1
default:
}
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{
Status: status,
}
f.initplayer(p1, false)
f.initplayer(p2, true)
defer func() {
rr := Fightpool.Submit(f.battleLoop)
if rr != nil {
panic(rr)
}
f.Broadcast(func(ff *input.Input) {
ff.Player.SendNoteReadyToFightInfo(f.Info)
})
}()
//go f.battleLoop() // 起战斗循环
return f
}
// 被击败的ID
func (b *FightC) IsWin(c *input.Input, cache uint32) bool {
var tt []*info.BattlePetEntity
bbb := b.Our.AllPet
if c.Player.GetInfo().UserID == b.ownerID { //如果是房主
bbb = b.Opp.AllPet
}
for _, v := range bbb {
if v.Info.CatchTime == cache {
v.NotAlive = true
}
tt = append(tt, v)
}
for _, v := range tt {
if !v.NotAlive { //如果存活
return false
}
}
return true
}
// 广播,并是否结束回合
func (f *FightC) Broadcast(t func(ff *input.Input)) {
2025-09-07 05:58:47 +08:00
t(f.Our)
t(f.Opp)
2025-09-07 05:58:47 +08:00
}
// 战斗回合循环
func (f *FightC) battleLoop() {
f.StartTime = time.Now()
2025-09-28 08:13:42 +00:00
f.actionChan = make(chan action.BattleActionI, 2) // 初始化全局操作通道
fmt.Println("战斗开始精灵", f.Our.Player.GetInfo().PetList[0].CatchTime)
//战斗开始前操作
for {
2025-09-07 05:58:47 +08:00
if f.closefight { //回合数超过250,战斗平局结束f.Round > 250 ||
close(f.actionChan)
break
}
2025-09-28 08:13:42 +00:00
f.Round++ //回合数自增
actions := make(map[uint32]action.BattleActionI) // 每个玩家一条记录
timeout := time.After(60 * time.Second)
for len(actions) < 2 {
select {
2025-09-28 08:13:42 +00:00
case paction, ok := <-f.actionChan:
// 只接受有效玩家 ID\
2025-09-28 08:13:42 +00:00
if paction == nil {
continue
}
if !ok {
fmt.Println("战斗结束")
break
}
2025-09-28 08:13:42 +00:00
if paction.GetPlayerID() != f.Our.Player.GetInfo().UserID && paction.GetPlayerID() != f.Opp.Player.GetInfo().UserID {
continue
}
2025-09-28 08:13:42 +00:00
if a, isExpelled := paction.(*action.ActiveSwitchAction); isExpelled {
f.Broadcast(func(ff *input.Input) {
if ff.Player.GetInfo().UserID == a.PlayerID { //先给自身广播
ff.Player.SendChangePet(a.Reason)
}
})
2025-09-28 08:13:42 +00:00
if f.GetInputByAction(paction, false).CanChange {
//如果是被动切换,不计入回合结算
2025-09-28 08:13:42 +00:00
f.GetInputByAction(paction, false).CanChange = false
continue
}
}
2025-09-28 08:13:42 +00:00
if paction.GetPlayerID() != 0 && f.Info.Status == 3 {
2025-09-28 08:13:42 +00:00
//AI的action实质上就是放技能如果阻止掉比如中毒那就也不能逃跑
f.GetInputByAction(paction, true).GetAction(f.Our)
}
// 如果该玩家已经提交过,就忽略重复动作
2025-09-28 08:13:42 +00:00
if _, exists := actions[uint32(paction.GetPlayerID())]; exists {
fmt.Printf("玩家%d 已经提交过动作,忽略重复\n", paction.GetPlayerID())
continue
}
2025-09-28 08:13:42 +00:00
actions[uint32(paction.GetPlayerID())] = paction
fmt.Println("玩家 执行动作", paction.GetPlayerID(), paction.Priority())
case <-timeout:
if _, exists := actions[f.Our.Player.GetInfo().UserID]; !exists {
f.Over(f.Opp.Player, info.BattleOverReason.PlayerOVerTime)
}
if _, exists := actions[f.Opp.Player.GetInfo().UserID]; !exists {
f.Over(f.Opp.Player, info.BattleOverReason.PlayerOVerTime)
}
}
}
// 双方动作齐了,取出来结算
//todo 如果一方没有选择,实际上就是后端判断PP是否还有,前端是直接发的
p1Action := actions[f.Our.Player.GetInfo().UserID]
p2Action := actions[f.Opp.Player.GetInfo().UserID]
fmt.Println("开始结算回合")
``` refactor(effectarg): 移动EffectArgs初始化逻辑 将EffectArgs的初始化从effectarg.go中的init函数移动到file.go的initfile函数中, 确保在使用前正确加载配置并初始化映射。 refactor(login): 更新Login方法中的Person调用 修改Login方法中对Person函数的调用,传递UserID参数以获取正确的用户信息。 refactor(user): 统一使用Person方法替代PersonOther 在UserSimInfo和UserMoreInfo方法中,将原先调用的PersonOther方法统一替换为 Person方法,保持代码一致性。 refactor(effect_damage): 简化属性获取和伤害计算逻辑 移除deepcopy相关逻辑,简化Effect0的OnSkill方法中的属性获取和伤害计算流程, 直接通过输入参数进行计算。 refactor(fightc): 优化玩家输入处理和战斗逻辑 更新GetInputByPlayer方法中的玩家判断逻辑,使用UserID比较代替对象比较; 在initplayer中添加InitAttackValue调用; 修复battleLoop中打印语句的格式问题; 调整技能攻击处理流程,增加SkillUseEnd回调调用。 refactor(attr): 改进属性获取方法和伤害计算逻辑 将GetProp方法重命名为Prop,并支持传入对方输入参数; 更新CalculatePower方法签名,使用Input类型代替BattlePetEntity; 在属性获取和伤害计算中正确处理双方属性影响。 refactor(playeraction): 简化技能使用逻辑 简化UseSkill方法中获取当前宠物信息的逻辑,去除冗余的条件判断; 在找到对应技能后添加break语句,提高执行效率。 refactor(reg): 更新Person方法实现 合并Person和PersonOther方法为统一的Person方法; 在数据库查询失败时添加错误处理,避免潜在的空指针异常。 ```
2025-09-24 12:40:13 +08:00
// 统一赋值,减少重复代码
2025-09-28 08:13:42 +00:00
var BattleActionI [2]action.BattleActionI
BattleActionI[0], BattleActionI[1] = f.Compare(p1Action, p2Action)
switch faction := BattleActionI[0].(type) {
2025-09-28 08:13:42 +00:00
case *action.PlayerOfflineAction: //单方掉线
f.Broadcast(func(ff *input.Input) {
ff.Player.SendFightEndInfo(faction.Reason) //广播逃跑原因
})
f.closefight = true
2025-09-28 08:13:42 +00:00
case *action.EscapeAction: //优先逃跑
f.Broadcast(func(ff *input.Input) {
ff.Player.SendFightEndInfo(faction.Reason) //广播逃跑原因
})
f.closefight = true
2025-09-28 08:13:42 +00:00
case *action.ActiveSwitchAction: //切换上场的,切换方放弃出手
f.enterturn(BattleActionI[1],
&action.SystemGiveUpAction{BaseAction: action.NewBaseAction(BattleActionI[0].GetPlayerID())}) //切换,相当于后手直接出手
2025-09-28 08:13:42 +00:00
case *action.UseItemAction: //使用道具
//fmt.Println(faction.ItemID)
switch {
case faction.ItemID >= 30001 && faction.ItemID <= 300010: //胶囊
//todo 将血量和技能pp传回enterturn
tt, ok := f.Our.Player.(*player.Player)
mo, ism := f.Opp.Player.(*player.AI_player)
if ok && ism && mo.CanCapture { //如果获取玩家
ok, res := f.Our.Capture(f.Opp.CurrentPet, faction.ItemID, -1)
if ok { //todo 待补充
fmt.Println(res)
tt.Service.PetAdd(*f.Opp.CurrentPet.Info)
tt.CatchPetInfo(info.CatchMonsterOutboundInfo{
CatchTime: uint32(f.Opp.CurrentPet.Info.CatchTime),
PetId: uint32(f.Opp.CurrentPet.ID),
})
tt.SendFightEndInfo(info.FightOverInfo{
WinnerId: f.ownerID,
})
f.closefight = true
} else {
tt.CatchPetInfo(info.CatchMonsterOutboundInfo{})
}
} else { //说明不是可以捕捉的
tt.CatchPetInfo(info.CatchMonsterOutboundInfo{})
}
// 当 ItemID 在 30001-300010 之间时执行的逻辑
fmt.Println("ItemID 在范围内")
case faction.ItemID == 300001:
// 原来的单个值判断(如果还需要保留)
fmt.Println("ItemID 是 300001")
default:
// 其他情况
fmt.Println("ItemID 不在指定范围内")
}
2025-09-28 08:13:42 +00:00
f.enterturn(BattleActionI[1], &action.SystemGiveUpAction{BaseAction: action.NewBaseAction(BattleActionI[0].GetPlayerID())}) //切换,相当于后手直接出手
default: //选择技能或者放弃出手
//回合前操作,比如挂载buff
f.enterturn(BattleActionI[0], BattleActionI[1])
//回合后操作
}
}
}
// 解析并 施加effect
2025-09-28 08:13:42 +00:00
func (f *FightC) parseskill(attacker, defender *input.Input, id *action.SelectSkillAction) {
temparg := id.Skill.SideEffectArgS
for _, v := range id.Skill.SideEffectS {
t := input.Geteffect(input.EffectType.Skill, v)
args := xmlres.EffectArgs[v]
2025-09-29 02:40:35 +08:00
if t.ID != 0 {
if t.Effect.GetOwner() { //如果取反,说明是给对方添加的回合效果
//实际上,owner永远为反,说明是对方给我添加的
t.Effect.SetArgs(attacker, temparg[:args]...) //设置入参,施加方永远是我方
//给双方添加
defender.AddEffect(t)
} else {
t.Effect.SetArgs(attacker, temparg[:args]...) //设置入参
attacker.AddEffect(t)
}
}
2025-09-29 02:40:35 +08:00
temparg = temparg[args:]
}
}
2025-09-28 08:13:42 +00:00
func (f *FightC) initAttackers(fattack action.BattleActionI) {
// 伤害值
// 根据攻击方归属设置当前战斗的主/次攻击方属性
if fattack.GetPlayerID() == f.ownerID {
f.First, f.Second = f.Our, f.Opp // 攻击方为我方时,主攻击方是我方
} else {
f.First, f.Second = f.Opp, f.Our // 攻击方为对方时,主攻击方是对方
}
fmt.Println("先手", f.First.CurrentPet.Info.CatchTime, "后手", f.Second.CurrentPet.Info.CatchTime)
// TODO: 在这里调用技能结算逻辑
}
// 处理技能攻击逻辑
2025-09-28 08:13:42 +00:00
func (f *FightC) processSkillAttack(attacker, defender *input.Input, a *action.SelectSkillAction) {
2025-09-29 02:40:35 +08:00
f.parseskill(attacker, defender, a) //是否miss都应该施加解析effect
2025-09-28 08:13:42 +00:00
attacker.Exec(func(t input.Effect) bool { //计算命中
t.Skill_Pre(input.Ctx{
Input: attacker,
SkillEntity: a.Skill,
}) //相当于先调整基础命中,不光调整命中,这里还能调整技能属性,暴击率
return true
})
2025-09-28 08:13:42 +00:00
a.Skill.AttackTimeC(attacker.GetProp(5, true)) //计算命中
defender.Exec(func(t input.Effect) bool { //计算闪避 ,然后修改对方命中),同时相当于计算属性无效这种
t.Skill_PreUse(input.Ctx{
Input: attacker,
SkillEntity: a.Skill,
})
return true
})
2025-09-28 08:13:42 +00:00
// 记录技能信息
attacker.AttackValue.SkillID = uint32(a.Skill.ID) //获取技能ID
attacker.UseSkill(defender, a.Skill) //攻击方计算技能使用
attacker.AttackValue.AttackTime = a.Skill.AttackTime
if attacker.AttackValue.AttackTime > 0 { //如果命中
2025-09-29 02:40:35 +08:00
attacker.DamageZone.Damage = attacker.CalculatePower(defender, a.Skill)
attacker.AttackValue.IsCritical = a.Skill.Crit
if attacker.AttackValue.IsCritical == 1 {
2025-09-29 02:40:35 +08:00
//暴击翻倍
attacker.DamageZone.Damage = attacker.DamageZone.Damage.Mul(decimal.NewFromInt(2))
}
attacker.Exec(func(t input.Effect) bool {
t.Hit(true) //我方效果命中
``` refactor(effectarg): 移动EffectArgs初始化逻辑 将EffectArgs的初始化从effectarg.go中的init函数移动到file.go的initfile函数中, 确保在使用前正确加载配置并初始化映射。 refactor(login): 更新Login方法中的Person调用 修改Login方法中对Person函数的调用,传递UserID参数以获取正确的用户信息。 refactor(user): 统一使用Person方法替代PersonOther 在UserSimInfo和UserMoreInfo方法中,将原先调用的PersonOther方法统一替换为 Person方法,保持代码一致性。 refactor(effect_damage): 简化属性获取和伤害计算逻辑 移除deepcopy相关逻辑,简化Effect0的OnSkill方法中的属性获取和伤害计算流程, 直接通过输入参数进行计算。 refactor(fightc): 优化玩家输入处理和战斗逻辑 更新GetInputByPlayer方法中的玩家判断逻辑,使用UserID比较代替对象比较; 在initplayer中添加InitAttackValue调用; 修复battleLoop中打印语句的格式问题; 调整技能攻击处理流程,增加SkillUseEnd回调调用。 refactor(attr): 改进属性获取方法和伤害计算逻辑 将GetProp方法重命名为Prop,并支持传入对方输入参数; 更新CalculatePower方法签名,使用Input类型代替BattlePetEntity; 在属性获取和伤害计算中正确处理双方属性影响。 refactor(playeraction): 简化技能使用逻辑 简化UseSkill方法中获取当前宠物信息的逻辑,去除冗余的条件判断; 在找到对应技能后添加break语句,提高执行效率。 refactor(reg): 更新Person方法实现 合并Person和PersonOther方法为统一的Person方法; 在数据库查询失败时添加错误处理,避免潜在的空指针异常。 ```
2025-09-24 12:40:13 +08:00
return true
})
defender.Exec(func(t input.Effect) bool {
if t.GetInput() == attacker { //如果取反,说明是给对方添加的回合效果
t.Hit(true)
}
return true
})
// 扣减防御方血量
}
attacker.Exec(func(t input.Effect) bool {
2025-09-29 02:40:35 +08:00
t.OnSkill(input.Ctx{
Input: defender,
SkillEntity: a.Skill,
}) //调用伤害计算
return true
})
2025-09-29 02:40:35 +08:00
defender.Damage(input.Ctx{
Input: attacker,
SkillEntity: a.Skill,
DamageZone: &info.DamageZone{
Type: info.DamageType.Red,
Damage: attacker.DamageZone.Damage,
},
feat(player): 新增玩家累计经验查询接口 新增 PlayerExp 控制器方法,用于返回玩家的累计经验值。同时调整了经验池字段类型为 uint32 并修复相关使用逻辑。 feat(pet): 实现宠物经验增加与升级逻辑 在 Player 结构体中新增 AddPetExp 方法,支持宠物经验增长、自动升级及进化判断。升级后会重新计算面板属性并推送更新包。 feat(fight): 重构战斗伤害计算与效果系统 引入 DamageZone 和 EnumDamageType 类型,统一红伤处理流程;移除旧有的 Pet/Skill/Prop 属性获取临时修改机制,改为直接访问真实属性。更新多个技能效果实现以适配新结构。 refactor(effect): 优化技能效果初始化和生命周期方法 统一技能效果初始化方式,明确各阶段回调函数职责,如 PreActionStart、PreAttacked 等。删除已废弃的属性修改钩子函数,并更新状态类效果实现。 refactor(input): 移除 deepcopy 依赖并替换为 go-deepcopy 将原先使用的 mohae/deepcopy 替换为 barkimedes/go-deepcopy,用于战斗节点中的 effect 拷贝逻辑,提升性能和安全性。 refactor(model): 调整玩家信息字段类型 将 PlayerInfo 中的 GoldBean 字段由 int32 改为 uint32,ExpPool 字段由 int64 改为 uint32,确保数据类型一致性与合理性。 feat(nono): 增加 Nono 跟随/收回协议结构定义 新增 NonoFollowOrHomeInInfo 和 NonoFollowOutInfo 结构体,用于处理 Nono 宠物的跟随与收回操作指令。 chore(deps): 添加 go-deepcopy 依赖 在 go.mod 中引入 github.com/barkimedes/go-deepcopy 依赖库,用于替代原有的 deepcopy 工具。
2025-09-26 13:33:55 +08:00
})
2025-09-28 08:13:42 +00:00
//这里其实是受到致死伤害
//然后先触发死亡效果消除所有buff
//然后触发回神效果
}
//回合有先手方和后手方,同时有攻击方和被攻击方
2025-09-29 02:40:35 +08:00
func (f *FightC) enterturn(fattack, sattack action.BattleActionI) {
if f.closefight { //战斗结束
return
}
f.initAttackers(fattack) //初始化先后手
var attacker, defender *input.Input
//开始回合操作
for i := 0; i < 2; i++ {
2025-09-29 02:40:35 +08:00
var attackeraction action.BattleActionI
if i == 0 { //
attacker, defender = f.First, f.Second
attackeraction = fattack
attacker.First = true //先手技能
} else {
attacker, defender = f.Second, f.First
attackeraction = sattack
attacker.First = false //先手技能
}
attacker.Exec(func(t input.Effect) bool { //回合开始前
//结算状态
2025-09-29 02:40:35 +08:00
t.Turn_Start(input.Ctx{Input: attacker})
return true
})
2025-09-29 02:40:35 +08:00
skill, ok := attackeraction.(*action.SelectSkillAction)
canuseskill := true
if !ok || attacker.CurrentPet.Info.Hp <= 0 { //还有系统选择放弃出手的
attacker.AttackValue.SkillID = 0
canuseskill = false
2025-09-07 05:58:47 +08:00
}
2025-09-29 02:40:35 +08:00
var oldskill interface{}
if ok {
oldskill, _ = deepcopy.Anything(skill.Skill) //备份技能
canuseskillok := attacker.Exec(func(t input.Effect) bool { //这个是能否使用技能
//结算状态
//然后这里还可以处理自爆类
return t.Skill_Can(input.Ctx{
Input: attacker,
SkillEntity: skill.Skill,
}) //返回本身结算,如果false,说明不能使用技能了
2025-09-29 02:40:35 +08:00
})
if canuseskill && canuseskillok { //可以使用技能
2025-09-29 02:40:35 +08:00
f.processSkillAttack(attacker, defender, skill)
skill.Skill = oldskill.(*info.SkillEntity) //还原技能效果
2025-09-29 02:40:35 +08:00
}
skill.Skill.Info.PP-- //减少PP
}
2025-09-29 02:40:35 +08:00
//技能使用后
defender.Exec(func(t input.Effect) bool {
t.Skill_Use(input.Ctx{Input: attacker})
return true
})
fmt.Println(i,
2025-09-29 02:40:35 +08:00
"玩家技能伤害:", attacker.DamageZone.Damage,
"自身剩余血量:", attacker.CurrentPet.Info.Hp,
"对手剩余血量:", defender.CurrentPet.Info.Hp,
)
if defender.CurrentPet.Info.Hp == 0 {
defender.AttackValue.SkillID = 0
defender.CanChange = true //被打死就可以切精灵了
if f.IsWin(attacker, defender.CurrentPet.Info.CatchTime) { //然后检查是否战斗结束
var WinnerId uint32
if i == 0 {
WinnerId = f.First.Player.GetInfo().UserID
} else {
WinnerId = f.Second.Player.GetInfo().UserID
}
defer f.Broadcast(func(ff *input.Input) {
//todo 将血量和技能pp传回enterturn
ff.Player.SendFightEndInfo(info.FightOverInfo{
WinnerId: WinnerId,
})
})
defer func() {
f.closefight = true
}()
2025-09-07 05:58:47 +08:00
}
}
}
f.First.Exec(func(t input.Effect) bool { //这个是能否使用技能
//结算状态
2025-09-29 02:40:35 +08:00
t.Turn_End(input.Ctx{
Input: f.Second,
}) //返回本身结算,如果false,说明不能使用技能了
return true
})
f.Second.Exec(func(t input.Effect) bool { //这个是能否使用技能
//结算状态
2025-09-29 02:40:35 +08:00
t.Turn_End(input.Ctx{
Input: f.First,
})
return true
})
f.First.AttackValue.RemainHp = int32(f.First.CurrentPet.Info.Hp)
f.Second.AttackValue.RemainHp = int32(f.Second.CurrentPet.Info.Hp)
ret := info.AttackValueS{
FAttack: *f.First.AttackValue,
SAttack: *f.Second.AttackValue,
}
for i := 0; i < 20; i++ { //堆叠状态剩余回合
2025-09-19 00:29:55 +08:00
t := f.First.GetEffect(input.EffectType.Status, i)
if t.ID != 0 {
ret.FAttack.Status[i] = int8(t.Effect.Duration())
}
t = f.Second.GetEffect(input.EffectType.Status, i)
if t.ID != 0 {
ret.FAttack.Status[i] = int8(t.Effect.Duration())
}
2025-09-19 00:29:55 +08:00
}
f.Broadcast(func(ff *input.Input) {
for _, v := range f.Switch {
if ff.Player.GetInfo().UserID != v.PlayerID {
ff.Player.SendChangePet(v.Reason)
}
}
2025-09-29 02:40:35 +08:00
f.Switch = []*action.ActiveSwitchAction{}
ff.Player.SendAttackValue(ret)
})
}