refactor: 重构战斗结构体以支持双打模式

This commit is contained in:
xinian
2026-04-04 22:13:42 +08:00
committed by cnb
parent 7916f90992
commit 39e1d4c42f
16 changed files with 543 additions and 246 deletions

View File

@@ -280,15 +280,10 @@ func (f *FightC) buildFightStartInfo() info.FightStartOutboundInfo {
startInfo := info.FightStartOutboundInfo{}
ourInfos := f.collectFightPetInfos(f.Our)
oppInfos := f.collectFightPetInfos(f.Opp)
startInfo.Infos = append(startInfo.Infos, ourInfos...)
startInfo.Infos = append(startInfo.Infos, oppInfos...)
startInfo.InfoLen = uint32(len(startInfo.Infos))
if len(ourInfos) > 0 {
startInfo.Info1 = ourInfos[0]
}
if len(oppInfos) > 0 {
startInfo.Info2 = oppInfos[0]
}
startInfo.Info1 = append(startInfo.Info1, ourInfos...)
startInfo.Info2 = append(startInfo.Info2, oppInfos...)
startInfo.Info1Len = uint32(len(startInfo.Info1))
startInfo.Info2Len = uint32(len(startInfo.Info2))
return startInfo
}

View File

@@ -63,11 +63,11 @@ func (e *Effect1263) Skill_Use() bool {
}
divisor := e.Args()[0]
if e.CarrierInput().CurPet[0].GetHP().Cmp(e.OpponentInput().CurPet[0].GetHP()) < 0 {
if e.CarrierPet().GetHP().Cmp(e.OpponentPet().GetHP()) < 0 {
divisor = e.Args()[1]
}
damage := e.OpponentInput().CurPet[0].GetMaxHP().Div(divisor)
damage := e.OpponentPet().GetMaxHP().Div(divisor)
if damage.Cmp(alpacadecimal.Zero) <= 0 {
return true
}
@@ -208,7 +208,7 @@ type Effect1268 struct {
}
func (e *Effect1268) Skill_Use() bool {
if len(e.Args()) == 0 || e.OpponentInput().CurPet[0].Info.Hp > 0 {
if len(e.Args()) == 0 || e.OpponentPet().Info.Hp > 0 {
return true
}
effect := e.CarrierInput().InitEffect(input.EffectType.Sub, 1268, int(e.Args()[0].IntPart()))
@@ -246,7 +246,7 @@ func (e *Effect1269) SkillHit_ex() bool {
return true
}
damage := e.OpponentInput().CurPet[0].GetMaxHP().Div(e.Args()[1])
damage := e.OpponentPet().GetMaxHP().Div(e.Args()[1])
if damage.Cmp(alpacadecimal.Zero) <= 0 {
return true
}
@@ -383,10 +383,10 @@ func (e *Effect1273) OnSkill() bool {
if len(e.Args()) == 0 || e.Args()[0].Cmp(alpacadecimal.Zero) <= 0 {
return true
}
if e.CarrierInput().CurPet[0].GetHP().Cmp(e.CarrierInput().CurPet[0].GetMaxHP()) != 0 {
if e.CarrierPet().GetHP().Cmp(e.CarrierPet().GetMaxHP()) != 0 {
return true
}
damage := e.CarrierInput().CurPet[0].GetMaxHP().Div(e.Args()[0])
damage := e.CarrierPet().GetMaxHP().Div(e.Args()[0])
if damage.Cmp(alpacadecimal.Zero) <= 0 {
return true
}
@@ -423,7 +423,7 @@ func (e *Effect1275) Skill_Use() bool {
if len(e.Args()) == 0 {
return true
}
e.CarrierInput().Damage(e.CarrierInput(), &info.DamageZone{Type: info.DamageType.Fixed, Damage: e.CarrierInput().CurPet[0].GetHP()})
e.CarrierInput().Damage(e.CarrierInput(), &info.DamageZone{Type: info.DamageType.Fixed, Damage: e.CarrierPet().GetHP()})
effect := e.CarrierInput().InitEffect(input.EffectType.Sub, 1275, int(e.Args()[0].IntPart()))
if effect != nil {
e.CarrierInput().AddEffect(e.CarrierInput(), effect)
@@ -468,8 +468,8 @@ func (e *Effect1276) Skill_Use() bool {
if len(e.Args()) == 0 || e.Args()[0].Cmp(alpacadecimal.Zero) <= 0 {
return true
}
damage := e.CarrierInput().CurPet[0].GetHP().Div(e.Args()[0])
e.CarrierInput().Damage(e.CarrierInput(), &info.DamageZone{Type: info.DamageType.Fixed, Damage: e.CarrierInput().CurPet[0].GetHP()})
damage := e.CarrierPet().GetHP().Div(e.Args()[0])
e.CarrierInput().Damage(e.CarrierInput(), &info.DamageZone{Type: info.DamageType.Fixed, Damage: e.CarrierPet().GetHP()})
if damage.Cmp(alpacadecimal.Zero) > 0 {
e.OpponentInput().Damage(e.CarrierInput(), &info.DamageZone{Type: info.DamageType.Percent, Damage: damage})
}
@@ -482,16 +482,16 @@ type Effect1277 struct {
}
func (e *Effect1277) Skill_Use() bool {
if e.CarrierInput().CurPet[0].GetHP().Cmp(e.OpponentInput().CurPet[0].GetHP()) > 0 {
if e.CarrierPet().GetHP().Cmp(e.OpponentPet().GetHP()) > 0 {
leave := alpacadecimal.NewFromInt(1)
if e.CarrierInput().CurPet[0].GetHP().Cmp(leave) > 0 {
e.CarrierInput().Damage(e.CarrierInput(), &info.DamageZone{Type: info.DamageType.Fixed, Damage: e.CarrierInput().CurPet[0].GetHP().Sub(leave)})
if e.CarrierPet().GetHP().Cmp(leave) > 0 {
e.CarrierInput().Damage(e.CarrierInput(), &info.DamageZone{Type: info.DamageType.Fixed, Damage: e.CarrierPet().GetHP().Sub(leave)})
}
e.OpponentInput().Damage(e.CarrierInput(), &info.DamageZone{Type: info.DamageType.Fixed, Damage: alpacadecimal.NewFromInt(int64(grand.N(350, 550)))})
return true
}
lost := e.CarrierInput().CurPet[0].GetHP()
lost := e.CarrierPet().GetHP()
e.CarrierInput().Damage(e.CarrierInput(), &info.DamageZone{Type: info.DamageType.Fixed, Damage: lost})
e.OpponentInput().Damage(e.CarrierInput(), &info.DamageZone{Type: info.DamageType.Fixed, Damage: lost})
applyStatusByID(e.CarrierInput(), e.OpponentInput(), int(info.PetStatus.Paralysis))
@@ -614,7 +614,7 @@ func (e *Effect1280) OnSkill() bool {
if percent > max {
percent = max
}
damage := e.CarrierInput().CurPet[0].GetHP().Mul(alpacadecimal.NewFromInt(int64(percent))).Div(alpacadecimal.NewFromInt(100))
damage := e.CarrierPet().GetHP().Mul(alpacadecimal.NewFromInt(int64(percent))).Div(alpacadecimal.NewFromInt(100))
if damage.Cmp(alpacadecimal.Zero) > 0 {
e.OpponentInput().Damage(e.CarrierInput(), &info.DamageZone{Type: info.DamageType.Percent, Damage: damage})
}
@@ -634,12 +634,12 @@ func (e *Effect1281) Skill_Use() bool {
}
e.OpponentInput().Damage(e.CarrierInput(), &info.DamageZone{Type: info.DamageType.Fixed, Damage: shield})
if shield.Cmp(alpacadecimal.NewFromInt(300)) > 0 {
dmg := e.CarrierInput().CurPet[0].GetMaxHP().Div(alpacadecimal.NewFromInt(3))
dmg := e.CarrierPet().GetMaxHP().Div(alpacadecimal.NewFromInt(3))
e.OpponentInput().Damage(e.CarrierInput(), &info.DamageZone{Type: info.DamageType.Percent, Damage: dmg})
return true
}
if shield.Cmp(alpacadecimal.NewFromInt(300)) < 0 {
val := e.CarrierInput().CurPet[0].GetMaxHP().Div(alpacadecimal.NewFromInt(3))
val := e.CarrierPet().GetMaxHP().Div(alpacadecimal.NewFromInt(3))
e.CarrierInput().Heal(e.CarrierInput(), &action.SelectSkillAction{}, val)
e.OpponentInput().Damage(e.CarrierInput(), &info.DamageZone{Type: info.DamageType.Percent, Damage: val})
}
@@ -729,7 +729,7 @@ type Effect1285 struct {
}
func (e *Effect1285) Skill_Use() bool {
if len(e.Args()) == 0 || e.OpponentInput().CurPet[0].Info.Hp == 0 {
if len(e.Args()) == 0 || e.OpponentPet().Info.Hp == 0 {
return true
}
zeroRandomSkillPP(e.OpponentInput(), int(e.Args()[0].IntPart()))

View File

@@ -68,26 +68,33 @@ func (e *Effect201) OnSkill() bool {
healAll := len(args) > 1 && args[0].IntPart() != 0
if !healAll {
e.Ctx().Our.Heal(
e.Ctx().Our,
target := e.TargetInput()
if target == nil {
target = e.Ctx().Our
}
current := target.CurrentPet()
if current == nil {
return true
}
target.Heal(
e.CarrierInput(),
&action.SelectSkillAction{},
e.Ctx().Our.CurPet[0].GetMaxHP().Div(divisor),
current.GetMaxHP().Div(divisor),
)
return true
}
for _, pet := range e.Ctx().Our.AllPet {
if pet == nil || !pet.Alive() {
team := e.Ctx().Our.Team
if len(team) == 0 {
team = []*input.Input{e.Ctx().Our}
}
for _, ally := range team {
if ally == nil {
continue
}
healAmount := pet.GetMaxHP().Div(divisor)
if pet == e.Ctx().Our.CurPet[0] {
e.Ctx().Our.Heal(e.Ctx().Our, &action.SelectSkillAction{}, healAmount)
continue
if current := ally.CurrentPet(); current != nil && current.Alive() {
ally.Heal(e.CarrierInput(), &action.SelectSkillAction{}, current.GetMaxHP().Div(divisor))
}
pet.Info.ModelHP(healAmount.IntPart())
}
return true

View File

@@ -35,9 +35,14 @@ func (f *FightC) processSkillAttack(attacker, defender *input.Input, skill *info
})
var originalProps [2][6]int8
var originalPetInfo [2]model.PetInfo
attackerPet := attacker.CurrentPet()
defenderPet := defender.CurrentPet()
if attackerPet == nil || defenderPet == nil {
return
}
//复制属性
originalProps[0], originalProps[1] = attacker.Prop, defender.Prop
originalPetInfo[0], originalPetInfo[1] = attacker.CurPet[0].Info, defender.CurPet[0].Info
originalPetInfo[0], originalPetInfo[1] = attackerPet.Info, defenderPet.Info
attacker.ExecWithOpponent(defender, func(effect input.Effect) bool {
//计算变威力
effect.Ctx().SkillEntity = skill
@@ -59,7 +64,7 @@ func (f *FightC) processSkillAttack(attacker, defender *input.Input, skill *info
//还原属性
attacker.Prop, defender.Prop = originalProps[0], originalProps[1]
attacker.CurPet[0].Info, defender.CurPet[0].Info = originalPetInfo[0], originalPetInfo[1]
attackerPet.Info, defenderPet.Info = originalPetInfo[0], originalPetInfo[1]
if attacker.IsCritical == 1 { //命中了才有暴击
//暴击破防
@@ -130,6 +135,28 @@ func (f *FightC) getSkillParticipants(skillAction *action.SelectSkillAction) (*i
return f.GetInputByAction(skillAction, false), f.GetInputByAction(skillAction, true)
}
func (f *FightC) collectAttackValues(inputs []*input.Input) []model.AttackValue {
values := make([]model.AttackValue, 0, len(inputs))
for actorIndex, fighter := range inputs {
if fighter == nil || fighter.AttackValue == nil {
continue
}
attackValue := *fighter.AttackValue
attackValue.ActorIndex = uint32(actorIndex)
values = append(values, attackValue)
}
return values
}
func (f *FightC) buildNoteUseSkillOutboundInfo() info.NoteUseSkillOutboundInfo {
result := info.NoteUseSkillOutboundInfo{}
result.FirstAttackInfo = append(result.FirstAttackInfo, f.collectAttackValues(f.Our)...)
result.SecondAttackInfo = append(result.SecondAttackInfo, f.collectAttackValues(f.Opp)...)
result.FirstAttackInfoLen = uint32(len(result.FirstAttackInfo))
result.SecondAttackInfoLen = uint32(len(result.SecondAttackInfo))
return result
}
// enterturn 处理战斗回合逻辑
// 回合有先手方和后手方,同时有攻击方和被攻击方
func (f *FightC) enterturn(firstAttack, secondAttack *action.SelectSkillAction) {
@@ -235,16 +262,18 @@ func (f *FightC) enterturn(firstAttack, secondAttack *action.SelectSkillAction)
}
currentSkill = originalSkill
defender.ExecWithOpponent(attacker, func(effect input.Effect) bool { //这个是能否使用技能
effect.Ctx().SkillEntity = currentSkill
return effect.ActionStartEx(firstAttack, secondAttack)
})
canUseSkill := attacker.ExecWithOpponent(defender, func(effect input.Effect) bool { //这个是能否使用技能
effect.Ctx().SkillEntity = currentSkill
return effect.ActionStart(firstAttack, secondAttack)
})
defender.ExecWithOpponent(attacker, func(effect input.Effect) bool { //这个是能否使用技能
effect.Ctx().SkillEntity = currentSkill
return effect.ActionStartEx(firstAttack, secondAttack)
})
canUseSkill := attacker.ExecWithOpponent(defender, func(effect input.Effect) bool { //这个是能否使用技能
effect.Ctx().SkillEntity = currentSkill
return effect.ActionStart(firstAttack, secondAttack)
})
canUse := canUseSkill && action.CanUse(currentSkill) && attacker != nil && attacker.CurPet[0].Info.Hp > 0
attackerPet := attacker.CurrentPet()
defenderPet := defender.CurrentPet()
canUse := canUseSkill && action.CanUse(currentSkill) && attacker != nil && attackerPet != nil && attackerPet.Info.Hp > 0
if !canUse {
@@ -268,7 +297,7 @@ func (f *FightC) enterturn(firstAttack, secondAttack *action.SelectSkillAction)
f.processSkillAttack(attacker, defender, currentSkill)
currentSkill = originalSkill //还原技能
_, skill, ok := utils.FindWithIndex(attacker.CurPet[0].Info.SkillList, func(item model.SkillInfo) bool {
_, skill, ok := utils.FindWithIndex(attackerPet.Info.SkillList, func(item model.SkillInfo) bool {
return item.ID == currentSkill.Info.ID
})
if ok {
@@ -281,7 +310,7 @@ func (f *FightC) enterturn(firstAttack, secondAttack *action.SelectSkillAction)
skill.Use(usecount)
}
}
if defender.CurPet[0].Info.Hp > 0 {
if defenderPet != nil && defenderPet.Info.Hp > 0 {
//技能使用后
defender.ExecWithOpponent(attacker, func(effect input.Effect) bool { //技能使用后的我方效果
effect.Ctx().SkillEntity = currentSkill
@@ -290,7 +319,7 @@ func (f *FightC) enterturn(firstAttack, secondAttack *action.SelectSkillAction)
})
}
if attacker.CurPet[0].Info.Hp > 0 {
if attackerPet != nil && attackerPet.Info.Hp > 0 {
//技能使用后
attacker.ExecWithOpponent(defender, func(effect input.Effect) bool { //技能使用后的我方效果
effect.Ctx().SkillEntity = currentSkill
@@ -311,16 +340,16 @@ func (f *FightC) enterturn(firstAttack, secondAttack *action.SelectSkillAction)
return true
})
if defender.CurPet[0].Info.Hp <= 0 && attacker.CurPet[0].Info.Hp <= 0 { //先手方死亡,触发反同归于尽
attacker.CurPet[0].Info.Hp = 1
if defenderPet != nil && attackerPet != nil && defenderPet.Info.Hp <= 0 && attackerPet.Info.Hp <= 0 { //先手方死亡,触发反同归于尽
attackerPet.Info.Hp = 1
}
if defender.CurPet[0].Info.Hp <= 0 {
if defenderPet != nil && defenderPet.Info.Hp <= 0 {
f.TURNOVER(defender)
break
}
if attacker.CurPet[0].Info.Hp <= 0 {
if attackerPet != nil && attackerPet.Info.Hp <= 0 {
f.TURNOVER(attacker)
break
@@ -343,10 +372,7 @@ func (f *FightC) enterturn(firstAttack, secondAttack *action.SelectSkillAction)
}
attackValueResult := info.AttackValueS{
FAttack: *f.First.AttackValue,
SAttack: *f.Second.AttackValue,
}
attackValueResult := f.buildNoteUseSkillOutboundInfo()
//因为切完才能广播,所以必须和回合结束分开结算
f.Broadcast(func(fighter *input.Input) {
for _, switchAction := range f.Switch {

View File

@@ -103,10 +103,6 @@ type FightPetInfo struct {
//能力提升属性
Prop [6]int8
}
type AttackValueS struct {
FAttack model.AttackValue
SAttack model.AttackValue
}
func NewAttackValue(userid uint32) *model.AttackValue {
return &model.AttackValue{
@@ -200,21 +196,22 @@ type PropDict struct {
// NoteUseSkillOutboundInfo 战斗技能使用通知的出站信息结构体
type NoteUseSkillOutboundInfo struct {
FirstAttackInfo model.AttackValue // 本轮先手的精灵在释放技能结束后的状态
SecondAttackInfo model.AttackValue // 本轮后手的精灵在释放技能结束后的状态
FirstAttackInfoLen uint32 `struc:"sizeof=FirstAttackInfo"`
FirstAttackInfo []model.AttackValue // 本轮先手方精灵在释放技能结束后的状态
SecondAttackInfoLen uint32 `struc:"sizeof=SecondAttackInfo"`
SecondAttackInfo []model.AttackValue // 本轮后手方精灵在释放技能结束后的状态
}
type FightStartOutboundInfo struct {
IsCanAuto uint32 `fieldDesc:"是否自动 默认给0 怀疑是自动战斗器使用的" `
InfoLen uint32 `struc:"sizeof=Infos"`
Infos []FightPetInfo
Info1Len uint32 `struc:"sizeof=Info1"`
// 我方当前战斗精灵信息前端通过userid判断是否为我方
Info1 []FightPetInfo
// 当前战斗精灵信息1前端通过userid判断是否为我方
Info1 FightPetInfo `fieldDesc:"当前战斗精灵信息 可能不准.看前端代码是以userid判断哪个结构体是我方的" serialize:"struct"`
// 当前战斗精灵信息2前端通过userid判断是否为我方
Info2 FightPetInfo `fieldDesc:"当前战斗精灵的信息 可能不准.看前端代码是以userid来判断哪个结构体是我方的" serialize:"struct"`
Info2Len uint32 `struc:"sizeof=Info2"`
// 对方当前战斗精灵信息(前端通过userid判断是否为我方)
Info2 []FightPetInfo
}
type S2C_2404 struct {

View File

@@ -162,6 +162,23 @@ func (f *FightC) linkOppInputs() {
}
}
func (f *FightC) linkTeamViews() {
for _, fighter := range f.Our {
if fighter == nil {
continue
}
fighter.Team = f.Our
fighter.OppTeam = f.Opp
}
for _, fighter := range f.Opp {
if fighter == nil {
continue
}
fighter.Team = f.Opp
fighter.OppTeam = f.Our
}
}
func (f *FightC) getSideInputs(userID uint32, isOpposite bool) []*input.Input {
isOur := f.isOurPlayerID(userID)
if isOpposite {
@@ -177,36 +194,48 @@ func (f *FightC) getSideInputs(userID uint32, isOpposite bool) []*input.Input {
}
func (f *FightC) findInputByUserID(userID uint32) (*input.Input, bool) {
isOur := f.isOurPlayerID(userID)
if isOur {
if in := f.selectInput(f.Our, 0); in != nil {
for _, in := range f.Our {
if in != nil && in.ControlledBy(userID) {
return in, true
}
return nil, true
}
if in := f.selectInput(f.Opp, 0); in != nil {
return in, false
for _, in := range f.Opp {
if in != nil && in.ControlledBy(userID) {
return in, false
}
}
return nil, false
}
func (f *FightC) getInputByUserID(userID uint32, index int, isOpposite bool) *input.Input {
return f.selectInput(f.getSideInputs(userID, isOpposite), index)
selected := f.selectInput(f.getSideInputs(userID, isOpposite), index)
if selected == nil {
return nil
}
// 操作自身站位时,必须由该站位控制者发起。
if !isOpposite && !selected.ControlledBy(userID) {
return nil
}
return selected
}
func (f *FightC) getInputByController(userID uint32, isOpposite bool) *input.Input {
sideInputs := f.getSideInputs(userID, isOpposite)
for _, in := range sideInputs {
if in != nil && in.ControlledBy(userID) {
return in
}
}
return f.selectInput(sideInputs, 0)
}
func (f *FightC) expectedActionSlots() map[actionSlotKey]struct{} {
slots := make(map[actionSlotKey]struct{}, len(f.Our)+len(f.Opp))
for actorIndex, fighter := range f.Our {
if fighter == nil || fighter.Player == nil {
continue
}
slots[newActionSlotKey(fighter.Player.GetInfo().UserID, actorIndex)] = struct{}{}
for _, slot := range f.SideSlots(SideOur) {
slots[newActionSlotKey(slot.ControllerUserID, slot.SlotIndex)] = struct{}{}
}
for actorIndex, fighter := range f.Opp {
if fighter == nil || fighter.Player == nil {
continue
}
slots[newActionSlotKey(fighter.Player.GetInfo().UserID, actorIndex)] = struct{}{}
for _, slot := range f.SideSlots(SideOpp) {
slots[newActionSlotKey(slot.ControllerUserID, slot.SlotIndex)] = struct{}{}
}
return slots
}
@@ -234,7 +263,7 @@ func (f *FightC) GetInputByPlayer(c common.PlayerI, isOpposite bool) *input.Inpu
}
return f.primaryOur()
}
return f.getInputByUserID(c.GetInfo().UserID, 0, isOpposite)
return f.getInputByController(c.GetInfo().UserID, isOpposite)
}
func (f *FightC) GetInputByAction(c action.BattleActionI, isOpposite bool) *input.Input {
@@ -266,6 +295,14 @@ func (f *FightC) GetCurrPETAt(c common.PlayerI, actorIndex int) *info.BattlePetE
}
return in.PrimaryCurPet()
}
func (f *FightC) GetCurrPETByAction(c action.BattleActionI, isOpposite bool) *info.BattlePetEntity {
in := f.GetInputByAction(c, isOpposite)
if in == nil {
return nil
}
return in.CurrentPet()
}
func (f *FightC) GetOpp(c common.PlayerI) *input.Input {
return f.GetInputByPlayer(c, true)
}

View File

@@ -58,8 +58,12 @@ func (our *Input) GetAction() {
// 计算技能对对方的伤害假设CalculatePower返回伤害值或需从技能中获取
s.DamageValue = our.CalculatePower(our.Opp, s)
oppPet := our.Opp.CurrentPet()
if oppPet == nil {
continue
}
// 判断是否能秒杀(伤害 >= 对方当前生命值)
if s.DamageValue.Cmp(our.Opp.CurPet[0].GetHP()) != -1 { // 假设oppPet.HP为对方当前剩余生命值
if s.DamageValue.Cmp(oppPet.GetHP()) != -1 { // 假设oppPet.HP为对方当前剩余生命值
if usedskill != nil {
if s.DamageValue.Cmp(usedskill.DamageValue) != -1 {

View File

@@ -91,9 +91,13 @@ func (our *Input) InitEffect(etype EnumEffectType, id int, a ...int) Effect {
// * battle_lv: atk(0), def(1), sp_atk(2), sp_def(3), spd(4), accuracy(5)
// 是否需要真实提升
func (our *Input) GetProp(id int) alpacadecimal.Decimal {
currentPet := our.CurrentPet()
if currentPet == nil {
return alpacadecimal.Zero
}
// 计算实际值(这里可以插入后续优化的函数调用)
realValue := info.CalculateRealValue(alpacadecimal.NewFromInt(int64(our.CurPet[0].Info.Prop[id])), our.AttackValue.Prop[id])
realValue := info.CalculateRealValue(alpacadecimal.NewFromInt(int64(currentPet.Info.Prop[id])), our.AttackValue.Prop[id])
// todo: 插入获取后处理函数,例如:
// realValue = postProcessValue(realValue, id, c)

View File

@@ -14,6 +14,11 @@ import (
// 计算暴击
func (our *Input) CalculateCrit(opp *Input, skill *info.SkillEntity) {
ourPet := our.CurrentPet()
oppPet := opp.CurrentPet()
if ourPet == nil || oppPet == nil {
return
}
skill.Crit = 0
if skill.Category() == info.Category.STATUS { //属性技能不用算暴击
@@ -30,11 +35,11 @@ func (our *Input) CalculateCrit(opp *Input, skill *info.SkillEntity) {
CritRate = 16
}
// CritSelfHalfHp: 自身体力低于一半时必定致命一击; 默认: 0
if skill.XML.CritSelfHalfHp != 0 && (our.CurPet[0].HP < int(our.CurPet[0].Info.MaxHp)/2) {
if skill.XML.CritSelfHalfHp != 0 && (ourPet.HP < int(ourPet.Info.MaxHp)/2) {
CritRate = 16
}
// CritFoeHalfHp: 对方体力低于一半时必定致命一击; 默认: 0
if skill.XML.CritSelfHalfHp != 0 && (opp.CurPet[0].HP < int(opp.CurPet[0].Info.MaxHp)/2) {
if skill.XML.CritSelfHalfHp != 0 && (oppPet.HP < int(oppPet.Info.MaxHp)/2) {
CritRate = 16
}
@@ -47,6 +52,10 @@ func (our *Input) CalculateCrit(opp *Input, skill *info.SkillEntity) {
// 恢复血量
func (our *Input) Heal(in *Input, ac action.BattleActionI, value alpacadecimal.Decimal) {
currentPet := our.CurrentPet()
if currentPet == nil {
return
}
healValue := int(value.IntPart())
if ac != nil {
@@ -67,30 +76,38 @@ func (our *Input) Heal(in *Input, ac action.BattleActionI, value alpacadecimal.D
}
if healValue >= 0 {
our.CurPet[0].Info.ModelHP(int64(healValue))
currentPet.Info.ModelHP(int64(healValue))
return
}
damage := uint32(-healValue)
if damage >= our.CurPet[0].Info.Hp {
our.CurPet[0].Info.Hp = 0
if damage >= currentPet.Info.Hp {
currentPet.Info.Hp = 0
return
}
our.CurPet[0].Info.Hp -= damage
currentPet.Info.Hp -= damage
}
func (our *Input) HealPP(value int) {
currentPet := our.CurrentPet()
if currentPet == nil {
return
}
our.CurPet[0].Info.HealPP(value)
currentPet.Info.HealPP(value)
}
func (our *Input) DelPP(value int) {
currentPet := our.CurrentPet()
if currentPet == nil {
return
}
for i := 0; i < len(our.CurPet[0].Info.SkillList); i++ {
if uint32(value) > our.CurPet[0].Info.SkillList[i].PP {
our.CurPet[0].Info.SkillList[i].PP = 0
for i := 0; i < len(currentPet.Info.SkillList); i++ {
if uint32(value) > currentPet.Info.SkillList[i].PP {
currentPet.Info.SkillList[i].PP = 0
} else {
our.CurPet[0].Info.SkillList[i].PP -= uint32(value)
currentPet.Info.SkillList[i].PP -= uint32(value)
}
}
@@ -102,6 +119,10 @@ func (our *Input) DelPP(value int) {
// 这个方法是对对方造成伤害
// 伤害落实 // 血量扣减节点比如触发回神,反弹也在这里实现
func (our *Input) Damage(in *Input, sub *info.DamageZone) {
currentPet := our.CurrentPet()
if currentPet == nil {
return
}
attacker := in
if attacker == nil {
attacker = our
@@ -200,11 +221,11 @@ func (our *Input) Damage(in *Input, sub *info.DamageZone) {
}
if uint32(sub.Damage.IntPart()) > our.CurPet[0].Info.Hp {
if uint32(sub.Damage.IntPart()) > currentPet.Info.Hp {
our.CurPet[0].Info.Hp = 0
currentPet.Info.Hp = 0
} else {
our.CurPet[0].Info.Hp = our.CurPet[0].Info.Hp - uint32(sub.Damage.IntPart())
currentPet.Info.Hp = currentPet.Info.Hp - uint32(sub.Damage.IntPart())
}
//todo 待实现死亡effet
@@ -213,9 +234,17 @@ func (our *Input) Damage(in *Input, sub *info.DamageZone) {
// 计算技能威力
func (our *Input) CalculatePower(deftype *Input, skill *info.SkillEntity) alpacadecimal.Decimal {
if deftype == nil {
return alpacadecimal.Zero
}
ourPet := our.CurrentPet()
defPet := deftype.CurrentPet()
if ourPet == nil || defPet == nil {
return alpacadecimal.Zero
}
// 1. 计算等级因子 (level * 0.4 + 2)
levelFactor := alpacadecimal.NewFromInt(int64(our.CurPet[0].Info.Level)).
levelFactor := alpacadecimal.NewFromInt(int64(ourPet.Info.Level)).
Mul(alpacadecimal.NewFromFloat(0.4)).Add(alpacadecimal.NewFromInt(2))
var (
@@ -243,17 +272,17 @@ func (our *Input) CalculatePower(deftype *Input, skill *info.SkillEntity) alpaca
// 11. DmgBindHpDv: 造成的伤害等于自身剩余体力*1/2+潜力(个体值); 默认: 0
if skill.XML.DmgBindLv != 0 {
skill.XML.Power = int(deftype.CurPet[0].Info.Level)
skill.XML.Power = int(defPet.Info.Level)
}
if skill.XML.PwrBindDv != 0 {
if skill.XML.PwrBindDv == 1 {
skill.XML.Power = int(our.CurPet[0].Info.Dv * 5)
skill.XML.Power = int(ourPet.Info.Dv * 5)
}
if skill.XML.PwrBindDv == 2 {
skill.XML.Power = int(our.CurPet[0].Info.Hp/3 + our.CurPet[0].Info.Dv)
skill.XML.Power = int(ourPet.Info.Hp/3 + ourPet.Info.Dv)
}
}
@@ -265,7 +294,7 @@ func (our *Input) CalculatePower(deftype *Input, skill *info.SkillEntity) alpaca
}
if skill.XML.DmgBindHpDv != 0 {
skill.XML.Power = int(our.CurPet[0].Info.Hp/2 + our.CurPet[0].Info.Dv)
skill.XML.Power = int(ourPet.Info.Hp/2 + ourPet.Info.Dv)
}
// 5. 基础伤害公式:等级因子 * 威力因子 * 攻击 / 防御 / 50 + 2
@@ -276,8 +305,8 @@ func (our *Input) CalculatePower(deftype *Input, skill *info.SkillEntity) alpaca
Add(alpacadecimal.NewFromInt(2))
var typeRate alpacadecimal.Decimal
//fmt.Println(skill.Type().ID, deftype.CurPet[0].Type().ID)
t, _ := element.Calculator.GetOffensiveMultiplier(skill.GetType().ID, deftype.CurPet[0].GetType().ID)
//fmt.Println(skill.Type().ID, defPet.Type().ID)
t, _ := element.Calculator.GetOffensiveMultiplier(skill.GetType().ID, defPet.GetType().ID)
our.AttackValue.Offensive = gconv.Float32(t)
typeRate = alpacadecimal.NewFromFloat(t)

View File

@@ -20,6 +20,8 @@ type Input struct {
// CanAction bool //是否可以行动
CurPet []*info.BattlePetEntity //当前上场精灵
AllPet []*info.BattlePetEntity
Team []*Input
OppTeam []*Input
Player common.PlayerI
Opp *Input
CanCapture int
@@ -30,8 +32,8 @@ type Input struct {
EffectLost []Effect
// 删掉伤害记录,可以在回调中记录,而不是每次调用记录
*model.AttackValue
FightC common.FightI
SumDamage alpacadecimal.Decimal //伤害
FightC common.FightI
SumDamage alpacadecimal.Decimal //伤害
ShieldDamageTaken alpacadecimal.Decimal
// 记录上一回合结束时的能力等级供效果727等回溯使用。
LastTurnEndProp [6]int8
@@ -76,6 +78,63 @@ func (our *Input) PrimaryCurPet() *info.BattlePetEntity {
return our.CurPetAt(0)
}
// CurrentPet 返回“当前输入槽位”的出战精灵。
// 注意Input 本身已对应一个 actor 槽位,这里不是“整队的 0 号主位”。
func (our *Input) CurrentPet() *info.BattlePetEntity {
return our.PrimaryCurPet()
}
func (our *Input) IsCurrentPetCatchTime(catchTime uint32) bool {
current := our.CurrentPet()
if current == nil {
return false
}
return current.Info.CatchTime == catchTime
}
func (our *Input) ControlledBy(userID uint32) bool {
return our != nil && our.Player != nil && our.Player.GetInfo().UserID == userID
}
// BenchPets 返回当前站位后备精灵(不含当前出战精灵)。
func (our *Input) BenchPets() []*info.BattlePetEntity {
if our == nil {
return nil
}
current := our.CurrentPet()
if current == nil {
return append([]*info.BattlePetEntity(nil), our.AllPet...)
}
bench := make([]*info.BattlePetEntity, 0, len(our.AllPet))
for _, pet := range our.AllPet {
if pet == nil || pet.Info.CatchTime == current.Info.CatchTime {
continue
}
bench = append(bench, pet)
}
return bench
}
func (our *Input) OpponentSlots() []*Input {
if our == nil {
return nil
}
if len(our.OppTeam) == 0 {
if our.Opp != nil {
return []*Input{our.Opp}
}
return nil
}
slots := make([]*Input, 0, len(our.OppTeam))
for _, in := range our.OppTeam {
if in == nil {
continue
}
slots = append(slots, in)
}
return slots
}
func (our *Input) SetCurPetAt(index int, pet *info.BattlePetEntity) {
if our == nil || index < 0 {
return

View File

@@ -75,7 +75,8 @@ func (f *FightC) battleLoop() {
if ff.Player.GetInfo().PetList[j].CatchTime == ff.AllPet[i].Info.CatchTime {
if ff.UserID == f.WinnerId {
if ff.CurPet[0].Info.CatchTime == ff.Player.GetInfo().PetList[j].CatchTime {
currentPet := ff.CurrentPet()
if currentPet != nil && currentPet.Info.CatchTime == ff.Player.GetInfo().PetList[j].CatchTime {
f.Winpet = &ff.Player.GetInfo().PetList[j]
}
@@ -100,9 +101,14 @@ func (f *FightC) battleLoop() {
addpet.EffectInfo = nil //清空特性信息
f.Our[0].Player.(*player.Player).Service.Pet.PetAdd(&addpet, 0)
oppPet := f.Opp[0].CurrentPet()
petID := uint32(0)
if oppPet != nil {
petID = uint32(oppPet.ID)
}
f.Our[0].Player.SendPackCmd(2409, &info.CatchMonsterOutboundInfo{
CatchTime: uint32(addpet.CatchTime),
PetId: uint32(f.Opp[0].CurPet[0].ID),
PetId: petID,
})
defer f.Our[0].Player.(*player.Player).Service.Done.UpdatePet(addpet, 0, 1)
@@ -323,11 +329,6 @@ func (f *FightC) handleTimeout(expectedSlots map[actionSlotKey]struct{}, actions
}
type roundActionPair struct {
our action.BattleActionI
opp action.BattleActionI
}
func flattenActionMap(actions map[actionSlotKey]action.BattleActionI) []action.BattleActionI {
flattened := make([]action.BattleActionI, 0, len(actions))
for _, act := range actions {
@@ -371,44 +372,38 @@ func (f *FightC) sortActions(actions []action.BattleActionI) {
})
}
func (f *FightC) buildRoundActionPairs(actions []action.BattleActionI) []roundActionPair {
remaining := append([]action.BattleActionI(nil), actions...)
f.sortActions(remaining)
func sortActionsByActor(actions []action.BattleActionI) {
sort.SliceStable(actions, func(i, j int) bool {
a, b := actions[i], actions[j]
if a == nil || b == nil {
return a != nil
}
if a.GetActorIndex() != b.GetActorIndex() {
return a.GetActorIndex() < b.GetActorIndex()
}
if a.GetTargetIndex() != b.GetTargetIndex() {
return a.GetTargetIndex() < b.GetTargetIndex()
}
return a.GetPlayerID() < b.GetPlayerID()
})
}
used := make([]bool, len(remaining))
pairs := make([]roundActionPair, 0, len(remaining))
for i, act := range remaining {
if used[i] || act == nil {
func (f *FightC) splitRoundActions(actions []action.BattleActionI) ([]action.BattleActionI, []action.BattleActionI) {
our := make([]action.BattleActionI, 0, len(actions))
opp := make([]action.BattleActionI, 0, len(actions))
for _, act := range actions {
if act == nil {
continue
}
used[i] = true
pair := roundActionPair{}
if f.isOurPlayerID(act.GetPlayerID()) {
pair.our = act
our = append(our, act)
} else {
pair.opp = act
opp = append(opp, act)
}
for j := i + 1; j < len(remaining); j++ {
candidate := remaining[j]
if used[j] || candidate == nil {
continue
}
if f.isOurPlayerID(candidate.GetPlayerID()) == f.isOurPlayerID(act.GetPlayerID()) {
continue
}
used[j] = true
if pair.our == nil {
pair.our = candidate
} else {
pair.opp = candidate
}
break
}
pairs = append(pairs, pair)
}
return pairs
sortActionsByActor(our)
sortActionsByActor(opp)
return our, opp
}
// 根据动作类型执行一回合结算
@@ -418,11 +413,24 @@ func (f *FightC) resolveRound(actions []action.BattleActionI) {
return
}
for _, pair := range f.buildRoundActionPairs(actions) {
ourActions, oppActions := f.splitRoundActions(actions)
roundLen := len(ourActions)
if len(oppActions) > roundLen {
roundLen = len(oppActions)
}
for i := 0; i < roundLen; i++ {
if f.closefight {
return
}
f.resolveActionPair(pair.our, pair.opp)
var ourAct action.BattleActionI
var oppAct action.BattleActionI
if i < len(ourActions) {
ourAct = ourActions[i]
}
if i < len(oppActions) {
oppAct = oppActions[i]
}
f.resolveActionPair(ourAct, oppAct)
}
}

View File

@@ -56,6 +56,7 @@ func buildFight(opts *fightBuildOptions) (*FightC, errorcode.ErrorCode) {
}
f.bindInputFightContext(f.Our)
f.bindInputFightContext(f.Opp)
f.linkTeamViews()
f.linkOppInputs()
f.ReadyInfo.OurInfo, f.ReadyInfo.OurPetList = initfightready(f.primaryOur())

View File

@@ -2,6 +2,7 @@ package node
import (
element "blazing/common/data/Element"
"blazing/logic/service/fight/info"
"blazing/logic/service/fight/input"
"sync"
@@ -75,12 +76,90 @@ func (e *EffectNode) OpponentInput() *input.Input {
return e.Ctx().Opp
}
func (e *EffectNode) SourcePet() *info.BattlePetEntity {
in := e.SourceInput()
if in == nil {
return nil
}
return in.CurrentPet()
}
func (e *EffectNode) CarrierPet() *info.BattlePetEntity {
in := e.CarrierInput()
if in == nil {
return nil
}
return in.CurrentPet()
}
func (e *EffectNode) TargetPet() *info.BattlePetEntity {
in := e.TargetInput()
if in == nil {
return nil
}
return in.CurrentPet()
}
func (e *EffectNode) OpponentPet() *info.BattlePetEntity {
in := e.OpponentInput()
if in == nil {
return nil
}
return in.CurrentPet()
}
func (e *EffectNode) ForEachOpponentSlot(fn func(*input.Input) bool) {
if fn == nil {
return
}
carrier := e.CarrierInput()
if carrier == nil {
if opp := e.OpponentInput(); opp != nil {
fn(opp)
}
return
}
opponents := carrier.OpponentSlots()
if len(opponents) == 0 {
if opp := e.OpponentInput(); opp != nil {
fn(opp)
}
return
}
for _, opp := range opponents {
if opp == nil {
continue
}
if !fn(opp) {
return
}
}
}
func (e *EffectNode) ForEachCarrierBenchPet(fn func(*info.BattlePetEntity) bool) {
if fn == nil {
return
}
carrier := e.CarrierInput()
if carrier == nil {
return
}
for _, pet := range carrier.BenchPets() {
if pet == nil {
continue
}
if !fn(pet) {
return
}
}
}
// IsOwner reports whether the current phase's Our side owns this effect.
func (e *EffectNode) IsOwner() bool {
if e.Ctx().Our == nil || len(e.Ctx().Our.CurPet) == 0 || e.Ctx().Our.CurPet[0] == nil {
if e.Ctx().Our == nil {
return false
}
return e.ID().GetCatchTime() == e.Ctx().Our.CurPet[0].Info.CatchTime
return e.Ctx().Our.IsCurrentPetCatchTime(e.ID().GetCatchTime())
}
func (e *EffectNode) Ctx() *input.Ctx {
@@ -176,10 +255,15 @@ func (e *EffectNode) PropBefer(in *input.Input, prop int8, level int8) bool {
func (e *EffectNode) ISNaturalEnemy() bool {
source := e.SourceInput()
opp := e.OpponentInput()
if source == nil || opp == nil || source.CurPet[0] == nil || opp.CurPet[0] == nil {
if source == nil || opp == nil {
return false
}
t, _ := element.Calculator.GetOffensiveMultiplier(opp.CurPet[0].Type, source.CurPet[0].Type)
sourcePet := source.CurrentPet()
oppPet := opp.CurrentPet()
if sourcePet == nil || oppPet == nil {
return false
}
t, _ := element.Calculator.GetOffensiveMultiplier(oppPet.Type, sourcePet.Type)
if t <= 1 {
return false

View File

@@ -38,8 +38,11 @@ func (e *EffectNode) OnSkill() bool {
}
func (e *EffectNode) Skill_Can() bool {
return e.Input.CurPet[0].HP != 0
currentPet := e.Input.CurrentPet()
if currentPet == nil {
return false
}
return currentPet.HP != 0
}
func (e *EffectNode) GenSub(t input.Effect, Duration int) input.Effect {

View File

@@ -0,0 +1,51 @@
package fight
import "blazing/logic/service/fight/input"
const (
SideOur = 1
SideOpp = 2
)
type SlotView struct {
Side int
SlotIndex int
ControllerUserID uint32
Input *input.Input
}
func (f *FightC) SlotInput(side int, slotIndex int) *input.Input {
switch side {
case SideOur:
return f.selectInput(f.Our, slotIndex)
case SideOpp:
return f.selectInput(f.Opp, slotIndex)
default:
return nil
}
}
func (f *FightC) SideSlots(side int) []SlotView {
var sideInputs []*input.Input
switch side {
case SideOur:
sideInputs = f.Our
case SideOpp:
sideInputs = f.Opp
default:
return nil
}
slots := make([]SlotView, 0, len(sideInputs))
for idx, in := range sideInputs {
if in == nil || in.Player == nil {
continue
}
slots = append(slots, SlotView{
Side: side,
SlotIndex: idx,
ControllerUserID: in.Player.GetInfo().UserID,
Input: in,
})
}
return slots
}