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

647 lines
17 KiB
Go
Raw Normal View History

package fight
import (
"blazing/common/utils"
"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/modules/blazing/model"
"fmt"
"math/rand"
"reflect"
"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
IsReady bool
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
actionChan chan action.BattleActionI // 所有操作统一从这里进入
2025-09-28 08:13:42 +00:00
Round int //回合数
overchan chan struct{}
First *input.Input
Second *input.Input
closefight bool
info.FightOverInfo
}
func (f *FightC) CanEscape() bool {
return f.Info.Status != info.BattleStatus.FIGHT_WITH_PLAYER
}
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
}
2025-09-30 18:32:15 +08:00
// 获取随机数
func (f *FightC) IsFirst(play common.PlayerI) bool {
return f.First.Player == play
2025-09-30 18:32:15 +08:00
}
// 加载进度
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) bool {
if len(c.GetInfo().PetList) == 0 {
return false
}
temp := input.NewInput(f, c)
temp.AllPet = make([]*info.BattlePetEntity, 0)
temp.InitAttackValue()
for i := 0; i < len(c.GetInfo().PetList); i++ {
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
})
switch f.Info.Mode {
case info.BattleMode.SINGLE_MODE:
temp.AllPet = temp.AllPet[:1]
default:
}
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]
return true
}
// 创建新战斗,邀请方和被邀请方,或者玩家和野怪方
func NewFight(mode, status info.EnumBattleMode, p1 common.PlayerI, p2 common.PlayerI) *FightC {
f := &FightC{}
f.ownerID = p1.GetInfo().UserID
f.overchan = make(chan struct{})
2025-10-22 00:25:38 +08:00
f.StartTime = time.Now()
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.Info.Status = status //房主
f.Info.Mode = mode
ok := f.initplayer(p1, false)
if !ok {
return nil
}
ok = f.initplayer(p2, true)
if !ok {
return nil
}
defer func() {
rr := Fightpool.Submit(f.battleLoop)
if rr != nil {
panic(rr)
}
f.Broadcast(func(ff *input.Input) {
ff.Player.SendNoteReadyToFightInfo(f.Info)
})
}()
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) processSkillAttack(attacker, defender *input.Input, a *info.SkillEntity) {
2025-09-28 08:13:42 +00:00
a.AttackTimeC(attacker.GetProp(5, true)) //计算命中
defender.Exec(func(t input.Effect) bool { //计算闪避 ,然后修改对方命中),同时相当于计算属性无效这种
2025-10-03 20:20:17 +08:00
t.Skill_Hit_to(input.Ctx{ //计算命中后,我方强制改命中效果
Input: attacker,
SkillEntity: a,
2025-09-28 08:13:42 +00:00
})
return true
})
attacker.AttackValue.AttackTime = a.AttackTime //是否命中赋值
attacker.Exec(func(t input.Effect) bool { //计算命中 miss改命中
t.Calculate_Pre(input.Ctx{ //计算视为效果
Input: defender,
SkillEntity: a,
}) //相当于先调整基础命中,不光调整命中,这里还能调整技能属性,暴击率
return true
})
attacker.Exec(func(t input.Effect) bool { //计算命中 miss改命中
t.Skill_Hit(input.Ctx{ //计算变威力
Input: defender,
SkillEntity: a,
}) //相当于先调整基础命中,不光调整命中,这里还能调整技能属性,暴击率
return true
})
//技能命中+效果失效 这里就是修改效果命中为false
//技能miss+效果生效 这里属于强制改命中效果,但是正常来说,技能miss掉后效果也应该失效
//技能失效+效果失效
2025-10-22 00:25:38 +08:00
// 记录技能信息
attacker.SkillID = uint32(a.ID) //获取技能ID
if attacker.AttackTime > 0 { //如果命中
``` refactor(socket): 优化消息处理逻辑,避免顺序执行问题 将消息处理的循环从协程外部移入协程内部,确保每个消息在独立的 goroutine 中处理, 避免因并发导致的消息顺序错乱问题。同时移除了多余的空行,使代码更简洁。 fix(controller): 为低 ID 用户设置 VIP 标志 在 COMMEND_ONLINE 接口逻辑中,新增对 UserID 小于 10000 的用户设置 IsVip = 1, 用于标识测试或特殊用户身份。 refactor(fight): 重构状态类技能效果注册与触发逻辑 - 使用 map 统一管理技能 ID 与状态类型映射关系,简化注册流程 - 重命名工厂函数 newp 为 newEffectStatus,提升可读性 - 增加命中判断和持续回合数随机逻辑,提高技能触发的灵活性 - 支持通过 SideEffectArgs 配置持续时间,默认 1~3 回合 refactor(fight): 重构能力操作类效果(Effect3) - 重命名字段 Rev、Etype 为 Reverse、OpType 提高语义清晰度 - 优化结构体初始化方式,使用匿名结构体数组进行批量注册 - 明确能力变化作用对象(自身/对手)逻辑,增强可维护性 refactor(fight): 重构能力值变化效果(Effect4/5) - 重命名构造函数 NewEffectStat 为 newEffectStat,符合 Go 命名规范 - 优化参数解析逻辑,防止 SideEffectArgs 越界访问 - 统一处理加减类型判断和属性变更逻辑,减少重复代码 refactor(fight): 重构技能威力翻倍条件判断逻辑 - 引入全局函数注册表 statusFuncRegistry 管理所有状态判断逻辑 - Effect96 改为通过 StatusID 查找对应函数,提升扩展性 - 合并多个相似效果注册逻辑,统一初始化入口 refactor(fight): 优化状态效果获取逻辑 - 修改 GetEffect 方法返回单个 Effect 而非数组,简化调用逻辑 - 在 fightc.go 中更新状态持续时间获取方式 - 修复部分未使用的代码注释,提高代码整洁度 fix(fight): 限制治疗后血量不超过最大值 在 Heal 方法中增加血量上限判断,防止因治疗导致当前血量超出最大值。 refactor(fight): 移除调试日志打印 删除 loop.go 中多余的 fmt.Println 日志输出语句,避免影响性能和日志干扰。 refactor(fight): 优化精灵切换相关逻辑 - 移除未实现的 panic 抛出,避免运行时异常 - 注释掉不必要的下场清除 effect 逻辑,待后续确认是否需要恢复 feat(nono): 移除 PetCureInboundInfo 中的 Type 字段 该字段原用于控制飞行模式开关,现已移除,后续功能将通过其他方式实现。 feat(player): 新增 SaveDone channel 支持保存完成通知 在 ClientData 结构体中添加 SaveDone channel,用于异步通知保存操作完成状态。 fix(user): 设置推荐服务器在线人数默认值 当 ServerInfo.OnlineID == 2 时,强制设置 UserCnt = 300 并使用测试 IP, 便于客户端识别测试服节点。 ```
2025-11-03 04:16:30 +08:00
attacker.CalculateCrit(defender, a) //暴击计算
attacker.AttackValue.IsCritical = a.Crit
attacker.DamageZone.Damage = attacker.CalculatePower(defender, a)
``` refactor(socket): 优化消息处理逻辑,避免顺序执行问题 将消息处理的循环从协程外部移入协程内部,确保每个消息在独立的 goroutine 中处理, 避免因并发导致的消息顺序错乱问题。同时移除了多余的空行,使代码更简洁。 fix(controller): 为低 ID 用户设置 VIP 标志 在 COMMEND_ONLINE 接口逻辑中,新增对 UserID 小于 10000 的用户设置 IsVip = 1, 用于标识测试或特殊用户身份。 refactor(fight): 重构状态类技能效果注册与触发逻辑 - 使用 map 统一管理技能 ID 与状态类型映射关系,简化注册流程 - 重命名工厂函数 newp 为 newEffectStatus,提升可读性 - 增加命中判断和持续回合数随机逻辑,提高技能触发的灵活性 - 支持通过 SideEffectArgs 配置持续时间,默认 1~3 回合 refactor(fight): 重构能力操作类效果(Effect3) - 重命名字段 Rev、Etype 为 Reverse、OpType 提高语义清晰度 - 优化结构体初始化方式,使用匿名结构体数组进行批量注册 - 明确能力变化作用对象(自身/对手)逻辑,增强可维护性 refactor(fight): 重构能力值变化效果(Effect4/5) - 重命名构造函数 NewEffectStat 为 newEffectStat,符合 Go 命名规范 - 优化参数解析逻辑,防止 SideEffectArgs 越界访问 - 统一处理加减类型判断和属性变更逻辑,减少重复代码 refactor(fight): 重构技能威力翻倍条件判断逻辑 - 引入全局函数注册表 statusFuncRegistry 管理所有状态判断逻辑 - Effect96 改为通过 StatusID 查找对应函数,提升扩展性 - 合并多个相似效果注册逻辑,统一初始化入口 refactor(fight): 优化状态效果获取逻辑 - 修改 GetEffect 方法返回单个 Effect 而非数组,简化调用逻辑 - 在 fightc.go 中更新状态持续时间获取方式 - 修复部分未使用的代码注释,提高代码整洁度 fix(fight): 限制治疗后血量不超过最大值 在 Heal 方法中增加血量上限判断,防止因治疗导致当前血量超出最大值。 refactor(fight): 移除调试日志打印 删除 loop.go 中多余的 fmt.Println 日志输出语句,避免影响性能和日志干扰。 refactor(fight): 优化精灵切换相关逻辑 - 移除未实现的 panic 抛出,避免运行时异常 - 注释掉不必要的下场清除 effect 逻辑,待后续确认是否需要恢复 feat(nono): 移除 PetCureInboundInfo 中的 Type 字段 该字段原用于控制飞行模式开关,现已移除,后续功能将通过其他方式实现。 feat(player): 新增 SaveDone channel 支持保存完成通知 在 ClientData 结构体中添加 SaveDone channel,用于异步通知保存操作完成状态。 fix(user): 设置推荐服务器在线人数默认值 当 ServerInfo.OnlineID == 2 时,强制设置 UserCnt = 300 并使用测试 IP, 便于客户端识别测试服节点。 ```
2025-11-03 04:16:30 +08:00
//睡眠受击消除
if attacker.AttackValue.IsCritical == 1 {
``` refactor(socket): 优化消息处理逻辑,避免顺序执行问题 将消息处理的循环从协程外部移入协程内部,确保每个消息在独立的 goroutine 中处理, 避免因并发导致的消息顺序错乱问题。同时移除了多余的空行,使代码更简洁。 fix(controller): 为低 ID 用户设置 VIP 标志 在 COMMEND_ONLINE 接口逻辑中,新增对 UserID 小于 10000 的用户设置 IsVip = 1, 用于标识测试或特殊用户身份。 refactor(fight): 重构状态类技能效果注册与触发逻辑 - 使用 map 统一管理技能 ID 与状态类型映射关系,简化注册流程 - 重命名工厂函数 newp 为 newEffectStatus,提升可读性 - 增加命中判断和持续回合数随机逻辑,提高技能触发的灵活性 - 支持通过 SideEffectArgs 配置持续时间,默认 1~3 回合 refactor(fight): 重构能力操作类效果(Effect3) - 重命名字段 Rev、Etype 为 Reverse、OpType 提高语义清晰度 - 优化结构体初始化方式,使用匿名结构体数组进行批量注册 - 明确能力变化作用对象(自身/对手)逻辑,增强可维护性 refactor(fight): 重构能力值变化效果(Effect4/5) - 重命名构造函数 NewEffectStat 为 newEffectStat,符合 Go 命名规范 - 优化参数解析逻辑,防止 SideEffectArgs 越界访问 - 统一处理加减类型判断和属性变更逻辑,减少重复代码 refactor(fight): 重构技能威力翻倍条件判断逻辑 - 引入全局函数注册表 statusFuncRegistry 管理所有状态判断逻辑 - Effect96 改为通过 StatusID 查找对应函数,提升扩展性 - 合并多个相似效果注册逻辑,统一初始化入口 refactor(fight): 优化状态效果获取逻辑 - 修改 GetEffect 方法返回单个 Effect 而非数组,简化调用逻辑 - 在 fightc.go 中更新状态持续时间获取方式 - 修复部分未使用的代码注释,提高代码整洁度 fix(fight): 限制治疗后血量不超过最大值 在 Heal 方法中增加血量上限判断,防止因治疗导致当前血量超出最大值。 refactor(fight): 移除调试日志打印 删除 loop.go 中多余的 fmt.Println 日志输出语句,避免影响性能和日志干扰。 refactor(fight): 优化精灵切换相关逻辑 - 移除未实现的 panic 抛出,避免运行时异常 - 注释掉不必要的下场清除 effect 逻辑,待后续确认是否需要恢复 feat(nono): 移除 PetCureInboundInfo 中的 Type 字段 该字段原用于控制飞行模式开关,现已移除,后续功能将通过其他方式实现。 feat(player): 新增 SaveDone channel 支持保存完成通知 在 ClientData 结构体中添加 SaveDone channel,用于异步通知保存操作完成状态。 fix(user): 设置推荐服务器在线人数默认值 当 ServerInfo.OnlineID == 2 时,强制设置 UserCnt = 300 并使用测试 IP, 便于客户端识别测试服节点。 ```
2025-11-03 04:16:30 +08:00
//暴击破防
if a.Category() == info.Category.PHYSICAL && defender.Prop[1] > 0 {
``` refactor(socket): 优化消息处理逻辑,避免顺序执行问题 将消息处理的循环从协程外部移入协程内部,确保每个消息在独立的 goroutine 中处理, 避免因并发导致的消息顺序错乱问题。同时移除了多余的空行,使代码更简洁。 fix(controller): 为低 ID 用户设置 VIP 标志 在 COMMEND_ONLINE 接口逻辑中,新增对 UserID 小于 10000 的用户设置 IsVip = 1, 用于标识测试或特殊用户身份。 refactor(fight): 重构状态类技能效果注册与触发逻辑 - 使用 map 统一管理技能 ID 与状态类型映射关系,简化注册流程 - 重命名工厂函数 newp 为 newEffectStatus,提升可读性 - 增加命中判断和持续回合数随机逻辑,提高技能触发的灵活性 - 支持通过 SideEffectArgs 配置持续时间,默认 1~3 回合 refactor(fight): 重构能力操作类效果(Effect3) - 重命名字段 Rev、Etype 为 Reverse、OpType 提高语义清晰度 - 优化结构体初始化方式,使用匿名结构体数组进行批量注册 - 明确能力变化作用对象(自身/对手)逻辑,增强可维护性 refactor(fight): 重构能力值变化效果(Effect4/5) - 重命名构造函数 NewEffectStat 为 newEffectStat,符合 Go 命名规范 - 优化参数解析逻辑,防止 SideEffectArgs 越界访问 - 统一处理加减类型判断和属性变更逻辑,减少重复代码 refactor(fight): 重构技能威力翻倍条件判断逻辑 - 引入全局函数注册表 statusFuncRegistry 管理所有状态判断逻辑 - Effect96 改为通过 StatusID 查找对应函数,提升扩展性 - 合并多个相似效果注册逻辑,统一初始化入口 refactor(fight): 优化状态效果获取逻辑 - 修改 GetEffect 方法返回单个 Effect 而非数组,简化调用逻辑 - 在 fightc.go 中更新状态持续时间获取方式 - 修复部分未使用的代码注释,提高代码整洁度 fix(fight): 限制治疗后血量不超过最大值 在 Heal 方法中增加血量上限判断,防止因治疗导致当前血量超出最大值。 refactor(fight): 移除调试日志打印 删除 loop.go 中多余的 fmt.Println 日志输出语句,避免影响性能和日志干扰。 refactor(fight): 优化精灵切换相关逻辑 - 移除未实现的 panic 抛出,避免运行时异常 - 注释掉不必要的下场清除 effect 逻辑,待后续确认是否需要恢复 feat(nono): 移除 PetCureInboundInfo 中的 Type 字段 该字段原用于控制飞行模式开关,现已移除,后续功能将通过其他方式实现。 feat(player): 新增 SaveDone channel 支持保存完成通知 在 ClientData 结构体中添加 SaveDone channel,用于异步通知保存操作完成状态。 fix(user): 设置推荐服务器在线人数默认值 当 ServerInfo.OnlineID == 2 时,强制设置 UserCnt = 300 并使用测试 IP, 便于客户端识别测试服节点。 ```
2025-11-03 04:16:30 +08:00
defender.Prop[1] = 0
} else if a.Category() == info.Category.SPECIAL && defender.Prop[3] > 0 {
``` refactor(socket): 优化消息处理逻辑,避免顺序执行问题 将消息处理的循环从协程外部移入协程内部,确保每个消息在独立的 goroutine 中处理, 避免因并发导致的消息顺序错乱问题。同时移除了多余的空行,使代码更简洁。 fix(controller): 为低 ID 用户设置 VIP 标志 在 COMMEND_ONLINE 接口逻辑中,新增对 UserID 小于 10000 的用户设置 IsVip = 1, 用于标识测试或特殊用户身份。 refactor(fight): 重构状态类技能效果注册与触发逻辑 - 使用 map 统一管理技能 ID 与状态类型映射关系,简化注册流程 - 重命名工厂函数 newp 为 newEffectStatus,提升可读性 - 增加命中判断和持续回合数随机逻辑,提高技能触发的灵活性 - 支持通过 SideEffectArgs 配置持续时间,默认 1~3 回合 refactor(fight): 重构能力操作类效果(Effect3) - 重命名字段 Rev、Etype 为 Reverse、OpType 提高语义清晰度 - 优化结构体初始化方式,使用匿名结构体数组进行批量注册 - 明确能力变化作用对象(自身/对手)逻辑,增强可维护性 refactor(fight): 重构能力值变化效果(Effect4/5) - 重命名构造函数 NewEffectStat 为 newEffectStat,符合 Go 命名规范 - 优化参数解析逻辑,防止 SideEffectArgs 越界访问 - 统一处理加减类型判断和属性变更逻辑,减少重复代码 refactor(fight): 重构技能威力翻倍条件判断逻辑 - 引入全局函数注册表 statusFuncRegistry 管理所有状态判断逻辑 - Effect96 改为通过 StatusID 查找对应函数,提升扩展性 - 合并多个相似效果注册逻辑,统一初始化入口 refactor(fight): 优化状态效果获取逻辑 - 修改 GetEffect 方法返回单个 Effect 而非数组,简化调用逻辑 - 在 fightc.go 中更新状态持续时间获取方式 - 修复部分未使用的代码注释,提高代码整洁度 fix(fight): 限制治疗后血量不超过最大值 在 Heal 方法中增加血量上限判断,防止因治疗导致当前血量超出最大值。 refactor(fight): 移除调试日志打印 删除 loop.go 中多余的 fmt.Println 日志输出语句,避免影响性能和日志干扰。 refactor(fight): 优化精灵切换相关逻辑 - 移除未实现的 panic 抛出,避免运行时异常 - 注释掉不必要的下场清除 effect 逻辑,待后续确认是否需要恢复 feat(nono): 移除 PetCureInboundInfo 中的 Type 字段 该字段原用于控制飞行模式开关,现已移除,后续功能将通过其他方式实现。 feat(player): 新增 SaveDone channel 支持保存完成通知 在 ClientData 结构体中添加 SaveDone channel,用于异步通知保存操作完成状态。 fix(user): 设置推荐服务器在线人数默认值 当 ServerInfo.OnlineID == 2 时,强制设置 UserCnt = 300 并使用测试 IP, 便于客户端识别测试服节点。 ```
2025-11-03 04:16:30 +08:00
defender.Prop[3] = 0
}
2025-09-29 02:40:35 +08:00
//暴击翻倍
attacker.DamageZone.Damage = attacker.DamageZone.Damage.Mul(decimal.NewFromInt(2))
}
}
for _, e := range attacker.EffectCache {
//这里实现应该参考本地技能是否命中,然后
e.Hit(a.AttackTime != 0) //我方效果命中
}
// for _, t := range defender.EffectCache {
// if t.GetInput() == attacker { //如果取反,说明是给对方添加的回合效果
// t.Hit(a.AttackTime != 0)
// }
// }
// 扣减防御方血量
attacker.Exec(func(t input.Effect) bool {
2025-09-29 02:40:35 +08:00
t.OnSkill(input.Ctx{
Input: defender,
SkillEntity: a,
DamageZone: &info.DamageZone{}, //给个空的,方便传递
2025-09-29 02:40:35 +08:00
}) //调用伤害计算
return true
})
2025-09-29 02:40:35 +08:00
defender.Damage(input.Ctx{
Input: attacker,
SkillEntity: a,
2025-09-29 02:40:35 +08:00
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
//然后触发回神效果
}
func IsNil(x interface{}) bool {
if x == nil {
return true
}
rv := reflect.ValueOf(x)
return rv.Kind() == reflect.Ptr && rv.IsNil()
}
func (f *FightC) copyskill(t *action.SelectSkillAction) *info.SkillEntity {
if t.SkillEntity == nil {
return nil
}
oldskill, _ := deepcopy.Anything(t.SkillEntity) //备份技能
oldskill.(*info.SkillEntity).Rand = f.rand //拷贝后随机数丢失
return oldskill.(*info.SkillEntity)
}
//回合有先手方和后手方,同时有攻击方和被攻击方
2025-09-30 18:32:15 +08:00
func (f *FightC) enterturn(fattack, sattack *action.SelectSkillAction) {
2025-09-30 18:32:15 +08:00
// 伤害值
// 根据攻击方归属设置当前战斗的主/次攻击方属性
if fattack != nil {
if fattack.GetPlayerID() == f.ownerID {
f.First, f.Second = f.Our, f.Opp // 攻击方为我方时,主攻击方是我方
2025-09-30 18:32:15 +08:00
} else {
f.First, f.Second = f.Opp, f.Our // 攻击方为对方时,主攻击方是对方
2025-09-30 18:32:15 +08:00
}
} else {
f.First, f.Second = f.Our, f.Opp
2025-09-30 18:32:15 +08:00
}
f.First.ResetAttackValue()
f.Second.ResetAttackValue()
2025-09-30 18:32:15 +08:00
if fattack != nil {
//是否miss都应该施加解析effect
f.First.Parseskill(f.Second, fattack) //解析到临时数据
f.First.Exec(func(t input.Effect) bool { //回合开始前
//结算状态
t.Compare_Pre(fattack, sattack) //先结算技能的优先级
return true
})
2025-09-30 18:32:15 +08:00
}
if sattack != nil {
f.Second.Parseskill(f.Second, sattack) //解析到临时数据
2025-09-30 18:32:15 +08:00
f.Second.Exec(func(t input.Effect) bool { //回合开始前
//结算状态
t.Compare_Pre(fattack, sattack) //先结算技能的优先级
return true
})
}
if fattack != nil && sattack != nil {
switch {
case fattack.SkillEntity.Priority < sattack.SkillEntity.Priority:
2025-09-30 18:32:15 +08:00
fattack, sattack = sattack, fattack //互换先手权
f.First, f.Second = f.Second, f.First
case fattack.SkillEntity.Priority == sattack.SkillEntity.Priority:
if f.Second.GetProp(4, false) > f.First.GetProp(4, false) {
fattack, sattack = sattack, fattack //互换先手权
f.First, f.Second = f.Second, f.First
}
2025-09-30 18:32:15 +08:00
}
}
2025-09-30 10:40:36 +00:00
var attacker, defender *input.Input
f.First.Exec(func(t input.Effect) bool { //回合开始前
//结算状态
t.Turn_Start(input.Ctx{Input: f.First})
return true
})
f.Second.Exec(func(t input.Effect) bool { //回合开始前
//结算状态
t.Turn_Start(input.Ctx{Input: f.Second})
return true
})
//开始回合操作
for i := 0; i < 2; i++ {
var oldskill *info.SkillEntity //原始技能
var currentskill *info.SkillEntity //当前技能
if i == 0 { //
2025-09-30 10:40:36 +00:00
attacker, defender = f.First, f.Second
oldskill = f.copyskill(fattack)
2025-09-30 10:40:36 +00:00
} else {
attacker, defender = f.Second, f.First
oldskill = f.copyskill(sattack)
}
currentskill = oldskill
//fmt.Println("开始攻击威力", oldskill.Power)
2025-09-29 02:40:35 +08:00
canuseskill := true
2025-09-30 10:40:36 +00:00
// 实际上攻击方 还有系统选择放弃出手的
if !action.CanUse(currentskill) || attacker.CurrentPet.Info.Hp <= 0 {
// attacker.AttackValue.SkillID = 0
canuseskill = false
2025-09-07 05:58:47 +08:00
}
canuseskillok := attacker.Exec(func(t input.Effect) bool { //这个是能否使用技能
//结算状态
//然后这里还可以处理自爆类
return t.Skill_Hit_Pre(input.Ctx{
Input: defender,
SkillEntity: currentskill,
DamageZone: &info.DamageZone{},
}) //返回本身结算,如果false,说明不能使用技能了
})
// 结算状态
// 然后这里还可以处理自爆类
if canuseskill && canuseskillok { //可以使用技能
f.processSkillAttack(attacker, defender, currentskill)
currentskill = oldskill
// fmt.Println("结束攻击1", oldskill.Power)
// fmt.Println("结束攻击", currentskill.Power)
_, skill, ok := utils.FindWithIndex(attacker.CurrentPet.Info.SkillList, func(item model.SkillInfo) bool {
return item.ID == currentskill.Info.ID
})
if ok {
skill.PP--
}
}
2025-09-29 02:40:35 +08:00
//技能使用后
defender.Exec(func(t input.Effect) bool {
t.Skill_Use(input.Ctx{Input: attacker, SkillEntity: currentskill, DamageZone: &info.DamageZone{
Type: info.DamageType.Red,
Damage: attacker.DamageZone.Damage,
}})
2025-09-29 02:40:35 +08:00
return true
})
//技能使用后
attacker.Exec(func(t input.Effect) bool { //技能使用后的我方效果
t.Skill_Useed(input.Ctx{Input: defender, SkillEntity: currentskill, DamageZone: &info.DamageZone{
Type: info.DamageType.Red,
Damage: attacker.DamageZone.Damage,
}})
return true
})
fmt.Println(i,
// "玩家技能:", oldskill.(*info.SkillEntity).ID,
2025-09-29 02:40:35 +08:00
"玩家技能伤害:", attacker.DamageZone.Damage,
"自身剩余血量:", attacker.CurrentPet.Info.Hp,
"对手剩余血量:", defender.CurrentPet.Info.Hp,
)
if attacker.CurrentPet.Info.Hp <= 0 {
if defender.CurrentPet.Info.Hp == 0 { //先手方死亡,触发反同归于尽
defender.CurrentPet.Info.Hp = 1
}
if f.IsWin(defender, attacker.CurrentPet.Info.CatchTime) { //然后检查是否战斗结束
f.FightOverInfo.WinnerId = defender.UserID
f.closefight = true
break
}
}
if defender.CurrentPet.Info.Hp == 0 {
if attacker.CurrentPet.Info.Hp == 0 { //先手方死亡,触发反同归于尽
attacker.CurrentPet.Info.Hp = 1
}
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
}
f.FightOverInfo.WinnerId = WinnerId
f.closefight = true
break
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.RemainHp = int32(f.First.CurrentPet.Info.Hp)
f.First.SkillList = f.First.CurrentPet.Info.SkillList
f.Second.SkillList = f.Second.CurrentPet.Info.SkillList
f.Second.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 != nil { //状态都是叠层类的
ret.FAttack.Status[i] = int8(t.Duration()) + 1
}
t = f.Second.GetEffect(input.EffectType.Status, i)
if t != nil {
ret.SAttack.Status[i] = int8(t.Duration()) + 1
}
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)
}
}
ff.Player.SendAttackValue(ret)
})
f.Switch = []*action.ActiveSwitchAction{}
}
// 处理战斗中宠物死亡后的逻辑(同归于尽+胜利判定)
// i=0 表示当前攻击者是先手方i=1 表示当前攻击者是后手方
func (f *FightC) handlePetDeath(attacker, defender *input.Input, i int) {
// 辅助函数根据攻击者顺序获取胜利者ID
getWinnerID := func() uint32 {
if i == 0 {
// 若攻击者是先手方胜利方为后手方defender对应的玩家
return defender.Player.GetInfo().UserID
}
// 若攻击者是后手方胜利方为先手方attacker对应的玩家
return attacker.Player.GetInfo().UserID
}
// 1. 处理攻击方宠物死亡的情况
if attacker.CurrentPet.Info.Hp <= 0 {
// 同归于尽攻击方死亡时若防守方也死亡给防守方留1点血
if defender.CurrentPet.Info.Hp == 0 {
defender.CurrentPet.Info.Hp = 1
}
// 检查是否战斗结束(攻击方死亡后,判定防守方胜利)
if f.IsWin(defender, attacker.CurrentPet.Info.CatchTime) {
f.FightOverInfo.WinnerId = getWinnerID()
f.closefight = true
return // 战斗结束,提前返回
}
}
// 2. 处理防守方宠物死亡的情况(若战斗未结束)
if !f.closefight && defender.CurrentPet.Info.Hp == 0 {
// 同归于尽防守方死亡时若攻击方也死亡给攻击方留1点血
if attacker.CurrentPet.Info.Hp == 0 {
attacker.CurrentPet.Info.Hp = 1
}
// 防守方被击败,允许切换精灵
defender.CanChange = true
// 检查是否战斗结束(防守方死亡后,判定攻击方胜利)
if f.IsWin(attacker, defender.CurrentPet.Info.CatchTime) {
f.FightOverInfo.WinnerId = getWinnerID()
f.closefight = true
}
}
}