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 工具。
This commit is contained in:
2025-09-26 13:33:55 +08:00
parent 728f3a52d6
commit c52c409ffc
33 changed files with 340 additions and 263 deletions

View File

@@ -31,3 +31,10 @@ func (h Controller) PlayerGoldCount(data *item.GoldOnlineRemainInboundInfo, c *p
GoldNumber: uint32(c.Info.GoldBean) * 100,
}, 0
}
func (h Controller) PlayerExp(data *item.ExpTotalRemainInboundInfo, c *player.Player) (result *item.ExpTotalRemainOutboundInfo, err errorcode.ErrorCode) {
return &item.ExpTotalRemainOutboundInfo{
TotalExp: uint32(c.Info.ExpPool),
}, 0
}

23
logic/controller/nano.go Normal file
View File

@@ -0,0 +1,23 @@
package controller
import (
"blazing/common/socket/errorcode"
"blazing/logic/service/nono"
"blazing/logic/service/player"
)
// 处理命令: 105
func (h *Controller) NonoFollowOrHome(data *nono.NonoFollowOrHomeInInfo, c *player.Conn) (result *nono.NonoFollowOutInfo, err errorcode.ErrorCode) { //这个时候player应该是空的
result = &nono.NonoFollowOutInfo{
data.Head.UserID,
data.Flag,
data.Flag,
"",
0,
0,
}
return
}

View File

@@ -173,3 +173,14 @@ func FindWithIndex[T any](slice []T, predicate func(item T) bool) (int, *T, bool
}
return -1, nil, false
}
func (h Controller) SetPetExp(data *pet.PetSetExpInboundInfo, c *player.Player) (result *pet.PetSetExpOutboundInfo, err errorcode.ErrorCode) {
_, onpet, ok := FindWithIndex(c.Info.PetList, func(item model.PetInfo) bool {
return item.CatchTime == data.CatchTime
})
if ok {
c.AddPetExp(onpet, data.Exp)
}
return &pet.PetSetExpOutboundInfo{}, 0
}

View File

@@ -9,6 +9,7 @@ require (
github.com/antlabs/cronex v0.0.5 // indirect
github.com/antlabs/stl v0.0.2 // indirect
github.com/antlabs/timer v0.1.4 // indirect
github.com/barkimedes/go-deepcopy v0.0.0-20220514131651-17c30cfc62df // indirect
github.com/butoften/array v1.0.9 // indirect
github.com/cespare/xxhash/v2 v2.3.0 // indirect
github.com/clbanning/mxj/v2 v2.7.0 // indirect

View File

@@ -8,6 +8,8 @@ github.com/antlabs/timer v0.1.4 h1:MHdE00MDnNfhJCmqSOdLXs35uGNwfkMwfbynxrGmQ1c=
github.com/antlabs/timer v0.1.4/go.mod h1:mpw4zlD5KVjstEyUDp43DGLWsY076Mdo4bS78NTseRE=
github.com/badu/bus v1.0.3 h1:MViRRyuFraixfaI2rfAqrkQao7ZilyFz6HacbmPk1aE=
github.com/badu/bus v1.0.3/go.mod h1:77qc3Fi2qSUoakSR34PIWrTHB6gM2NJKceRsYUbx41Q=
github.com/barkimedes/go-deepcopy v0.0.0-20220514131651-17c30cfc62df h1:GSoSVRLoBaFpOOds6QyY1L8AX7uoY+Ln3BHc22W40X0=
github.com/barkimedes/go-deepcopy v0.0.0-20220514131651-17c30cfc62df/go.mod h1:hiVxq5OP2bUGBRNS3Z/bt/reCLFNbdcST6gISi1fiOM=
github.com/butoften/array v1.0.9 h1:/kPHAc+fHz72u5B23p2W1RzIoT2eOYvhsY0tKMvsHEc=
github.com/butoften/array v1.0.9/go.mod h1:RgJ3XIUy/Z2rQllTkXmS4LtfqJeD3mjYJ4XoP3odTqM=
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=

View File

@@ -11,49 +11,46 @@ type Effect10 struct {
Status info.EnumBattleStatus
}
func newp(t1 info.EnumBattleStatus) *Effect10 {
t := &Effect10{
Status: info.PetStatus.Paralysis,
}
t.Effect = t
return t
}
func init() {
//n%令对方麻痹
input.InitEffect(input.EffectType.Skill, 10, &Effect10{
Status: info.PetStatus.Paralysis,
})
input.InitEffect(input.EffectType.Skill, 10, newp(info.PetStatus.Paralysis))
// n%令对方中毒
input.InitEffect(input.EffectType.Skill, 11, &Effect10{
Status: info.PetStatus.Poisoned,
})
input.InitEffect(input.EffectType.Skill, 11, newp(info.PetStatus.Poisoned))
//n%令对方烧伤
input.InitEffect(input.EffectType.Skill, 12, &Effect10{
Status: info.PetStatus.Burned,
})
input.InitEffect(input.EffectType.Skill, 12, newp(info.PetStatus.Burned))
//n回合吸取对方最大体力的1/8(草系无效)
//n%令对方冻伤
input.InitEffect(input.EffectType.Skill, 14, &Effect10{
Status: info.PetStatus.Frozen,
})
input.InitEffect(input.EffectType.Skill, 14, newp(info.PetStatus.Frozen))
//n%令对方害怕
input.InitEffect(input.EffectType.Skill, 15, &Effect10{
Status: info.PetStatus.Fear,
})
input.InitEffect(input.EffectType.Skill, 15, newp(info.PetStatus.Fear))
//n%令对手睡眠
input.InitEffect(input.EffectType.Skill, 16, &Effect10{
Status: info.PetStatus.Sleep,
})
input.InitEffect(input.EffectType.Skill, 16, newp(info.PetStatus.Sleep))
//n%令对方石化
input.InitEffect(input.EffectType.Skill, 94, &Effect10{
Status: info.PetStatus.Petrified,
})
input.InitEffect(input.EffectType.Skill, 94, newp(info.PetStatus.Petrified))
//n%几率令对手混乱
input.InitEffect(input.EffectType.Skill, 99, &Effect10{
Status: info.PetStatus.Confused,
})
input.InitEffect(input.EffectType.Skill, 99, newp(info.PetStatus.Confused))
//n%几率令对方易燃
input.InitEffect(input.EffectType.Skill, 114, &Effect10{
Status: info.PetStatus.Flammable,
})
input.InitEffect(input.EffectType.Skill, 114, newp(info.PetStatus.Flammable))
}
func (e *Effect10) AfterSkill(opp *input.Input, skill *info.SkillEntity) {
func (e *Effect10) OnHit(opp *input.Input, skill *info.SkillEntity) {
if e.Hit() {
t, _, _ := e.Input.Player.Roll(e.EffectNode.SideEffectArgs[0], 100)
if t {

View File

@@ -16,11 +16,12 @@ func init() {
func NewEffectStat(b bool) input.Effect {
return &EffectStat{
node.EffectNode{},
b,
ret := &EffectStat{
EffectNode: node.EffectNode{},
Etype: b,
}
ret.Effect = ret
return ret
}
type EffectStat struct {

View File

@@ -34,9 +34,6 @@ func init() {
}
func (e *Effect61) PreSkill(opp *input.Input, skill *info.SkillEntity) {
opp.Pet(e.Input, func() {
skill.Power = int(e.Input.FightC.GetRand().Int31n(int32(e.Max)-int32(e.Min)+1) + int32(e.Min))
})
skill.Power = int(e.Input.FightC.GetRand().Int31n(int32(e.Max)-int32(e.Min)+1) + int32(e.Min))
}

View File

@@ -16,11 +16,13 @@ type Effect62 struct {
}
func init() {
input.InitEffect(input.EffectType.Skill, 62, &Effect62{
t := &Effect62{
EffectNode: node.EffectNode{
Owner: true,
},
})
}
t.Effect = t
input.InitEffect(input.EffectType.Skill, 62, t)
}

View File

@@ -2,8 +2,11 @@ package effect
import (
"blazing/common/utils"
"blazing/logic/service/fight/info"
"blazing/logic/service/fight/input"
"blazing/logic/service/fight/node"
"github.com/shopspring/decimal"
)
/**
@@ -22,16 +25,12 @@ type Effect8 struct {
}
// 伤害落实前触发,限制最大伤害
func (e *Effect8) Attack(opp *input.Input, eff *input.EffectID) {
func (e *Effect8) Attack(opp *input.Input, v *info.DamageZone) {
// //在这里修改伤害
// if opp.GetEffect(input.EffectType.Damage, 0).MaxStack() != 0 { //限制最大伤害
// opp.GetEffect(input.EffectType.Damage, 0).Stack(utils.Min(opp.GetEffect(input.EffectType.Damage, 0).Stack(), opp.GetEffect(input.EffectType.Damage, 0).MaxStack()))
// }
if v.Type == info.DamageType.Red {
opp.Pet(e.Input, func() { //我方取敌方属性
v.Damage = decimal.NewFromInt(utils.Min(v.Damage.IntPart(), int64(opp.CurrentPet.Info.Hp)-1))
e.Input.DamageZone[0] = utils.Min(e.Input.GetDamage(0), int(opp.CurrentPet.Info.Hp)-1)
})
}
}

View File

@@ -14,13 +14,12 @@ func init() {
EffectNode: node.EffectNode{},
Status: func(i, o *input.Input) bool {
ret := false
o.Pet(i, func() { //我方取敌方防御
if o.CurrentPet.Info.Hp < (o.CurrentPet.Info.MaxHp / 2) {
ret = true
}
if o.CurrentPet.Info.Hp < (o.CurrentPet.Info.MaxHp / 2) {
ret = true
}
})
return ret
},
})

View File

@@ -4,6 +4,8 @@ import (
"blazing/logic/service/fight/info"
"blazing/logic/service/fight/input"
"blazing/logic/service/fight/node"
"github.com/shopspring/decimal"
)
// 施加一个基类effect
@@ -27,8 +29,12 @@ type DrainHP struct {
}
func (e *DrainHP) OnTurnStart(opp *input.Input) {
e.Input.Pet(e.Input, func() { //我方取我方
e.Input.CurrentPet.Info.Hp = -e.Input.CurrentPet.Info.MaxHp / 8
e.Input.Damage(e.Input, info.DamageZone{
Type: info.DamageType.Red,
Damage: decimal.NewFromUint64(uint64(opp.CurrentPet.Info.MaxHp)).
Div(decimal.NewFromInt(8)),
})
}
@@ -40,9 +46,8 @@ type StatusDrainedHP struct {
func (e *StatusDrainedHP) OnTurnStart(opp *input.Input) {
e.DrainHP.OnTurnStart(opp)
opp.Pet(e.Input, func() { //我方取我方
opp.CurrentPet.Info.Hp = -e.Input.CurrentPet.Info.MaxHp / 8
})
opp.CurrentPet.Info.Hp = -e.Input.CurrentPet.Info.MaxHp / 8
}
func init() {

View File

@@ -12,6 +12,7 @@ import (
"time"
"github.com/jinzhu/copier"
"github.com/shopspring/decimal"
)
type FightC struct {
@@ -440,10 +441,7 @@ func (f *FightC) processSkillAttack(attacker, defender *input.Input, a *SelectSk
if attacker.AttackValue.AttackTime > 0 { //如果命中
attacker.Skill(a.Skill, func() { //变威力作用
attacker.DamageZone[0] = int(attacker.CalculatePower(defender, a.Skill).IntPart())
})
attacker.DamageZone[0] = int(attacker.CalculatePower(defender, a.Skill).IntPart())
attacker.AttackValue.IsCritical = a.Skill.Crit
@@ -471,8 +469,11 @@ func (f *FightC) processSkillAttack(attacker, defender *input.Input, a *SelectSk
}
defender.Damage(attacker, 0)
attacker.AttackValue.LostHp = uint32(attacker.GetEffect(input.EffectType.Damage, 0).Effect.Stack()) //红伤落实
defender.Damage(attacker, info.DamageZone{
Type: info.DamageType.Red,
Damage: decimal.NewFromInt(int64(attacker.DamageZone[0])),
})
}
//回合有先手方和后手方,同时有攻击方和被攻击方
@@ -526,7 +527,7 @@ func (f *FightC) enterturn(fattack, sattack BattleActionI) {
skill.Skill.Info.PP-- //减少PP
}
fmt.Println(i,
"玩家技能伤害:", attacker.GetEffect(input.EffectType.Damage, 0).Effect.Stack(),
"玩家技能伤害:", attacker.GetDamage(info.DamageType.Red),
"自身剩余血量:", attacker.CurrentPet.Info.Hp,
"对手剩余血量:", defender.CurrentPet.Info.Hp,
)

View File

@@ -26,12 +26,24 @@ var Category = enum.New[struct {
PHYSICAL EnumCategory `enum:"1"` // 物理攻击
SPECIAL EnumCategory `enum:"2"` // 特殊攻击
STATUS EnumCategory `enum:"4"` // 状态技能
Fixed EnumCategory // 固定伤害
Percent EnumCategory // 百分比伤害
True EnumCategory // 真伤
ALL EnumCategory //任何类型
//ALL EnumCategory //任何类型
}]()
type EnumDamageType int
var DamageType = enum.New[struct {
Red EnumDamageType
Fixed EnumDamageType // 固定伤害
Percent EnumDamageType // 百分比伤害
True EnumDamageType // 真伤
}]()
type DamageZone struct {
Type EnumDamageType
Damage decimal.Decimal
}
// SkillEntity 战斗技能实体
// 实现了战斗中技能的所有属性和行为包括PP管理、技能使用、属性获取等
// 战斗中可以修改技能实体值,比如是否暴击,是否必中等
@@ -138,18 +150,6 @@ func getSkillName(move *SkillEntity) string {
// return move.SideEffects
// }
type EnumsZoneType int
var DamageZone = enum.New[struct {
add EnumsZoneType // 加区
nul EnumsZoneType // 乘区
}]()
var DamageC = enum.New[struct {
boost EnumsZoneType //增伤区
reduction EnumsZoneType // 减伤区
}]()
// func (s *BattleSkillEntity) Random() *random.RandomXS128 {
// battle, _ := s.ctx.Value(BattleContainerCtx).(*Battle1V1)
@@ -238,4 +238,3 @@ func (a *SkillEntity) GetAccuracy(level int) uint32 {
IntPart(), // 转为int64
)
}

View File

@@ -31,9 +31,9 @@ var BattleMode = enum.New[struct {
}]()
var BattleStatus = enum.New[struct {
// 原ActionScript中的常量映射
FIGHT_WITH_NPC EnumBattleMode `enum:"0"` // 与NPC战斗
FIGHT_WITH_BOSS EnumBattleMode `enum:"1"` // 与BOSS战斗
FIGHT_WITH_PLAYER EnumBattleMode `enum:"2"` // 与玩家战斗PVP
FIGHT_WITH_NPC EnumBattleMode `enum:"3"` // 与NPC战斗
FIGHT_WITH_BOSS EnumBattleMode `enum:"2"` // 与BOSS战斗
FIGHT_WITH_PLAYER EnumBattleMode `enum:"1"` // 与玩家战斗PVP
}]()

View File

@@ -0,0 +1,52 @@
package info
// UpdatePropInfo 对应Java的UpdatePropInfo类用于精灵属性更新信息所有数值类型改为uint32
type UpdatePropInfo struct {
// CatchTime 精灵的捕获时间对应Java的@UInt long改为uint32
CatchTime uint32 `fieldDescription:"精灵的捕获时间" uint:"true" autoCodec:"true"`
// 精灵编号(@UInt long → uint32
ID uint32 `fieldDesc:"精灵编号" `
// Level 精灵等级对应Java的@UInt long改为uint32
Level uint32 `fieldDescription:"精灵等级" uint:"true" autoCodec:"true"`
// 当前等级已获得经验(@UInt long → uint32
Exp uint32 `fieldDesc:"当前等级已经获得的经验 2538" `
// 当前等级所需经验(@UInt long → uint32
LvExp uint32 `fieldDesc:"当前等级所需的经验" `
// 升到下一级的经验(@UInt long → uint32
NextLvExp uint32 `fieldDesc:"升到下一级的经验" `
// MaxHp 最大血量对应Java的@UInt long改为uint32
MaxHp uint32 `fieldDescription:"最大血量" uint:"true" autoCodec:"true"`
Prop [5]uint32 `fieldDesc:"属性" `
// 生命学习力(@UInt long → uint32
EvHp uint32 `fieldDesc:"生命学习力" `
// 攻击学习力(@UInt long → uint32
EvAttack uint32 `fieldDesc:"攻击学习力" `
// 防御学习力(@UInt long → uint32
EvDefence uint32 `fieldDesc:"防御学习力" `
// 特攻学习力(@UInt long → uint32
EvSpecialAttack uint32 `fieldDesc:"特攻学习力" `
// 特防学习力(@UInt long → uint32注意原Java拼写evSpecialDefense
EvSpecialDefense uint32 `fieldDesc:"特防学习力" `
// 速度学习力(@UInt long → uint32
EvSpeed uint32 `fieldDesc:"速度学习力" `
}
type PetUpdateOutboundInfo struct {
// Addition 超No加成,数值固定为20,若此字段为0则为普通经验分配
// 对应Java的@UInt long使用uint32参考历史转换习惯
Addition uint32 `fieldDescription:"超No加成,数值固定为20,若此字段为0则为普通经验分配" uint:"true" autoCodec:"true"`
DataLen uint32 `struc:"sizeof=Data"`
// Data 更新数据对应Java的List<UpdatePropInfo>
Data []UpdatePropInfo `fieldDescription:"更新数据" autoCodec:"true"`
}

View File

@@ -6,7 +6,6 @@ import (
"blazing/logic/service/fight/info"
"fmt"
"github.com/mohae/deepcopy"
"github.com/shopspring/decimal"
)
@@ -43,39 +42,44 @@ func (u *Input) BeforeSkill(opp *Input, skill *info.SkillEntity) {
}
// 伤害落实
func (u *Input) Damage(attacker *Input, id int) {
func (u *Input) Damage(attacker *Input, id info.DamageZone) {
attacker.Exec(func(t Effect) bool {
t.BeforeAttack(u, attacker.GetDamage(0)) //红伤落实前,我方增伤
t.BeforeAttack(u, &id) //红伤落实前,我方增伤
return true
})
attacker.Exec(func(t Effect) bool {
t.Attack(u, attacker.GetEffect(EffectType.Damage, id)) //红伤落实前,我方增伤
t.Attack(u, &id) //红伤落实前,我方增伤
return true
})
u.Exec(func(t Effect) bool {
t.BeforeAttacked(attacker, attacker.GetEffect(EffectType.Damage, id)) //红伤落实,内部有befer
t.BeforeAttacked(attacker, &id) //红伤落实,内部有befer
return true
})
u.Exec(func(t Effect) bool {
t.Attacked(attacker, attacker.GetEffect(EffectType.Damage, id)) //红伤落实,内部有befer
t.Attacked(attacker, &id) //红伤落实,内部有befer
return true
})
if uint32(attacker.GetEffect(EffectType.Damage, id).Effect.Stack()) > u.CurrentPet.Info.Hp {
switch id.Type {
case info.DamageType.Red:
attacker.AttackValue.LostHp = uint32(id.Damage.IntPart()) //红伤落实
}
if uint32(id.Damage.IntPart()) > u.CurrentPet.Info.Hp {
//这里其实是受到致死伤害
//然后先触发死亡效果消除所有buff
//然后触发回神效果
u.CurrentPet.Info.Hp = 0
} else {
u.CurrentPet.Info.Hp = u.CurrentPet.Info.Hp - u.AttackValue.LostHp
u.CurrentPet.Info.Hp = u.CurrentPet.Info.Hp - attacker.AttackValue.LostHp
}
//todo 待实现死亡effet
@@ -151,56 +155,6 @@ func (i *Input) GetAction(opp *Input) {
}
// todo获取属性待实现获取后改回原属性
func (i *Input) Prop(in *Input, f func()) {
oldourr := deepcopy.Copy(in).(*Input)
in.Exec(func(t Effect) bool { //属性获取后
t.AfterProp(i) //视为xx 需要在里面判断来源
return true
})
f()
in = oldourr
//opp.CurrentPet = oldouo //恢复
}
// todo获取属性待实现获取后改回原属性
func (i *Input) Pet(in *Input, f func()) {
oldour := deepcopy.Copy(i.CurrentPet).(*info.BattlePetEntity)
//oldouo := deepcopy.Copy(opp.CurrentPet).(*info.BattlePetEntity)
i.Exec(func(t Effect) bool { //属性获取前
t.BeferAttr(i.CurrentPet) //使XX为XX
return true
})
oldourr := deepcopy.Copy(in.CurrentPet).(*info.BattlePetEntity)
in.Exec(func(t Effect) bool { //属性获取后
t.AfterAttr(i.CurrentPet) //视为xx 需要在里面判断来源
return true
})
f()
in.CurrentPet = oldourr
i.CurrentPet = oldour //恢复
//opp.CurrentPet = oldouo //恢复
}
func (i *Input) Skill(in *info.SkillEntity, f func()) {
oldour := deepcopy.Copy(in).(*info.SkillEntity)
//oldouo := deepcopy.Copy(opp.CurrentPet).(*info.BattlePetEntity)
i.Exec(func(t Effect) bool { //属性获取前
t.BeforeSkill(i, in) //使XX为XX
return true
})
f()
in = oldour //恢复
}
// 计算技能威力
func (i *Input) CalculatePower(deftype *Input, skill *info.SkillEntity) decimal.Decimal {
@@ -216,23 +170,13 @@ func (i *Input) CalculatePower(deftype *Input, skill *info.SkillEntity) decimal.
switch skill.Category() { //判断技能类型
case info.Category.PHYSICAL:
i.Pet(i, func() { //我方取我方攻击
attackDec = decimal.NewFromInt(int64(i.GetProp(0, false)))
})
deftype.Pet(i, func() { //我方取敌方防御
defenseDec = decimal.NewFromInt(int64(deftype.GetProp(1, false)))
})
attackDec = decimal.NewFromInt(int64(i.GetProp(0, false)))
defenseDec = decimal.NewFromInt(int64(deftype.GetProp(1, false)))
case info.Category.SPECIAL:
i.Pet(i, func() { //我方取我方攻击
attackDec = decimal.NewFromInt(int64(i.GetProp(2, false)))
})
deftype.Pet(i, func() { //我方取敌方防御
defenseDec = decimal.NewFromInt(int64(deftype.GetProp(3, false)))
})
attackDec = decimal.NewFromInt(int64(i.GetProp(2, false)))
defenseDec = decimal.NewFromInt(int64(deftype.GetProp(3, false)))
default:
return decimal.NewFromInt(0)
@@ -247,13 +191,9 @@ func (i *Input) CalculatePower(deftype *Input, skill *info.SkillEntity) decimal.
Add(decimal.NewFromInt(2))
var typeRate decimal.Decimal
t, _ := element.NewElementCalculator().GetOffensiveMultiplier(skill.Type().ID, deftype.CurrentPet.Type().ID)
deftype.Pet(i, func() { //我方取敌方属性,得到敌方的实际和我方的视为
t, _ := element.NewElementCalculator().GetOffensiveMultiplier(skill.Type().ID, deftype.CurrentPet.Type().ID)
typeRate = decimal.NewFromFloat(t)
})
typeRate = decimal.NewFromFloat(t)
damage := baseDamage.
Mul(skill.CriticalsameTypeBonus()). // 同属性加成

View File

@@ -5,48 +5,43 @@ import (
)
type Effect interface {
OnBattleStart() bool //战斗开始
OnTurnStart(opp *Input) //回合开始
OnBattleStart() bool //战斗开始
OnTurnStart(opp *Input) //回合开始,注入特性
PreActionStart() bool //行动开始前,注入视为等参数在这里实现
CanSkill(opp *Input) bool //使用技能 可以取消用技能节点
PreSkill(opp *Input, skill *info.SkillEntity) //对技能修改,比如变威力
BeforeSkill(opp *Input, skill *info.SkillEntity) // 技能命中前触发
OnSkill(opp *Input, skill *info.SkillEntity) //闪避率计算,,实际上是修改命中的判断
// OnSkillPP() bool //技能PP减少节点
AfterProp(t *Input)
BeferProp(in *Input, prop, level int8, ptype info.EnumAbilityOpType) bool
AfterAttr(t *info.BattlePetEntity) //在获取属性前,比如重写对方属性AfterAttr
BeferAttr(t *info.BattlePetEntity) //在获取属性后,比如视为对方属性
BeferProp(in *Input, prop, level int8, ptype info.EnumAbilityOpType) bool //锁定属性
SetArgs(input *Input, param ...int)
BeforeAttack(opp *Input, id int) // 攻击前触发 ,这时候就是+区间
Attack(opp *Input, id int) // 攻击触发
BeforeAttacked(opp *Input, id int) //受击前触发 这时候就是百分比减伤区间
Attacked(opp *Input, id int) // 受击触发 这时候就是点数减伤
BeforeAttack(opp *Input, id *info.DamageZone) // 攻击前触发 ,这时候就是+区间
Attack(opp *Input, id *info.DamageZone) // 攻击触发
PreAttacked(opp *Input, skill *info.SkillEntity) //预处理受击技能
BeforeAttacked(opp *Input, id *info.DamageZone) //受击触发 这时候就是百分比减伤区间
Attacked(opp *Input, id *info.DamageZone) // 受击触发 这时候就是点数减伤
Shield() bool // 护盾值变化时触发
OnSwitchIn() bool // 精灵出战 / 上场时触发
OnSwitchOut() bool // 精灵下场时触发
OnOwnerSwitchIn() bool // 所属玩家精灵出战时触发
OnOwnerSwitchOut() bool // 所属玩家精灵下场时触发
OnActionEnd() bool //行动结束后
TurnEnd(opp *Input) //闪避率计算,,实际上是修改命中的判断
PreBattleEnd() bool //战斗结束前
OnBattleEnd() bool //战斗结束
// Shield() bool // 护盾值变化时触发
// PostDamage() bool // 伤害结算后触发(血量扣除后)
// AfterAttacked() bool // 被攻击后触发(受击判定)
// OnDefeat() bool // 精灵被击败时触发
TurnEnd(opp *Input) //闪避率计算,,实际上是修改命中的判断
// OnSkillPP() bool //技能PP减少节点
// // 治疗相关触发
// OnBeforeHeal() bool // 治疗前触发
// OnHeal() bool // 治疗生效时触发
// // 精灵切换相关触发
OnSwitchIn() bool // 精灵出战 / 上场时触发
OnSwitchOut() bool // 精灵下场时触发
OnOwnerSwitchIn() bool // 所属玩家精灵出战时触发
OnOwnerSwitchOut() bool // 所属玩家精灵下场时触发
// PreBattleEnd() bool //战斗结束前
// OnBattleEnd() bool //战斗结束
// OnDefeat() bool // 精灵被击败时触发
//回合数,然后次数另外维护
Duration(...int) int
Hit(...bool) bool

View File

@@ -21,14 +21,14 @@ type Input struct {
FightC common.FightI
// info.BattleActionI
Effects *utils.OrderedMap[int, Effect] //effects 实际上全局就是effect无限回合 //effects容器 技能的
DamageZone map[info.EnumCategory]int //伤害容器
DamageZone map[info.EnumDamageType]int //伤害容器
First bool //是否先手
}
func NewInput(c common.FightI, p common.PlayerI) *Input {
ret := &Input{FightC: c, Player: p}
ret.Effects = utils.NewOrderedMap[int, Effect]()
ret.DamageZone = make(map[info.EnumCategory]int)
ret.DamageZone = make(map[info.EnumDamageType]int)
// t := Geteffect(EffectType.Damage, 0)
// t.Effect.SetArgs(ret)
// ret.AddEffect(t) //添加默认基类,实现继承

View File

@@ -6,7 +6,7 @@ import (
"blazing/modules/blazing/model"
"reflect"
"github.com/mohae/deepcopy"
"github.com/barkimedes/go-deepcopy"
"github.com/tnnmigga/enum"
)
@@ -17,7 +17,6 @@ var EffectType = enum.New[struct {
Skill EnumEffectType `enum:"1000000"` //技能
//Prop EnumEffectType `enum:"2000000"` //属性
Status EnumEffectType `enum:"3000000"` //状态
}]()
var NodeM = make(map[int]Effect, 0)
@@ -32,10 +31,10 @@ func Geteffect(etype EnumEffectType, id int) *EffectID {
ret, ok := NodeM[id+int(etype)]
if ok {
//todo 获取前GetEffect
eff := deepcopy.Copy(ret).(Effect)
eff, _ := deepcopy.Anything(ret)
return &EffectID{
ID: id + int(etype),
Effect: eff,
Effect: eff.(Effect),
}
//todo 获取后GetEffect
}
@@ -67,7 +66,7 @@ func (c *Input) GetProp(id int, istue bool) int {
return realValue
}
func (c *Input) GetDamage(etype info.EnumCategory) int {
func (c *Input) GetDamage(etype info.EnumDamageType) int {
rer, ok := c.DamageZone[etype]
if ok {

View File

@@ -1,15 +0,0 @@
package node
// 先手选择前
func (this *EffectNode) BeforeSort() bool {
return true
}
func (this *EffectNode) OnSort() bool {
//todo 这里待实现对action先手的判断
return true
}

View File

@@ -17,12 +17,24 @@ func (e *EffectNode) OnTurnStart(opp *input.Input) {
}
func (e *EffectNode) OnActionEnd() bool {
//处理异常状态
return true
}
func (e *EffectNode) PreActionStart() bool {
//处理异常状态
return true
}
// 回合结束一次性effect清楚掉
func (e *EffectNode) TurnEnd(opp *input.Input) {
if e.duration == 0 { // 保留 (负数表示永久)
e.NotALive()
e.NotALive()
}
e.duration--

View File

@@ -5,18 +5,6 @@ import (
"blazing/logic/service/fight/input"
)
// 返回false阻止继续运行
// 回合开始
func (this *EffectNode) AfterAttr(t *info.BattlePetEntity) {
}
// 返回false阻止继续运行
// 回合开始
func (this *EffectNode) BeferAttr(t *info.BattlePetEntity) {
}
func (this *EffectNode) BeforeMultiHit() bool {
panic("not implemented") // TODO: Implement
}
@@ -49,9 +37,7 @@ func (this *EffectNode) PostDamage() bool {
func (this *EffectNode) OnDefeat() bool {
panic("not implemented") // TODO: Implement
}
func (this *EffectNode) AfterProp(t *input.Input) {
}
func (this *EffectNode) BeferProp(in *input.Input, prop, level int8, ptype info.EnumAbilityOpType) bool {
return true
}

View File

@@ -1,21 +1,22 @@
package node
import (
"blazing/logic/service/fight/info"
"blazing/logic/service/fight/input"
)
// 受击触发
func (this *EffectNode) Attack(opp *input.Input, eff *input.EffectID) {
func (this *EffectNode) Attack(*input.Input, *info.DamageZone) {
}
// 受击触发
func (this *EffectNode) BeforeAttack(opp *input.Input, eff *input.EffectID) {
func (this *EffectNode) BeforeAttack(*input.Input, *info.DamageZone) {
}
// 受击触发
func (this *EffectNode) Attacked(opp *input.Input, eff *input.EffectID) {
func (this *EffectNode) Attacked(*input.Input, *info.DamageZone) {
}
// 受击触发
func (this *EffectNode) BeforeAttacked(opp *input.Input, eff *input.EffectID) {
func (this *EffectNode) BeforeAttacked(*input.Input, *info.DamageZone) {
}

View File

@@ -7,10 +7,7 @@ import (
// 检查,激活,延后
// /基础节点
type EffectNode struct {
//Turn int // 当前回合数 ,回合数其实从战斗的上下文中获取
//本质上Ctx还要传入战斗双方数据来判断是否是本精灵切换
//Ctx context.Context //节点上下文
//次数相当于重写回合
Effect
duration int // 默认为-1 持续回合/次0 = 即时生效,>0 = 回合数 ,负数是永久) \
Input *input.Input

View File

@@ -19,7 +19,9 @@ func (e *EffectNode) CanSkill(opp *input.Input) bool {
func (e *EffectNode) PreSkill(opp *input.Input, skill *info.SkillEntity) {
}
func (e *EffectNode) PreAttacked(*input.Input, *info.SkillEntity) {
}
func (e *EffectNode) BeforeSkill(opp *input.Input, skill *info.SkillEntity) {
}
@@ -40,10 +42,7 @@ func (e *EffectNode) OnMiss(opp *input.Input, skill *info.SkillEntity) {
}
// 命中
func (e *EffectNode) OnHit(opp *input.Input, skill *info.SkillEntity) {
}
func (e *EffectNode) AfterSkill(opp *input.Input, skill *info.SkillEntity) {
type Effect interface {
OnMiss(opp *input.Input, skill *info.SkillEntity)
OnHit(opp *input.Input, skill *info.SkillEntity)
}

View File

@@ -30,3 +30,15 @@ type GoldOnlineRemainOutboundInfo struct {
// GoldNumber 金豆数量(后端返回实际数量需要*100
GoldNumber uint32 `json:"goldNumber" `
}
// ExpTotalRemainInboundInfo 累计经验相关的入站信息
type ExpTotalRemainInboundInfo struct {
// Head 消息头部信息cmd保持原标识如需调整可根据实际需求修改
Head player.TomeeHeader `cmd:"2319" struc:"[0]pad"`
}
// ExpTotalRemainOutboundInfo 累计经验相关的出站信息
type ExpTotalRemainOutboundInfo struct {
// TotalExp 累计经验值(根据实际业务需求确定是否需要倍率转换)
TotalExp uint32 `json:"totalExp" `
}

View File

@@ -54,9 +54,34 @@ type NonoOutboundInfo struct {
}
type NonoInboundInfo struct {
// 消息头部命令ID对应MessageCommandIDRegistry.Nono_Info
Head player.TomeeHeader `cmd:"9003" struc:"[0]pad"`
// 米米号
UserID uint32
}
type NonoFollowOrHomeInInfo struct {
Head player.TomeeHeader `cmd:"9019" struc:"[0]pad"`
// Flag 1为跟随 0为收回 且如果为收回收回 那么后续结构不需要发送
// 对应Java的@UInt long类型在Go中使用uint64
Flag uint32 `fieldDescription:"1为跟随 0为收回 且如果为收回 那么后续结构不需要发送" uint:"true"`
}
type NonoFollowOutInfo struct {
// UserID 米米号对应Java的@UInt long改为uint32
UserID uint32 `fieldDescription:"米米号" uint:"true"`
// SuperStage 设置的值与下面的flag变量相同改为uint32
SuperStage uint32 `fieldDescription:"设置的值与下面的flag变量相同" uint:"true"`
// Flag 1为跟随 0为收回不序列化改为uint32
Flag uint32 `fieldDescription:"1为跟随 0为收回 且如果为收回 那么后续结构不需要发送, 不序列化" uint:"true" serialize:"false"`
// NonoNick nono名字 补到16字节
NonoNick string `struc:"[16]byte"`
// Color nono颜色 rpg改为uint32
Color uint32 `fieldDescription:"nono颜色 rpg" uint:"true"`
// Power 能量值 前端显示除以1000后的值改为uint32
Power uint32 `fieldDescription:"能量值 前端显示除以1000后的值" uint:"true"`
}

19
logic/service/pet/exp.go Normal file
View File

@@ -0,0 +1,19 @@
package pet
import "blazing/logic/service/player"
// 实现InboundMessage接口用于处理宠物设置经验的入站消息
type PetSetExpInboundInfo struct {
Head player.TomeeHeader `cmd:"2318" struc:"[0]pad"`
// CatchTime 精灵获取时间对应Java的@UInt long
CatchTime uint32 `fieldDescription:"精灵获取时间" uint:"true" autoCodec:"true"`
// Exp 分配经验对应Java的@UInt long
Exp uint32 `fieldDescription:"分配经验" uint:"true" autoCodec:"true"`
}
// 实现OutboundMessage接口用于处理宠物设置经验的出站消息
type PetSetExpOutboundInfo struct {
// Exp 剩余累计经验对应Java的@UInt long
Exp uint32 `fieldDescription:"剩余累计经验" uint:"true" autoCodec:"true"`
}

View File

@@ -3,7 +3,6 @@ package player
import (
"blazing/logic/service/fight/info"
"blazing/modules/blazing/model"
"fmt"
)
type AI_player struct {
@@ -20,12 +19,12 @@ func (f *AI_player) SendPack(b []byte) error {
func (f *AI_player) SendReadyToFightInfo(gg info.FightStartOutboundInfo) {
fmt.Println(gg)
//fmt.Println(gg)
}
func (f *AI_player) SendNoteReadyToFightInfo(fs info.NoteReadyToFightInfo) {
fmt.Println(fs)
// fmt.Println(fs)
}
func (f *AI_player) SendFightEndInfo(_ info.FightOverInfo) {

View File

@@ -2,8 +2,12 @@ package player
import (
"blazing/common/data/xmlres"
"blazing/logic/service/fight/info"
"blazing/modules/blazing/model"
"math"
"github.com/jinzhu/copier"
)
// calculateExperience 计算指定等级和种族值所需的经验值
@@ -27,62 +31,70 @@ func calculateExperience(level uint32, baseValue uint32) uint32 {
// 主函数实现
// 添加经验
// 超NO 加成
func (p *Player) AddPetExp(pet *model.PetInfo, addExp uint32) {
func (p *Player) AddPetExp(petinfo *model.PetInfo, addExp uint32) {
originalLevel := pet.Level
originalLevel := petinfo.Level
for {
basic := xmlres.PetMAP[int(pet.ID)]
basic := xmlres.PetMAP[int(petinfo.ID)]
ba := basic.Atk +
basic.Def +
basic.SpAtk +
basic.SpDef +
basic.Spd +
uint32(basic.HP)
needExp := calculateExperience(pet.Level, ba)
needExp := calculateExperience(petinfo.Level, ba)
if addExp >= needExp {
addExp -= needExp
pet.Level++
petinfo.Level++
// 检查是否可以进化
if basic.EvolvesTo != 0 || // 有明确的进化
basic.EvolvingLv >= int(pet.Level) || // 有明确的进化等级
if basic.EvolvesTo != 0 && // 有明确的进化
int(petinfo.Level) >= basic.EvolvingLv && // 有明确的进化等级
basic.IsLarge == 0 { // 非最终形态
pet.ID = uint32(basic.EvolvesTo)
petinfo.ID = uint32(basic.EvolvesTo)
}
} else {
pet.NextLvExp = calculateExperience(pet.Level, ba)
petinfo.NextLvExp = calculateExperience(petinfo.Level, ba)
break
}
}
pet.Exp = addExp
petinfo.Exp = addExp
pet.LvExp = pet.NextLvExp - pet.Exp
petinfo.LvExp = petinfo.NextLvExp - petinfo.Exp
// 处理进化逻辑
// 重新计算面板
if originalLevel != pet.Level {
pet.CalculatePetPane()
if originalLevel != petinfo.Level {
petinfo.CalculatePetPane()
}
t1 := NewTomeeHeader(2508, p.Info.UserID)
rrr := &info.PetUpdateOutboundInfo{}
var petinfwo info.UpdatePropInfo
copier.Copy(&petinfwo, petinfo)
rrr.Data = append(rrr.Data, petinfwo)
p.SendPack(t1.Pack(rrr)) //准备包由各自发,因为协议不一样
// 发送经验更新消息
//player.SendMessage(generatePetUpdateInfo(petEntity, originalExp+addExp-exp, addition))
// 处理技能学习
canLearnSkillList := model.LastFourElements(pet.GetLevelRangeCanLearningSkills(originalLevel, pet.Level)) //获取最后四个技能,如果不足,那就取全部技能
canLearnSkillList := model.LastFourElements(petinfo.GetLevelRangeCanLearningSkills(originalLevel, petinfo.Level)) //获取最后四个技能,如果不足,那就取全部技能
for i := 0; i < 4; i++ {
if pet.SkillList[i].ID == 0 {
if petinfo.SkillList[i].ID == 0 {
if len(canLearnSkillList) != 0 {
skid := canLearnSkillList[len(canLearnSkillList)-1]
pet.SkillList[i].ID = skid
pet.SkillList[i].PP = uint32(xmlres.SkillMap[int(skid)].MaxPP)
petinfo.SkillList[i].ID = skid
petinfo.SkillList[i].PP = uint32(xmlres.SkillMap[int(skid)].MaxPP)
petinfo.SkillListLen += 1
canLearnSkillList = canLearnSkillList[:len(canLearnSkillList)-1]
}

View File

@@ -236,7 +236,7 @@ func (p *Player) ItemAdd(t []model.SingleItemInfo) {
case 1: //塞尔豆
p.Info.Coins = p.Info.Coins + v.ItemCnt
case 3: //累计经验
p.Info.ExpPool = p.Info.ExpPool + int64(v.ItemCnt)
p.Info.ExpPool = p.Info.ExpPool + v.ItemCnt
default:
ttt = append(ttt, v)
}

View File

@@ -49,9 +49,9 @@ func NewPlayerInfo() *PlayerInfo {
}
type PlayerInfo struct {
GoldBean int32 `struc:"skip" json:"nieo_gold_bean"` // 金豆(特殊货币)
GoldBean uint32 `struc:"skip" json:"nieo_gold_bean"` // 金豆(特殊货币)
ExpPool int64 `struc:"skip" json:"exp_pool"` // 累计经验池
ExpPool uint32 `struc:"skip" json:"exp_pool"` // 累计经验池
LastResetTime time.Time `struc:"skip" json:"last_reset_time"` // 重置时间,比如电池和每日任务
// OutInfo 字段
UserID uint32 `struc:"uint32" json:"user_id"` // 米米号 通过sid拿到