refactor: 重构战斗系统支持多单位多动作

This commit is contained in:
xinian
2026-04-04 05:44:02 +08:00
committed by cnb
parent b62b4af628
commit 28d92c1e18
23 changed files with 1652 additions and 311 deletions

View File

@@ -8,15 +8,19 @@ import (
type FightI interface {
Over(c PlayerI, id model.EnumBattleOverReason) //逃跑
UseSkill(c PlayerI, id uint32) //使用技能
GetCurrPET(c PlayerI) *info.BattlePetEntity //当前精灵
UseSkillAt(c PlayerI, id uint32, actorIndex, targetIndex int)
GetCurrPET(c PlayerI) *info.BattlePetEntity //当前精灵
GetCurrPETAt(c PlayerI, actorIndex int) *info.BattlePetEntity
GetOverInfo() model.FightOverInfo
Ownerid() uint32
ReadyFight(c PlayerI) //是否准备战斗
ChangePet(c PlayerI, id uint32)
ChangePetAt(c PlayerI, id uint32, actorIndex int)
Capture(c PlayerI, id uint32)
LoadPercent(c PlayerI, percent int32)
UseItem(c PlayerI, cacthid, itemid uint32)
UseItemAt(c PlayerI, cacthid, itemid uint32, actorIndex, targetIndex int)
Chat(c PlayerI, msg string)
IsFirst(c PlayerI) bool
GetOverChan() chan struct{}

View File

@@ -6,12 +6,16 @@ import (
"blazing/logic/service/fight/info"
"blazing/logic/service/fight/input"
"blazing/modules/player/model"
"github.com/jinzhu/copier"
)
// Compare 比较两个1v1战斗动作的执行优先级(核心逻辑)
func (*FightC) Compare(a, b action.BattleActionI) (action.BattleActionI, action.BattleActionI) {
// Compare 比较两个战斗动作的执行优先级
func (f *FightC) Compare(a, b action.BattleActionI) (action.BattleActionI, action.BattleActionI) {
if a == nil {
return b, a
}
if b == nil {
return a, b
}
// 动作本身的优先级比较
p1 := b.Priority() - a.Priority()
if p1 > 0 { // 对手优先级更高
@@ -20,11 +24,22 @@ func (*FightC) Compare(a, b action.BattleActionI) (action.BattleActionI, action.
return a, b
}
if speedA, speedB := f.actionSpeed(a), f.actionSpeed(b); speedA.Cmp(speedB) != 0 {
if speedA.Cmp(speedB) > 0 {
return a, b
}
return b, a
}
if a.GetActorIndex() != b.GetActorIndex() {
if a.GetActorIndex() < b.GetActorIndex() {
return a, b
}
return b, a
}
return a, b // 速度相同时,发起方优先
}
const maxPendingActionsPerPlayer = 2
func (f *FightC) openActionWindow() {
f.actionMu.Lock()
f.acceptActions = true
@@ -58,16 +73,15 @@ func (f *FightC) submitAction(act action.BattleActionI) {
f.actionMu.Unlock()
return
}
count := 0
replaceIndex := -1
for i, pending := range f.pendingActions {
if pending == nil || pending.GetPlayerID() != act.GetPlayerID() {
if pending == nil || actionSlotKeyFromAction(pending) != actionSlotKeyFromAction(act) {
continue
}
count++
replaceIndex = i
break
}
if count >= maxPendingActionsPerPlayer && replaceIndex >= 0 {
if replaceIndex >= 0 {
f.pendingActions[replaceIndex] = act
} else {
f.pendingActions = append(f.pendingActions, act)
@@ -263,21 +277,51 @@ func (f *FightC) ReadyFight(c common.PlayerI) {
// buildFightStartInfo 构建战斗开始时需要发送给双方的信息
func (f *FightC) buildFightStartInfo() info.FightStartOutboundInfo {
var startInfo info.FightStartOutboundInfo
// 复制双方初始宠物信息(取列表第一个宠物)
if len(f.ReadyInfo.OurPetList) > 0 {
_ = copier.Copy(&startInfo.Info1, &f.ReadyInfo.OurPetList[0])
startInfo.Info1.UserID = f.ReadyInfo.OurInfo.UserID
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(f.ReadyInfo.OpponentPetList) > 0 {
_ = copier.Copy(&startInfo.Info2, &f.ReadyInfo.OpponentPetList[0])
startInfo.Info2.UserID = f.ReadyInfo.OpponentInfo.UserID
if len(oppInfos) > 0 {
startInfo.Info2 = oppInfos[0]
}
return startInfo
}
func (f *FightC) collectFightPetInfos(inputs []*input.Input) []info.FightPetInfo {
infos := make([]info.FightPetInfo, 0, len(inputs))
for actorIndex, fighter := range inputs {
if fighter == nil || fighter.Player == nil {
continue
}
currentPet := fighter.PrimaryCurPet()
if currentPet == nil {
continue
}
fightInfo := info.FightPetInfo{
UserID: fighter.Player.GetInfo().UserID,
ActorIndex: uint32(actorIndex),
ControllerUserID: currentPet.ControllerUserID,
ID: currentPet.Info.ID,
Name: currentPet.Info.Name,
CatchTime: currentPet.Info.CatchTime,
Hp: currentPet.Info.Hp,
MaxHp: currentPet.Info.MaxHp,
Level: currentPet.Info.Level,
Catchable: uint32(fighter.CanCapture),
}
if fighter.AttackValue != nil {
fightInfo.Prop = fighter.AttackValue.Prop
}
infos = append(infos, fightInfo)
}
return infos
}
// checkBothPlayersReady 检查PVP战斗中双方是否都已准备完成
// 参数c为当前准备的玩家返回true表示双方均准备完成
func (f *FightC) checkBothPlayersReady(currentPlayer common.PlayerI) bool {

View File

@@ -0,0 +1,45 @@
package fight
import (
"blazing/logic/service/fight/action"
"testing"
)
func TestSubmitActionKeepsDifferentActorSlots(t *testing.T) {
f := &FightC{
acceptActions: true,
pendingActions: make([]action.BattleActionI, 0, 4),
}
f.actionRound.Store(1)
first := &action.SelectSkillAction{BaseAction: action.BaseAction{PlayerID: 1001, ActorIndex: 0}}
second := &action.SelectSkillAction{BaseAction: action.BaseAction{PlayerID: 1001, ActorIndex: 1}}
f.submitAction(first)
f.submitAction(second)
if got := len(f.pendingActions); got != 2 {
t.Fatalf("expected 2 pending actions, got %d", got)
}
}
func TestSubmitActionReplacesSameActorSlot(t *testing.T) {
f := &FightC{
acceptActions: true,
pendingActions: make([]action.BattleActionI, 0, 4),
}
f.actionRound.Store(1)
first := &action.SelectSkillAction{BaseAction: action.BaseAction{PlayerID: 1001, ActorIndex: 0}}
replacement := &action.SelectSkillAction{BaseAction: action.BaseAction{PlayerID: 1001, ActorIndex: 0, TargetIndex: 1}}
f.submitAction(first)
f.submitAction(replacement)
if got := len(f.pendingActions); got != 1 {
t.Fatalf("expected 1 pending action, got %d", got)
}
if f.pendingActions[0] != replacement {
t.Fatalf("expected replacement action to be kept")
}
}

View File

@@ -0,0 +1,199 @@
package effect
import (
"blazing/logic/service/fight/action"
"blazing/logic/service/fight/info"
"blazing/logic/service/fight/input"
"blazing/logic/service/fight/node"
"github.com/alpacahq/alpacadecimal"
"github.com/gogf/gf/v2/util/grand"
)
// Effect 1635: 立刻恢复自身{0}点体力,{1}回合后恢复自身全部体力
type Effect1635 struct {
node.EffectNode
}
func (e *Effect1635) Skill_Use() bool {
if len(e.Args()) < 2 || e.Ctx().Our == nil {
return true
}
if e.Args()[0].Cmp(alpacadecimal.Zero) > 0 {
e.Ctx().Our.Heal(e.Ctx().Our, &action.SelectSkillAction{}, e.Args()[0])
}
sub := e.Ctx().Our.InitEffect(input.EffectType.Sub, 1635, int(e.Args()[1].IntPart()))
if sub != nil {
e.Ctx().Our.AddEffect(e.Ctx().Our, sub)
}
return true
}
type Effect1635Sub struct {
RoundEffectArg0Base
}
func (e *Effect1635Sub) TurnEnd() {
if len(e.Args()) == 0 {
return
}
if e.Duration() == 1 && e.Ctx().Our != nil && e.Ctx().Our.CurPet[0] != nil {
e.Ctx().Our.Heal(
e.Ctx().Our,
&action.SelectSkillAction{},
e.Ctx().Our.CurPet[0].GetMaxHP().Sub(e.Ctx().Our.CurPet[0].GetHP()),
)
}
e.EffectNode.TurnEnd()
}
// Effect 1636: 涵双1回合释放4-8张玫瑰卡牌进行攻击每张卡牌额外附加50点固定伤害自身体力低于最大体力的1/3时效果翻倍
type Effect1636 struct {
node.EffectNode
hits int
bonusHP alpacadecimal.Decimal
multipl int
}
func (e *Effect1636) SkillHit() bool {
if e.Ctx().SkillEntity == nil || e.Ctx().SkillEntity.Category() == info.Category.STATUS || e.Ctx().SkillEntity.AttackTime == 0 {
return true
}
base := 4 + grand.Intn(5)
extra := base
if e.Ctx().Our != nil && e.Ctx().Our.CurPet[0] != nil {
threshold := e.Ctx().Our.CurPet[0].GetMaxHP().Div(alpacadecimal.NewFromInt(3))
if e.Ctx().Our.CurPet[0].GetHP().Cmp(threshold) < 0 {
extra *= 2
}
}
e.hits = extra
if e.hits > 1 {
e.Ctx().SkillEntity.AttackTime += uint32(e.hits - 1)
}
e.bonusHP = alpacadecimal.NewFromInt(int64(extra * 50))
return true
}
func (e *Effect1636) DamageDivEx(zone *info.DamageZone) bool {
if e.hits <= 0 || zone == nil || zone.Type != info.DamageType.Red {
return true
}
if e.bonusHP.Cmp(alpacadecimal.Zero) > 0 {
zone.Damage = zone.Damage.Add(e.bonusHP)
}
e.hits = 0
return true
}
// Effect 1637: {0}回合内若对手使用属性技能,则使用属性技能后的下{1}回合属性技能命中效果失效
type Effect1637 struct {
node.EffectNode
triggered bool
}
func (e *Effect1637) Skill_Use() bool {
if len(e.Args()) < 2 || e.Ctx().Opp == nil {
return true
}
sub := e.Ctx().Opp.InitEffect(input.EffectType.Sub, 1637, int(e.Args()[0].IntPart()), int(e.Args()[1].IntPart()))
if sub != nil {
e.Ctx().Opp.AddEffect(e.Ctx().Our, sub)
}
return true
}
type Effect1637Sub struct {
RoundEffectArg0Base
applied bool
}
func (e *Effect1637Sub) ActionStart(fattack, sattack *action.SelectSkillAction) bool {
if e.applied || len(e.Args()) < 2 {
return true
}
oppAction := actionByPlayer(fattack, sattack, e.Ctx().Opp.UserID)
if oppAction == nil || oppAction.SkillEntity == nil || oppAction.SkillEntity.Category() != info.Category.STATUS {
return true
}
sub := e.Ctx().Opp.InitEffect(input.EffectType.Sub, 16371, int(e.Args()[1].IntPart()))
if sub != nil {
e.Ctx().Opp.AddEffect(e.Ctx().Our, sub)
}
e.applied = true
return true
}
type Effect16371Sub struct {
RoundEffectArg0Base
}
func (e *Effect16371Sub) SkillHit_ex() bool {
if e.Ctx().SkillEntity == nil || e.Ctx().SkillEntity.Category() != info.Category.STATUS {
return true
}
e.Ctx().SkillEntity.SetNoSide()
e.Ctx().SkillEntity.AttackTime = 0
return true
}
// Effect 1638: {0}回合内若自身未受到攻击伤害则令对手全属性-{1}
type Effect1638 struct {
RoundEffectArg0Base
touched bool
}
func (e *Effect1638) Skill_Use() bool {
if len(e.Args()) < 2 || e.Ctx().Our == nil {
return true
}
sub := e.Ctx().Our.InitEffect(
input.EffectType.Sub,
1638,
int(e.Args()[0].IntPart()),
int(e.Args()[1].IntPart()),
)
if sub != nil {
e.Ctx().Our.AddEffect(e.Ctx().Our, sub)
}
return true
}
func (e *Effect1638) DamageSubEx(zone *info.DamageZone) bool {
if zone != nil && zone.Type == info.DamageType.Red && zone.Damage.Cmp(alpacadecimal.Zero) > 0 {
e.touched = true
}
return true
}
func (e *Effect1638) TurnEnd() {
if len(e.Args()) >= 2 && !e.touched {
applyAllPropDown(e.Ctx().Our, e.Ctx().Opp, int8(e.Args()[1].IntPart()))
}
e.touched = false
e.EffectNode.TurnEnd()
}
// Effect 1639: 自身处于能力提升状态时100%打出致命一击
type Effect1639 struct {
node.EffectNode
}
func (e *Effect1639) SkillHit() bool {
if e.Ctx().SkillEntity == nil || e.Ctx().Our == nil || !e.Ctx().Our.HasPropADD() || e.Ctx().SkillEntity.Category() == info.Category.STATUS {
return true
}
e.Ctx().SkillEntity.XML.CritRate = 16
return true
}
func init() {
input.InitEffect(input.EffectType.Skill, 1635, &Effect1635{})
input.InitEffect(input.EffectType.Sub, 1635, &Effect1635Sub{})
input.InitEffect(input.EffectType.Skill, 1636, &Effect1636{})
input.InitEffect(input.EffectType.Skill, 1637, &Effect1637{})
input.InitEffect(input.EffectType.Sub, 1637, &Effect1637Sub{})
input.InitEffect(input.EffectType.Sub, 16371, &Effect16371Sub{})
input.InitEffect(input.EffectType.Skill, 1638, &Effect1638{})
input.InitEffect(input.EffectType.Skill, 1639, &Effect1639{})
}

View File

@@ -0,0 +1,160 @@
package effect
import (
"blazing/logic/service/fight/action"
"blazing/logic/service/fight/info"
"blazing/logic/service/fight/input"
"blazing/logic/service/fight/node"
"github.com/alpacahq/alpacadecimal"
)
func addFatalMarks(target *input.Input, count int) {
if target == nil || count <= 0 {
return
}
pet := target.CurPet[0]
if pet == nil {
return
}
pet.FatalCrackLayers += count
}
// Effect 1640: 出手时若自身满体力则100%打出致命一击
type Effect1640 struct{ node.EffectNode }
func (e *Effect1640) Skill_Use() bool {
if e.Ctx().Our == nil || e.Ctx().Opp == nil || e.Ctx().Our.CurPet[0] == nil {
return true
}
if e.Ctx().Our.CurPet[0].GetHP().Cmp(e.Ctx().Our.CurPet[0].GetMaxHP()) < 0 {
return true
}
sub := e.Ctx().Our.InitEffect(input.EffectType.Sub, 1640)
if sub != nil {
e.Ctx().Our.AddEffect(e.Ctx().Our, sub)
}
return true
}
type Effect1640Sub struct{ FixedDuration1Base }
func (e *Effect1640Sub) ActionStart(fattack, sattack *action.SelectSkillAction) bool {
current := actionByPlayer(fattack, sattack, e.Ctx().Our.UserID)
if current == nil || current.SkillEntity == nil || current.SkillEntity.Category() == info.Category.STATUS {
return true
}
current.SkillEntity.XML.CritRate = 16
return true
}
// Effect 1641: 自身处于能力提升状态时造成伤害的{0}%恢复自身体力值当前体力低于最大体力的1/{1}时附加等量百分比伤害
type Effect1641 struct{ node.EffectNode }
func (e *Effect1641) Skill_Use() bool {
if len(e.Args()) < 2 || e.Ctx().Our == nil || e.Ctx().Opp == nil || e.Ctx().Our.CurPet[0] == nil || e.Ctx().Opp.CurPet[0] == nil {
return true
}
if !e.Ctx().Our.HasPropADD() {
return true
}
damage := e.Ctx().Our.SumDamage
if damage.Cmp(alpacadecimal.Zero) <= 0 {
return true
}
heal := damage.Mul(e.Args()[0]).Div(hundred)
if heal.Cmp(alpacadecimal.Zero) > 0 {
e.Ctx().Our.Heal(e.Ctx().Our, &action.SelectSkillAction{}, heal)
}
threshold := e.Ctx().Our.CurPet[0].GetMaxHP().Div(e.Args()[1])
if e.Ctx().Our.CurPet[0].GetHP().Cmp(threshold) < 0 && heal.Cmp(alpacadecimal.Zero) > 0 {
e.Ctx().Opp.Damage(e.Ctx().Our, &info.DamageZone{Type: info.DamageType.True, Damage: heal})
}
return true
}
// Effect 1642: 消除对手能力提升状态,消除成功则{0}%随机为对手任意技能散布{1}枚致命印记
type Effect1642 struct{ node.EffectNode }
func (e *Effect1642) Skill_Use() bool {
if len(e.Args()) < 2 || e.Ctx().Opp == nil {
return true
}
cleared := clearPositiveProps(e.Ctx().Opp, e.Ctx().Our)
if !cleared {
return true
}
if ok, _, _ := e.Input.Player.Roll(int(e.Args()[0].IntPart()), 100); ok {
addFatalMarks(e.Ctx().Opp, int(e.Args()[1].IntPart()))
}
return true
}
// Effect 1643: 对手每存在1层致命裂痕则附加{0}点真实伤害
type Effect1643 struct{ node.EffectNode }
func (e *Effect1643) Skill_Use() bool {
if len(e.Args()) == 0 || e.Ctx().Opp == nil || e.Ctx().Opp.CurPet[0] == nil {
return true
}
layers := e.Ctx().Opp.CurPet[0].FatalCrackLayers
if layers <= 0 {
return true
}
damage := alpacadecimal.NewFromInt(int64(layers)).Mul(e.Args()[0])
if damage.Cmp(alpacadecimal.Zero) <= 0 {
return true
}
e.Ctx().Opp.Damage(e.Ctx().Our, &info.DamageZone{Type: info.DamageType.True, Damage: damage})
return true
}
// Effect 1644: {0}回合内对手使用攻击技能则随机进入{1}种异常状态,未触发则随机为对手任意技能散布{2}枚致命印记
type Effect1644 struct{ node.EffectNode }
func (e *Effect1644) Skill_Use() bool {
if len(e.Args()) < 3 || e.Ctx().Opp == nil {
return true
}
sub := e.Ctx().Opp.InitEffect(input.EffectType.Sub, 1644, e.SideEffectArgs...)
if sub != nil {
e.Ctx().Opp.AddEffect(e.Ctx().Our, sub)
}
return true
}
type Effect1644Sub struct {
RoundEffectArg0Base
triggered bool
}
func (e *Effect1644Sub) Action_end() bool {
if len(e.Args()) < 3 || e.Ctx().SkillEntity == nil || e.Ctx().SkillEntity.Category() == info.Category.STATUS || e.Ctx().SkillEntity.AttackTime == 0 {
return true
}
if e.Args()[1].IntPart() > 0 {
applyRandomStatuses1507(e.Ctx().Our, e.Ctx().Opp, int(e.Args()[1].IntPart()))
}
e.triggered = true
return true
}
func (e *Effect1644Sub) TurnEnd() {
if !e.triggered && len(e.Args()) >= 3 {
addFatalMarks(e.Ctx().Opp, int(e.Args()[2].IntPart()))
}
e.triggered = false
e.EffectNode.TurnEnd()
}
func init() {
input.InitEffect(input.EffectType.Skill, 1640, &Effect1640{})
input.InitEffect(input.EffectType.Sub, 1640, &Effect1640Sub{})
input.InitEffect(input.EffectType.Skill, 1641, &Effect1641{})
input.InitEffect(input.EffectType.Skill, 1642, &Effect1642{})
input.InitEffect(input.EffectType.Skill, 1643, &Effect1643{})
input.InitEffect(input.EffectType.Skill, 1644, &Effect1644{})
input.InitEffect(input.EffectType.Sub, 1644, &Effect1644Sub{})
}

View File

@@ -0,0 +1,272 @@
package effect
import (
"blazing/logic/service/fight/action"
"blazing/logic/service/fight/info"
"blazing/logic/service/fight/input"
"blazing/logic/service/fight/node"
"github.com/alpacahq/alpacadecimal"
)
const fatalCrackEffectID = 17001
func fatalCrackLayers(target *input.Input) int {
if target == nil {
return 0
}
total := 0
for _, eff := range target.Effects {
if eff == nil || !eff.Alive() || eff.ID().GetEffectType() != input.EffectType.Sub {
continue
}
if int(eff.ID().Suffix()) != fatalCrackEffectID {
continue
}
if sub, ok := eff.(*FatalCrackSub); ok {
total += sub.layers
}
}
return total
}
func addFatalCrack(owner, target *input.Input, layers int) {
if target == nil || owner == nil || layers <= 0 {
return
}
eff := owner.InitEffect(input.EffectType.Sub, fatalCrackEffectID, layers)
if eff != nil {
target.AddEffect(owner, eff)
}
}
type FatalCrackSub struct {
node.EffectNode
layers int
}
func (f *FatalCrackSub) SetArgs(t *input.Input, a ...int) {
f.EffectNode.SetArgs(t, a...)
f.CanStack(true)
f.Duration(-1)
if len(a) > 0 {
f.layers = a[0]
}
if f.layers <= 0 {
f.layers = 0
}
}
// Effect 1645: {0}回合内对手使用属性技能则自身下{1}次受到的攻击伤害减少{2}%
type Effect1645 struct{ node.EffectNode }
func (e *Effect1645) Skill_Use() bool {
if len(e.Args()) < 3 {
return true
}
sub := e.Ctx().Our.InitEffect(input.EffectType.Sub, 1645, int(e.Args()[0].IntPart()), int(e.Args()[1].IntPart()), int(e.Args()[2].IntPart()))
if sub != nil {
e.Ctx().Our.AddEffect(e.Ctx().Our, sub)
}
return true
}
type Effect1645Sub struct {
RoundEffectArg0Base
active bool
hits int
reduce alpacadecimal.Decimal
}
func (e *Effect1645Sub) SetArgs(t *input.Input, a ...int) {
e.RoundEffectArg0Base.SetArgs(t, a...)
if len(a) > 1 {
e.hits = a[1]
}
if len(a) > 2 {
e.reduce = alpacadecimal.NewFromInt(int64(a[2]))
}
if e.hits < 0 {
e.hits = 0
}
if e.reduce.Cmp(alpacadecimal.Zero) < 0 {
e.reduce = alpacadecimal.Zero
}
e.active = false
}
func (e *Effect1645Sub) SkillHit_ex() bool {
if e.Ctx().SkillEntity == nil || e.Ctx().SkillEntity.Category() != info.Category.STATUS {
return true
}
if e.hits <= 0 {
return true
}
e.active = true
return true
}
func (e *Effect1645Sub) DamageDivEx(zone *info.DamageZone) bool {
if !e.active || e.hits <= 0 || zone == nil || zone.Type != info.DamageType.Red || e.reduce.Cmp(alpacadecimal.Zero) <= 0 {
return true
}
zone.Damage = zone.Damage.Mul(hundred.Sub(e.reduce)).Div(hundred)
e.hits--
if e.hits <= 0 {
e.active = false
}
return true
}
// Effect 1646: 全属性+{0},对手存在致命裂痕时强化效果翻倍
type Effect1646 struct{ node.EffectNode }
func (e *Effect1646) Skill_Use() bool {
if len(e.Args()) == 0 {
return true
}
boost := int8(e.Args()[0].IntPart())
if fatalCrackLayers(e.Ctx().Opp) > 0 {
boost *= 2
}
for i := 0; i < 6; i++ {
e.Ctx().Our.SetProp(e.Ctx().Our, int8(i), boost)
}
return true
}
// Effect 1647: {0}回合内每回合使用技能吸取对手最大体力的1/{1}吸取体力时若自身体力低于最大体力的1/{2}则吸取效果翻倍,对手免疫百分比伤害时额外附加{3}点真实伤害
type Effect1647 struct{ node.EffectNode }
func (e *Effect1647) Skill_Use() bool {
if len(e.Args()) < 4 {
return true
}
sub := e.Ctx().Our.InitEffect(input.EffectType.Sub, 1647, e.SideEffectArgs...)
if sub != nil {
e.Ctx().Our.AddEffect(e.Ctx().Our, sub)
}
return true
}
type Effect1647Sub struct{ RoundEffectArg0Base }
func (e *Effect1647Sub) OnSkill() bool {
if len(e.Args()) < 4 || e.Ctx().Opp == nil || e.Ctx().Opp.CurPet[0] == nil || e.Ctx().Our == nil || e.Ctx().Our.CurPet[0] == nil {
return true
}
drain := e.Ctx().Opp.CurPet[0].GetMaxHP().Div(e.Args()[1])
threshold := e.Ctx().Our.CurPet[0].GetMaxHP().Div(alpacadecimal.NewFromInt(2))
if e.Ctx().Our.CurPet[0].GetHP().Cmp(threshold) < 0 {
drain = drain.Mul(alpacadecimal.NewFromInt(2))
}
if drain.Cmp(alpacadecimal.Zero) <= 0 {
return true
}
before := e.Ctx().Opp.CurPet[0].GetHP()
e.Ctx().Opp.Damage(e.Ctx().Our, &info.DamageZone{Type: info.DamageType.Percent, Damage: drain})
e.Ctx().Our.Heal(e.Ctx().Our, &action.SelectSkillAction{}, drain)
if e.Ctx().Opp.CurPet[0].GetHP().Cmp(before) == 0 && e.Args()[3].Cmp(alpacadecimal.Zero) > 0 {
e.Ctx().Opp.Damage(e.Ctx().Our, &info.DamageZone{Type: info.DamageType.True, Damage: e.Args()[3]})
}
return true
}
// Effect 1648: 附加自身最大体力{0}%的百分比伤害并恢复等量体力值,对手存在致命裂痕时转变为等量的真实伤害
type Effect1648 struct{ node.EffectNode }
func (e *Effect1648) OnSkill() bool {
if len(e.Args()) == 0 || e.Ctx().Opp == nil || e.Ctx().Opp.CurPet[0] == nil {
return true
}
percent := e.Args()[0]
damage := e.Ctx().Our.CurPet[0].GetMaxHP().Mul(percent).Div(alpacadecimal.NewFromInt(100))
if damage.Cmp(alpacadecimal.Zero) <= 0 {
return true
}
target := &info.DamageZone{Type: info.DamageType.Percent, Damage: damage}
if fatalCrackLayers(e.Ctx().Opp) > 0 {
target.Type = info.DamageType.True
}
e.Ctx().Opp.Damage(e.Ctx().Our, target)
e.Ctx().Our.Heal(e.Ctx().Our, &action.SelectSkillAction{}, damage)
return true
}
// Effect 1649: {0}%概率造成的攻击伤害为{1}倍对手每存在1层致命裂痕则概率提升{2}%,未触发则{3}回合内令对手使用的属性技能无效
type Effect1649 struct{ node.EffectNode }
func (e *Effect1649) Skill_Use() bool {
if len(e.Args()) < 4 {
return true
}
sub := e.Ctx().Our.InitEffect(input.EffectType.Sub, 1649, e.SideEffectArgs...)
if sub != nil {
e.Ctx().Our.AddEffect(e.Ctx().Our, sub)
}
return true
}
type Effect1649Sub struct {
RoundEffectArg0Base
fired bool
}
func (e *Effect1649Sub) SkillHit() bool {
e.fired = false
return true
}
func (e *Effect1649Sub) Damage_Mul(zone *info.DamageZone) bool {
if e.fired || zone == nil || zone.Type != info.DamageType.Red || len(e.Args()) < 4 {
return true
}
chance := int(e.Args()[0].IntPart())
chance += fatalCrackLayers(e.Ctx().Opp) * int(e.Args()[2].IntPart())
if chance > 100 {
chance = 100
}
if chance > 0 {
if ok, _, _ := e.Input.Player.Roll(chance, 100); ok {
mul := int(e.Args()[1].IntPart())
if mul <= 1 {
mul = 1
}
e.fired = true
zone.Damage = zone.Damage.Mul(alpacadecimal.NewFromInt(int64(mul)))
return true
}
}
duration := int(e.Args()[3].IntPart())
if duration > 0 {
block := e.Ctx().Opp.InitEffect(input.EffectType.Sub, 16494, duration)
if block != nil {
e.Ctx().Opp.AddEffect(e.Ctx().Our, block)
}
}
e.fired = true
return true
}
type Effect1649BlockSub struct{ RoundEffectArg0Base }
func (e *Effect1649BlockSub) SkillHit_ex() bool {
if e.Ctx().SkillEntity == nil || e.Ctx().SkillEntity.Category() != info.Category.STATUS {
return true
}
e.Ctx().SkillEntity.SetNoSide()
return true
}
func init() {
input.InitEffect(input.EffectType.Sub, fatalCrackEffectID, &FatalCrackSub{})
input.InitEffect(input.EffectType.Skill, 1645, &Effect1645{})
input.InitEffect(input.EffectType.Sub, 1645, &Effect1645Sub{})
input.InitEffect(input.EffectType.Skill, 1646, &Effect1646{})
input.InitEffect(input.EffectType.Skill, 1647, &Effect1647{})
input.InitEffect(input.EffectType.Sub, 1647, &Effect1647Sub{})
input.InitEffect(input.EffectType.Skill, 1648, &Effect1648{})
input.InitEffect(input.EffectType.Skill, 1649, &Effect1649{})
input.InitEffect(input.EffectType.Sub, 1649, &Effect1649Sub{})
input.InitEffect(input.EffectType.Sub, 16494, &Effect1649BlockSub{})
}

View File

@@ -0,0 +1,270 @@
package effect
import (
"blazing/logic/service/fight/info"
"blazing/logic/service/fight/input"
"blazing/logic/service/fight/node"
"github.com/alpacahq/alpacadecimal"
"github.com/gogf/gf/v2/util/grand"
)
const fatalMarkDamagePercent = 10
func randomSkillIndexes(count int, skills []*info.SkillEntity) []int {
if count <= 0 || len(skills) == 0 {
return nil
}
if count > len(skills) {
count = len(skills)
}
indexes := grand.Perm(len(skills))
return indexes[:count]
}
func addFatalMark(owner, target *input.Input, skillID int) bool {
if owner == nil || target == nil {
return false
}
sub := target.InitEffect(input.EffectType.Sub, 1650, skillID)
if sub == nil {
return false
}
target.AddEffect(owner, sub)
return true
}
func fatalMarkCount(target *input.Input) int {
if target == nil {
return 0
}
count := 0
for _, eff := range target.Effects {
if eff == nil || !eff.Alive() {
continue
}
if eff.ID().GetEffectType() != input.EffectType.Sub || int(eff.ID().Suffix()) != 1650 {
continue
}
count++
}
return count
}
func fatalMarkReduction(target *input.Input) (int, *Effect1651Sub) {
if target == nil {
return 1, nil
}
eff := target.GetEffect(input.EffectType.Sub, 1651)
if eff == nil {
return 1, nil
}
sub, ok := eff.(*Effect1651Sub)
if !ok || sub.remaining <= 0 {
return 1, nil
}
divisor := sub.divisor
if divisor <= 0 {
divisor = 1
}
sub.remaining--
if sub.remaining <= 0 {
sub.Alive(false)
}
return divisor, sub
}
// Effect 1650: 命中后{0}%随机为对手任意技能散布{1}枚致命印记,若对手当前精灵致命裂痕≥{2}层则额外散布{3}枚致命印记
type Effect1650 struct {
node.EffectNode
}
func (e *Effect1650) SkillHit() bool {
if len(e.Args()) < 4 || e.Ctx().SkillEntity == nil || e.Ctx().SkillEntity.Category() == info.Category.STATUS || e.Ctx().SkillEntity.AttackTime == 0 || e.Ctx().Opp == nil || e.Ctx().Opp.CurPet[0] == nil {
return true
}
if ok, _, _ := e.Input.Player.Roll(int(e.Args()[0].IntPart()), 100); !ok {
return true
}
count := int(e.Args()[1].IntPart())
if count <= 0 {
return true
}
skillList := e.Ctx().Opp.CurPet[0].Skills
selected := randomSkillIndexes(count, skillList)
if fatalMarkCount(e.Ctx().Opp) >= int(e.Args()[2].IntPart()) {
extra := randomSkillIndexes(int(e.Args()[3].IntPart()), skillList)
selected = append(selected, extra...)
}
for _, idx := range selected {
if idx < 0 || idx >= len(skillList) {
continue
}
addFatalMark(e.Ctx().Our, e.Ctx().Opp, skillList[idx].XML.ID)
}
return true
}
type Effect1650MarkSub struct {
node.EffectNode
skillID int
}
func (e *Effect1650MarkSub) SetArgs(t *input.Input, a ...int) {
e.EffectNode.SetArgs(t, a...)
e.Duration(-1)
if len(a) > 0 {
e.skillID = a[0]
}
}
func (e *Effect1650MarkSub) SkillHit() bool {
if e.skillID == 0 || e.Ctx().SkillEntity == nil || e.Ctx().SkillEntity.AttackTime == 0 || e.Ctx().SkillEntity.Category() == info.Category.STATUS {
return true
}
if e.Ctx().SkillEntity.XML.ID != e.skillID {
return true
}
damage := e.Ctx().Our.CurPet[0].GetMaxHP().Mul(alpacadecimal.NewFromInt(fatalMarkDamagePercent)).Div(alpacadecimal.NewFromInt(100))
divisor, _ := fatalMarkReduction(e.Ctx().Opp)
damage = damage.Div(alpacadecimal.NewFromInt(int64(divisor)))
if damage.Cmp(alpacadecimal.Zero) > 0 {
e.Ctx().Opp.Damage(e.Ctx().Our, &info.DamageZone{Type: info.DamageType.True, Damage: damage})
}
e.Alive(false)
return true
}
// Effect 1651: 当回合击败对手则令对手下{0}次触发致命印记真实伤害效果转变为1/{1}
type Effect1651 struct {
node.EffectNode
}
func (e *Effect1651) Skill_Use() bool {
if len(e.Args()) < 2 || e.Ctx().Opp == nil || e.Ctx().Opp.CurPet[0] == nil || e.Ctx().Opp.CurPet[0].Info.Hp > 0 {
return true
}
sub := e.Ctx().Opp.InitEffect(input.EffectType.Sub, 1651, int(e.Args()[0].IntPart()), int(e.Args()[1].IntPart()))
if sub != nil {
e.Ctx().Opp.AddEffect(e.Ctx().Our, sub)
}
return true
}
type Effect1651Sub struct {
node.EffectNode
remaining int
divisor int
}
func (e *Effect1651Sub) SetArgs(t *input.Input, a ...int) {
e.EffectNode.SetArgs(t, a...)
e.Duration(-1)
if len(a) > 0 {
e.remaining = a[0]
}
if len(a) > 1 {
e.divisor = a[1]
}
}
// Effect 1652: 释放技能时自身每损失{0}%的体力值则此技能威力提升{1}点
type Effect1652 struct {
node.EffectNode
}
func (e *Effect1652) SkillHit() bool {
if len(e.Args()) < 2 || e.Ctx().SkillEntity == nil || e.Ctx().SkillEntity.Category() == info.Category.STATUS || e.Ctx().Our == nil || e.Ctx().Our.CurPet[0] == nil {
return true
}
maxHP := e.Ctx().Our.CurPet[0].GetMaxHP()
curHP := e.Ctx().Our.CurPet[0].GetHP()
if maxHP.Cmp(alpacadecimal.Zero) == 0 {
return true
}
missing := maxHP.Sub(curHP)
percent := missing.Mul(alpacadecimal.NewFromInt(100)).Div(maxHP)
unit := e.Args()[0]
if unit.Cmp(alpacadecimal.Zero) <= 0 {
return true
}
steps := int(percent.Div(unit).IntPart())
if steps <= 0 {
return true
}
e.Ctx().SkillEntity.XML.Power += steps * int(e.Args()[1].IntPart())
return true
}
// Effect 1653: 释放技能时对手每残留{0}%的体力值则此技能附加{1}点固定伤害
type Effect1653 struct {
node.EffectNode
}
func (e *Effect1653) Skill_Use() bool {
if len(e.Args()) < 2 || e.Ctx().Opp == nil || e.Ctx().Opp.CurPet[0] == nil || e.Ctx().SkillEntity == nil || e.Ctx().SkillEntity.AttackTime == 0 {
return true
}
maxHP := e.Ctx().Opp.CurPet[0].GetMaxHP()
curHP := e.Ctx().Opp.CurPet[0].GetHP()
if maxHP.Cmp(alpacadecimal.Zero) == 0 {
return true
}
percent := curHP.Mul(alpacadecimal.NewFromInt(100)).Div(maxHP)
unit := e.Args()[0]
if unit.Cmp(alpacadecimal.Zero) <= 0 {
return true
}
count := int(percent.Div(unit).IntPart())
if count <= 0 {
return true
}
damage := alpacadecimal.NewFromInt(int64(count) * int64(e.Args()[1].IntPart()))
if damage.Cmp(alpacadecimal.Zero) > 0 {
e.Ctx().Opp.Damage(e.Ctx().Our, &info.DamageZone{Type: info.DamageType.Fixed, Damage: damage})
}
return true
}
// Effect 1654: 当回合击败对手则令对手下只登场精灵首次使用的技能所附加的效果失效
type Effect1654 struct {
node.EffectNode
}
func (e *Effect1654) Skill_Use() bool {
if len(e.Args()) == 0 || e.Ctx().Opp == nil || e.Ctx().Opp.CurPet[0] == nil || e.Ctx().Opp.CurPet[0].Info.Hp > 0 {
return true
}
sub := e.Ctx().Opp.InitEffect(input.EffectType.Sub, 1654)
if sub != nil {
e.Ctx().Opp.AddEffect(e.Ctx().Our, sub)
}
return true
}
type Effect1654Sub struct {
node.EffectNode
deployed bool
}
func (e *Effect1654Sub) OnSkill() bool {
if e.deployed || e.Ctx().SkillEntity == nil {
return true
}
e.Ctx().SkillEntity.XML.SideEffectS = nil
e.Ctx().SkillEntity.XML.SideEffectArgS = nil
e.deployed = true
e.Alive(false)
return true
}
func init() {
input.InitEffect(input.EffectType.Skill, 1650, &Effect1650{})
input.InitEffect(input.EffectType.Sub, 1650, &Effect1650MarkSub{})
input.InitEffect(input.EffectType.Skill, 1651, &Effect1651{})
input.InitEffect(input.EffectType.Sub, 1651, &Effect1651Sub{})
input.InitEffect(input.EffectType.Skill, 1652, &Effect1652{})
input.InitEffect(input.EffectType.Skill, 1653, &Effect1653{})
input.InitEffect(input.EffectType.Skill, 1654, &Effect1654{})
input.InitEffect(input.EffectType.Sub, 1654, &Effect1654Sub{})
}

View File

@@ -0,0 +1,233 @@
package effect
import (
"blazing/logic/service/fight/action"
"blazing/logic/service/fight/info"
"blazing/logic/service/fight/input"
"blazing/logic/service/fight/node"
"github.com/alpacahq/alpacadecimal"
"github.com/gogf/gf/v2/util/grand"
)
// Effect 1655: {0}回合内每回合结束后{1}%恢复自身所有技能{2}点PP值
type Effect1655 struct{ node.EffectNode }
func (e *Effect1655) Skill_Use() bool {
if len(e.Args()) < 3 {
return true
}
sub := e.Ctx().Our.InitEffect(input.EffectType.Sub, 1655, int(e.Args()[0].IntPart()), int(e.Args()[1].IntPart()), int(e.Args()[2].IntPart()))
if sub != nil {
e.Ctx().Our.AddEffect(e.Ctx().Our, sub)
}
return true
}
type Effect1655Sub struct{ RoundEffectArg0Base }
func (e *Effect1655Sub) TurnEnd() {
if len(e.Args()) < 3 || e.Ctx().Our == nil || e.Args()[2].Cmp(alpacadecimal.Zero) <= 0 {
e.EffectNode.TurnEnd()
return
}
chance := int(e.Args()[1].IntPart())
if chance <= 0 {
e.EffectNode.TurnEnd()
return
}
if ok, _, _ := e.Input.Player.Roll(chance, 100); ok {
amount := int(e.Args()[2].IntPart())
if amount > 0 && e.Ctx().Our.CurPet[0] != nil {
for i := range e.Ctx().Our.CurPet[0].Info.SkillList {
e.Ctx().Our.CurPet[0].Info.SkillList[i].PP += uint32(amount)
}
}
}
e.EffectNode.TurnEnd()
}
// Effect 1656: 100%复制对手当回合释放的技能若对手当回合使用的技能为攻击技能则令对手随机1个技能PP值归零若对手当回合使用的技能为属性技能则令对手下回合先制-2
type Effect1656 struct {
node.EffectNode
applied bool
}
func (e *Effect1656) ComparePre(fattack, sattack *action.SelectSkillAction) bool {
if e.applied {
return true
}
current := actionByPlayer(fattack, sattack, e.Ctx().Our.UserID)
opponent := actionByPlayer(fattack, sattack, e.Ctx().Opp.UserID)
if current == nil || opponent == nil || opponent.SkillEntity == nil {
return true
}
clone := cloneSkillEntity(opponent.SkillEntity)
if clone == nil {
return true
}
current.SkillEntity = clone
e.applied = true
if opponent.SkillEntity.Category() != info.Category.STATUS {
zeroRandomSkillPP(e.Ctx().Opp, 1)
} else {
sub := e.Ctx().Opp.InitEffect(input.EffectType.Sub, 1656, 1, 2)
if sub != nil {
e.Ctx().Opp.AddEffect(e.Ctx().Opp, sub)
}
}
return true
}
func (e *Effect1656) Action_end() bool {
e.applied = false
return true
}
type Effect1656Sub struct {
FixedDuration1Base
priority int
}
func (e *Effect1656Sub) SetArgs(t *input.Input, a ...int) {
e.FixedDuration1Base.SetArgs(t, a...)
e.CanStack(false)
if len(a) > 1 {
e.priority = a[1]
}
}
func (e *Effect1656Sub) ComparePre(fattack, sattack *action.SelectSkillAction) bool {
if e.priority == 0 {
return true
}
current := actionByPlayer(fattack, sattack, e.Ctx().Opp.UserID)
if current == nil || current.SkillEntity == nil {
return true
}
current.SkillEntity.XML.Priority -= e.priority
return true
}
// Effect 1657: 己方每有一只精灵死亡则附加{0}点固定伤害对手体力高于最大体力的1/{1}时转变为{2}点
type Effect1657 struct{ node.EffectNode }
func (e *Effect1657) Skill_Use() bool {
if len(e.Args()) < 3 || e.Ctx().Opp == nil || e.Ctx().Our == nil {
return true
}
dead := 0
for _, pet := range e.Ctx().Our.AllPet {
if pet == nil {
continue
}
if !pet.Alive() {
dead++
}
}
if dead == 0 {
return true
}
damage := e.Args()[0].Mul(alpacadecimal.NewFromInt(int64(dead)))
if e.Args()[1].Cmp(alpacadecimal.Zero) > 0 && e.Ctx().Opp.CurPet[0] != nil {
threshold := e.Ctx().Opp.CurPet[0].GetMaxHP().Div(e.Args()[1])
if threshold.Cmp(alpacadecimal.Zero) > 0 && e.Ctx().Opp.CurPet[0].GetHP().Cmp(threshold) > 0 {
damage = e.Args()[2]
}
}
if damage.Cmp(alpacadecimal.Zero) <= 0 {
return true
}
e.Ctx().Opp.Damage(e.Ctx().Our, &info.DamageZone{Type: info.DamageType.Fixed, Damage: damage})
return true
}
// Effect 1658: 3回合内每回合80%闪避对手攻击未触发时自身处于圣念状态则使对手随机1项技能PP值归零自身处于邪念状态则使对手失明
type Effect1658 struct{ node.EffectNode }
func (e *Effect1658) Skill_Use() bool {
sub := e.Ctx().Our.InitEffect(input.EffectType.Sub, 1658, 3, 80)
if sub != nil {
e.Ctx().Our.AddEffect(e.Ctx().Our, sub)
}
return true
}
type Effect1658Sub struct{ RoundEffectArg0Base }
func (e *Effect1658Sub) SkillHit_ex() bool {
if len(e.Args()) < 2 || e.Ctx().Opp == nil || e.Ctx().SkillEntity == nil || e.Ctx().SkillEntity.Category() == info.Category.STATUS || e.Ctx().SkillEntity.AttackTime == 0 {
return true
}
if e.Ctx().SkillEntity.AttackTime == 2 {
return true
}
chance := int(e.Args()[1].IntPart())
if chance <= 0 {
return true
}
if ok, _, _ := e.Input.Player.Roll(chance, 100); ok {
e.Ctx().SkillEntity.SetMiss()
return true
}
switch {
case e.Ctx().Our.StatEffect_Exist(petStatus2077Holy):
zeroRandomSkillPP(e.Ctx().Opp, 1)
case e.Ctx().Our.StatEffect_Exist(petStatus2077Evil):
addStatusByID(e.Ctx().Our, e.Ctx().Opp, int(info.PetStatus.Blind))
}
return true
}
// Effect 1659: 随机附加给对手{0}-{1}点固定伤害,若打出致命一击则效果转变为吸取对手{2}-{3}点体力
type Effect1659 struct{ node.EffectNode }
func (e *Effect1659) Skill_Use() bool {
if len(e.Args()) < 4 || e.Ctx().SkillEntity == nil || e.Ctx().SkillEntity.AttackTime == 0 || e.Ctx().Opp == nil {
return true
}
minDamage := int(e.Args()[0].IntPart())
maxDamage := int(e.Args()[1].IntPart())
if maxDamage < minDamage {
maxDamage = minDamage
}
damage := alpacadecimal.NewFromInt(int64(minDamage))
if maxDamage > minDamage {
damage = alpacadecimal.NewFromInt(int64(minDamage + grand.Intn(maxDamage-minDamage+1)))
}
if e.Ctx().SkillEntity.Crit != 0 {
minDrain := int(e.Args()[2].IntPart())
maxDrain := int(e.Args()[3].IntPart())
if maxDrain < minDrain {
maxDrain = minDrain
}
if minDrain < 0 {
minDrain = 0
}
drain := minDrain
if maxDrain > minDrain {
drain += grand.Intn(maxDrain - minDrain + 1)
}
if drain > 0 {
amount := alpacadecimal.NewFromInt(int64(drain))
e.Ctx().Opp.Damage(e.Ctx().Our, &info.DamageZone{Type: info.DamageType.Fixed, Damage: amount})
e.Ctx().Our.Heal(e.Ctx().Our, &action.SelectSkillAction{}, amount)
}
return true
}
if damage.Cmp(alpacadecimal.Zero) > 0 {
e.Ctx().Opp.Damage(e.Ctx().Our, &info.DamageZone{Type: info.DamageType.Fixed, Damage: damage})
}
return true
}
func init() {
input.InitEffect(input.EffectType.Skill, 1655, &Effect1655{})
input.InitEffect(input.EffectType.Sub, 1655, &Effect1655Sub{})
input.InitEffect(input.EffectType.Skill, 1656, &Effect1656{})
input.InitEffect(input.EffectType.Sub, 1656, &Effect1656Sub{})
input.InitEffect(input.EffectType.Skill, 1657, &Effect1657{})
input.InitEffect(input.EffectType.Skill, 1658, &Effect1658{})
input.InitEffect(input.EffectType.Sub, 1658, &Effect1658Sub{})
input.InitEffect(input.EffectType.Skill, 1659, &Effect1659{})
}

View File

@@ -18,6 +18,7 @@ func (e *Effect63) OnSkill() bool {
for i, v := range e.Ctx().Our.Prop[:] {
if v < 0 {
e.Ctx().Opp.SetProp(e.Ctx().Our, int8(i), v)
e.Ctx().Our.SetProp(e.Ctx().Our, int8(i), 0)
}
}

View File

@@ -1060,6 +1060,26 @@ var effectInfoByID = map[int]string{
1632: "吸取对手{0}点体力若对手任意1项技能PP值小于{1}点则额外吸取{2}点体力",
1633: "使自身体力百分比与对手体力百分比对调,自身体力百分比高于对手时{0}%令对手{1}",
1634: "自身体力低于250时必定先手",
1640: "出手时若自身满体力则100%打出致命一击",
1641: "自身处于能力提升状态时造成伤害的{0}%恢复自身体力值当前体力低于最大体力的1/{1}时附加等量百分比伤害",
1642: "消除对手能力提升状态,消除成功则{0}%随机在对手身上散布{1}枚致命印记",
1643: "对手每存在1层致命裂痕则附加{0}点真实伤害",
1644: "{0}回合内对手使用攻击技能则随机进入{1}种异常状态,未触发则随机散布{2}枚致命印记",
1645: "{0}回合内对手使用属性技能则自身下{1}次受到的攻击伤害减少{2}%",
1646: "全属性+{0},对手存在致命裂痕时强化效果翻倍",
1647: "{0}回合内每回合使用技能吸取对手最大体力的1/{1}吸取体力时若自身体力低于最大体力的1/{2}则吸取效果翻倍,对手免疫百分比伤害时额外附加{3}点真实伤害",
1648: "附加自身最大体力{0}%的百分比伤害并恢复等量体力值,对手存在致命裂痕时转变为等量的真实伤害",
1649: "{0}%概率造成的攻击伤害为{1}倍对手每存在1层致命裂痕则概率提升{2}%,未触发则{3}回合内令对手使用的属性技能无效",
1650: "命中后{0}%随机为对手任意技能散布{1}枚致命印记,若对手当前精灵致命裂痕≥{2}层则额外散布{3}枚致命印记",
1651: "当回合击败对手则令对手下{0}次触发致命印记真实伤害效果转变为1/{1}",
1652: "释放技能时自身每损失{0}%的体力值则此技能威力提升{1}点",
1653: "释放技能时对手每残留{0}%的体力值则此技能附加{1}点固定伤害",
1654: "当回合击败对手则令对手下只登场精灵首次使用的技能所附加的效果失效",
1655: "{0}回合内每回合结束后{1}%恢复自身所有技能{2}点PP值",
1656: "100%复制对手当回合释放的技能若对手当回合使用的技能为攻击技能则令对手随机1个技能PP值归零若对手当回合使用的技能为属性技能则令对手下回合先制-2",
1657: "己方每有一只精灵死亡则附加{0}点固定伤害对手体力高于最大体力的1/{1}时转变为{2}点",
1658: "3回合内每回合80%闪避对手攻击未触发时自身处于圣念状态则使对手随机1项技能PP值归零自身处于邪念状态则使对手失明",
1659: "随机附加给对手{0}-{1}点固定伤害,若打出致命一击则效果转变为吸取对手{2}-{3}点体力",
1601: "命中后附加自身最大体力{0}%的百分比伤害,若打出的攻击伤害为奇数则额外恢复等量体力值",
1602: "{0}回合内每回合使用技能恢复自身最大体力的1/{1},恢复体力时若自身为满体力则恢复己方所有不在场精灵{2}点体力",
1603: "{0}%降低对手所有PP值{1}点",
@@ -1088,6 +1108,11 @@ var effectInfoByID = map[int]string{
1627: "{0}回合做{1}-{2}次攻击,若本回合攻击次数达到最大则必定秒杀对手",
1628: "每次使用该技能击败对手则恢复自身全部体力,同时重置该技能使用次数并使该技能攻击威力提升{0}点,未击败对手时令自身下回合攻击技能先制+{1}",
1629: "{0}基础速度值{1}{2}则自身下回合先制+{3}",
1635: "立刻恢复自身{0}点体力,{1}回合后恢复自身全部体力",
1636: "涵双1回合释放4-8张玫瑰卡牌进行攻击每张卡牌额外附加50点固定伤害自身体力低于最大体力的1/3时效果翻倍",
1637: "{0}回合内若对手使用属性技能,则使用属性技能后的下{1}回合属性技能命中效果失效",
1638: "{0}回合内若自身未受到攻击伤害则令对手全属性-{1}",
1639: "自身处于能力提升状态时100%打出致命一击",
1670: "{0}%令对手{1},对手为自身天敌时概率提升{2}%,未触发则消除对手回合类效果",
1671: "造成的攻击伤害不低于{0},若对手处于能力提升状态则造成的攻击伤害不低于{1}",
1672: "出手时若自身未满体力则吸取对手{0}点体力",

View File

@@ -16,6 +16,9 @@ import (
// processSkillAttack 处理技能攻击逻辑
func (f *FightC) processSkillAttack(attacker, defender *input.Input, skill *info.SkillEntity) {
if attacker == nil || defender == nil || skill == nil {
return
}
skill.AttackTimeC(attacker.Prop[5]) //计算命中
defender.Exec(func(effect input.Effect) bool { //计算闪避,然后修改对方命中),同时相当于计算属性无效这种
@@ -33,8 +36,8 @@ func (f *FightC) processSkillAttack(attacker, defender *input.Input, skill *info
var originalProps [2][6]int8
var originalPetInfo [2]model.PetInfo
//复制属性
originalProps[0], originalProps[1] = f.Our[0].Prop, f.Opp[0].Prop //先复制能力提升
originalPetInfo[0], originalPetInfo[1] = f.Our[0].CurPet[0].Info, f.Opp[0].CurPet[0].Info //先复制宠物信息
originalProps[0], originalProps[1] = attacker.Prop, defender.Prop
originalPetInfo[0], originalPetInfo[1] = attacker.CurPet[0].Info, defender.CurPet[0].Info
attacker.Exec(func(effect input.Effect) bool {
//计算变威力
effect.Ctx().SkillEntity = skill
@@ -55,8 +58,8 @@ func (f *FightC) processSkillAttack(attacker, defender *input.Input, skill *info
}
//还原属性
f.Our[0].Prop, f.Opp[0].Prop = originalProps[0], originalProps[1]
f.Our[0].CurPet[0].Info, f.Opp[0].CurPet[0].Info = originalPetInfo[0], originalPetInfo[1]
attacker.Prop, defender.Prop = originalProps[0], originalProps[1]
attacker.CurPet[0].Info, defender.CurPet[0].Info = originalPetInfo[0], originalPetInfo[1]
if attacker.IsCritical == 1 { //命中了才有暴击
//暴击破防
@@ -149,15 +152,13 @@ func (f *FightC) enterturn(firstAttack, secondAttack *action.SelectSkillAction)
})
})
if firstAttack != nil { //如果首技能是空的,说明都空过了
firstAttacker, _ := f.getSkillParticipants(firstAttack)
if firstAttacker != nil {
firstAttacker.Parseskill(firstAttack)
}
secondAttacker, _ := f.getSkillParticipants(secondAttack)
if secondAttacker != nil {
secondAttacker.Parseskill(secondAttack)
for _, skillAction := range []*action.SelectSkillAction{firstAttack, secondAttack} {
attackerInput, _ := f.getSkillParticipants(skillAction)
if attackerInput == nil {
continue
}
f.setActionAttackValue(skillAction)
attackerInput.Parseskill(skillAction)
}
f.Broadcast(func(fighter *input.Input) {
fighter.Exec(func(effect input.Effect) bool { //回合开始前
@@ -212,17 +213,26 @@ func (f *FightC) enterturn(firstAttack, secondAttack *action.SelectSkillAction)
for i := 0; i < 2; i++ {
var originalSkill *info.SkillEntity //原始技能
var currentSkill *info.SkillEntity //当前技能
var currentAction *action.SelectSkillAction
if i == 0 {
attacker, defender = f.First, f.Second
currentAction = firstAttack
attacker, defender = f.getSkillParticipants(firstAttack)
originalSkill = f.copySkill(firstAttack)
//先手阶段,先修复后手效果
f.Second.RecoverEffect()
} else {
attacker, defender = f.Second, f.First
currentAction = secondAttack
attacker, defender = f.getSkillParticipants(secondAttack)
originalSkill = f.copySkill(secondAttack)
//取消后手历史效果
f.Second.ReactvieEffect()
}
if attacker == nil {
attacker = f.First
}
if defender == nil {
defender = f.Second
}
currentSkill = originalSkill
defender.Exec(func(effect input.Effect) bool { //这个是能否使用技能
@@ -234,7 +244,7 @@ func (f *FightC) enterturn(firstAttack, secondAttack *action.SelectSkillAction)
return effect.ActionStart(firstAttack, secondAttack)
})
canUse := canUseSkill && action.CanUse(currentSkill) && attacker.CurPet[0].Info.Hp > 0
canUse := canUseSkill && action.CanUse(currentSkill) && attacker != nil && attacker.CurPet[0].Info.Hp > 0
if !canUse {
@@ -250,6 +260,7 @@ func (f *FightC) enterturn(firstAttack, secondAttack *action.SelectSkillAction)
//先手权不一定出手
} else {
f.setActionAttackValue(currentAction)
for _, effect := range attacker.EffectCache {
effect.IsFirst(true)
@@ -345,7 +356,7 @@ func (f *FightC) enterturn(firstAttack, secondAttack *action.SelectSkillAction)
}
}
})
f.Switch = make(map[uint32]*action.ActiveSwitchAction)
f.Switch = make(map[actionSlotKey]*action.ActiveSwitchAction)
if f.closefight && f.Info.Mode == info.BattleMode.PET_MELEE {
// f.Broadcast(func(fighter *input.Input) {
// if fighter.UserID != f.WinnerId {

View File

@@ -17,6 +17,7 @@ type BattlePetEntity struct {
Shield alpacadecimal.Decimal
DivineEnergy int
ControllerUserID uint32
FatalCrackLayers int
//*input.Input
//PType int

View File

@@ -9,6 +9,8 @@ import (
type ChangePetInfo struct {
// UserId 米米号野怪为0
UserId uint32 `json:"userId"`
// ActorIndex 当前发生切宠的战斗位
ActorIndex uint32 `json:"actorIndex"`
// PetId 切换上场的精灵编号
ID uint32 `fieldDesc:"当前对战精灵ID" `
// PetName 精灵名字固定16字节长度
@@ -75,6 +77,10 @@ type RPCFightStartinfo struct {
type FightPetInfo struct {
// 用户ID野怪为0
UserID uint32 `fieldDesc:"用户ID 野怪为0" `
// ActorIndex 当前战斗位下标
ActorIndex uint32 `json:"actorIndex"`
// ControllerUserID 当前战斗位绑定的操作者
ControllerUserID uint32 `json:"controllerUserId"`
// 当前对战精灵ID
ID uint32 `fieldDesc:"当前对战精灵ID" `
@@ -104,20 +110,21 @@ type AttackValueS struct {
func NewAttackValue(userid uint32) *model.AttackValue {
return &model.AttackValue{
userid,
0,
0,
0,
0,
0,
0,
0,
0,
[]model.SkillInfo{},
0,
[20]int8{},
[6]int8{},
0,
UserID: userid,
ActorIndex: 0,
TargetIndex: 0,
SkillID: 0,
AttackTime: 0,
LostHp: 0,
GainHp: 0,
RemainHp: 0,
MaxHp: 0,
State: 0,
SkillList: []model.SkillInfo{},
IsCritical: 0,
Status: [20]int8{},
Prop: [6]int8{},
Offensive: 0,
}
}
@@ -200,6 +207,9 @@ type NoteUseSkillOutboundInfo struct {
type FightStartOutboundInfo struct {
IsCanAuto uint32 `fieldDesc:"是否自动 默认给0 怀疑是自动战斗器使用的" `
InfoLen uint32 `struc:"sizeof=Infos"`
Infos []FightPetInfo
// 当前战斗精灵信息1前端通过userid判断是否为我方
Info1 FightPetInfo `fieldDesc:"当前战斗精灵的信息 可能不准.看前端代码是以userid来判断哪个结构体是我方的" serialize:"struct"`

View File

@@ -30,14 +30,14 @@ type FightC struct {
Opp []*input.Input // 敌方战斗位
OurPlayers []common.PlayerI // 我方操作者
OppPlayers []common.PlayerI // 敌方操作者
Switch map[uint32]*action.ActiveSwitchAction
Switch map[actionSlotKey]*action.ActiveSwitchAction
startl sync.Once
StartTime time.Time
actionMu sync.Mutex
actionNotify chan struct{}
acceptActions bool
pendingActions []action.BattleActionI // 待处理动作队列,同一玩家最多保留两段动作
pendingActions []action.BattleActionI // 待处理动作队列,同一战斗位最多保留一个动作
actionRound atomic.Uint32
quit chan struct{}
@@ -53,6 +53,25 @@ type FightC struct {
callback func(model.FightOverInfo)
}
type actionSlotKey struct {
PlayerID uint32
ActorIndex int
}
func newActionSlotKey(playerID uint32, actorIndex int) actionSlotKey {
return actionSlotKey{
PlayerID: playerID,
ActorIndex: actorIndex,
}
}
func actionSlotKeyFromAction(act action.BattleActionI) actionSlotKey {
if act == nil {
return actionSlotKey{}
}
return newActionSlotKey(act.GetPlayerID(), act.GetActorIndex())
}
func (f *FightC) primaryOur() *input.Input {
if len(f.Our) == 0 {
return nil
@@ -116,6 +135,20 @@ func (f *FightC) isOurPlayerID(userID uint32) bool {
return userID == f.ownerID
}
func (f *FightC) getSideInputs(userID uint32, isOpposite bool) []*input.Input {
isOur := f.isOurPlayerID(userID)
if isOpposite {
if isOur {
return f.Opp
}
return f.Our
}
if isOur {
return f.Our
}
return f.Opp
}
func (f *FightC) findInputByUserID(userID uint32) (*input.Input, bool) {
isOur := f.isOurPlayerID(userID)
if isOur {
@@ -131,17 +164,36 @@ func (f *FightC) findInputByUserID(userID uint32) (*input.Input, bool) {
}
func (f *FightC) getInputByUserID(userID uint32, index int, isOpposite bool) *input.Input {
isOur := f.isOurPlayerID(userID)
if isOpposite {
if isOur {
return f.selectInput(f.Opp, index)
return f.selectInput(f.getSideInputs(userID, isOpposite), index)
}
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
}
return f.selectInput(f.Our, index)
slots[newActionSlotKey(fighter.Player.GetInfo().UserID, actorIndex)] = struct{}{}
}
if isOur {
return f.selectInput(f.Our, index)
for actorIndex, fighter := range f.Opp {
if fighter == nil || fighter.Player == nil {
continue
}
slots[newActionSlotKey(fighter.Player.GetInfo().UserID, actorIndex)] = struct{}{}
}
return f.selectInput(f.Opp, index)
return slots
}
func (f *FightC) setActionAttackValue(act action.BattleActionI) {
if act == nil {
return
}
attacker := f.GetInputByAction(act, false)
if attacker == nil || attacker.AttackValue == nil {
return
}
attacker.AttackValue.ActorIndex = uint32(act.GetActorIndex())
attacker.AttackValue.TargetIndex = uint32(act.GetTargetIndex())
}
func (f *FightC) Ownerid() uint32 {
@@ -174,7 +226,14 @@ func (f *FightC) GetInputByAction(c action.BattleActionI, isOpposite bool) *inpu
// 玩家使用技能
func (f *FightC) GetCurrPET(c common.PlayerI) *info.BattlePetEntity {
in := f.GetInputByPlayer(c, false)
return f.GetCurrPETAt(c, 0)
}
func (f *FightC) GetCurrPETAt(c common.PlayerI, actorIndex int) *info.BattlePetEntity {
if c == nil {
return nil
}
in := f.getInputByUserID(c.GetInfo().UserID, actorIndex, false)
if in == nil {
return nil
}
@@ -293,10 +352,17 @@ func initfightready(in *input.Input) (model.FightUserInfo, []model.ReadyFightPet
// 被击败的ID
func (f *FightC) IsWin(c *input.Input) bool {
for _, v := range f.GetInputByPlayer(c.Player, true).AllPet {
if v.Alive() { //如果存活
return false
if c == nil || c.Player == nil {
return false
}
for _, sideInput := range f.getSideInputs(c.Player.GetInfo().UserID, true) {
if sideInput == nil {
continue
}
for _, v := range sideInput.AllPet {
if v.Alive() {
return false
}
}
}
return true

View File

@@ -7,6 +7,7 @@ import (
"blazing/cool"
"blazing/modules/player/model"
"context"
"sort"
"sync/atomic"
"blazing/logic/service/common"
@@ -45,20 +46,19 @@ func (f *FightC) battleLoop() {
}()
//fmt.Println("战斗开始精灵", f.Our[0].Player.GetInfo().PetList[0].CatchTime)
ourID := f.Our[0].Player.GetInfo().UserID
oppID := f.Opp[0].Player.GetInfo().UserID
//fmt.Println("开始收集玩家动作", waitr)
for !f.closefight {
f.Round++
actions := f.collectPlayerActions(ourID, oppID)
expectedSlots := f.expectedActionSlots()
actions := f.collectPlayerActions(expectedSlots)
if f.closefight {
break
}
f.resolveRound(actions[ourID], actions[oppID])
f.resolveRound(actions)
}
f.Broadcast(func(ff *input.Input) {
@@ -145,8 +145,8 @@ func (f *FightC) battleLoop() {
}
// 收集玩家动作(含超时判定)
func (f *FightC) collectPlayerActions(ourID, oppID uint32) map[uint32]action.BattleActionI {
actions := make(map[uint32]action.BattleActionI)
func (f *FightC) collectPlayerActions(expectedSlots map[actionSlotKey]struct{}) []action.BattleActionI {
actions := make(map[actionSlotKey]action.BattleActionI, len(expectedSlots))
f.openActionWindow()
defer f.closeActionWindow()
@@ -157,12 +157,12 @@ func (f *FightC) collectPlayerActions(ourID, oppID uint32) map[uint32]action.Bat
waitr := time.Duration(f.waittime)*time.Millisecond*10 + 30*time.Second
timeout := time.After(waitr)
for len(actions) < 2 {
for len(actions) < len(expectedSlots) {
select {
case <-f.quit:
f.closefight = true
return actions
return flattenActionMap(actions)
case <-f.actionNotify:
paction := f.nextAction()
@@ -170,18 +170,21 @@ func (f *FightC) collectPlayerActions(ourID, oppID uint32) map[uint32]action.Bat
continue
}
pid := paction.GetPlayerID()
if pid != ourID && pid != oppID {
key := actionSlotKeyFromAction(paction)
if _, ok := expectedSlots[key]; !ok {
continue
}
selfinput := f.GetInputByAction(paction, false)
if selfinput == nil {
continue
}
if ret, ok := paction.(*action.ActiveSwitchAction); ok {
//正常结束可以切换,以及死切后还能再切一次
if selfinput.CanChange == 0 {
if selfinput.CurPet[0].Info.Hp > 0 { //非死亡切换
currentPet := selfinput.PrimaryCurPet()
if currentPet != nil && currentPet.Info.Hp > 0 { //非死亡切换
selfinput.CanChange = 1
f.Broadcast(func(ff *input.Input) {
@@ -199,11 +202,17 @@ func (f *FightC) collectPlayerActions(ourID, oppID uint32) map[uint32]action.Bat
}
// oldpet := selfinput.CurPet[0]
// InitAttackValue := *selfinput.AttackValue
selfinput.CurPet[0], ret.Reason = selfinput.GetPet(ret.Cid)
nextPet, reason := selfinput.GetPet(ret.Cid)
if nextPet == nil {
continue
}
selfinput.SetCurPetAt(0, nextPet)
ret.Reason = reason
ret.Reason.ActorIndex = uint32(ret.ActorIndex)
selfinput.Player.SendPackCmd(2407, &ret.Reason)
f.Switch[selfinput.UserID] = ret
f.Switch[key] = ret
selfinput.InitAttackValue() //切换精灵消除能力提升
//这时候精灵已经切换过了,可以直接给新精灵加效果
@@ -220,8 +229,8 @@ func (f *FightC) collectPlayerActions(ourID, oppID uint32) map[uint32]action.Bat
if selfinput.CanChange == 2 {
selfinput.CanChange = 0
if f.Info.Status == info.BattleMode.FIGHT_WITH_NPC && pid == 0 {
f.Switch = make(map[uint32]*action.ActiveSwitchAction)
if f.Info.Status == info.BattleMode.FIGHT_WITH_NPC && paction.GetPlayerID() == 0 {
f.Switch = make(map[actionSlotKey]*action.ActiveSwitchAction)
f.Our[0].Player.SendPackCmd(2407, &ret.Reason)
//println("AI出手死切")
go f.Opp[0].GetAction() //boss出手后获取出招
@@ -235,51 +244,78 @@ func (f *FightC) collectPlayerActions(ourID, oppID uint32) map[uint32]action.Bat
} else {
// println("玩家执行释放技能动作:", pid, paction.(*action.SelectSkillAction).Info.ID)
if selfinput.CurPet[0].Info.Hp <= 0 { //0血执行非切换动作
currentPet := selfinput.PrimaryCurPet()
if currentPet == nil || currentPet.Info.Hp <= 0 { //0血执行非切换动作
//todo 记录异常操作
cool.Logger.Print(context.TODO(), "玩家执行了异常操作当前精灵血量为0不能执行非切换动作", pid)
cool.Logger.Print(context.TODO(), "玩家执行了异常操作当前精灵血量为0不能执行非切换动作", paction.GetPlayerID())
continue
}
if selfinput.CanChange == 1 { //非被动死亡情况下,不能执行额外动作,0允许切2是死亡可以额外动作
cool.Logger.Print(context.TODO(), "玩家执行了异常操作,切换后二次释放技能,不能执行非切换动作", pid)
cool.Logger.Print(context.TODO(), "玩家执行了异常操作,切换后二次释放技能,不能执行非切换动作", paction.GetPlayerID())
continue
}
}
actions[pid] = paction
actions[key] = paction
//fmt.Println("玩家执行动作:", pid, paction.Priority())
case <-timeout:
r := f.handleTimeout(ourID, oppID, actions)
r := f.handleTimeout(expectedSlots, actions)
if r {
return actions
return flattenActionMap(actions)
}
}
}
return actions
return flattenActionMap(actions)
}
// 超时处理逻辑
func (f *FightC) handleTimeout(ourID, oppID uint32, actions map[uint32]action.BattleActionI) bool {
func (f *FightC) handleTimeout(expectedSlots map[actionSlotKey]struct{}, actions map[actionSlotKey]action.BattleActionI) bool {
if f.Info.Status == info.BattleMode.FIGHT_WITH_NPC {
// f.WinnerId = 0
go f.UseSkill(f.Our[0].Player, 0) //boss出手后获取出招
for key := range expectedSlots {
if _, exists := actions[key]; exists || !f.isOurPlayerID(key.PlayerID) {
continue
}
player := f.getPlayerByID(key.PlayerID)
if player != nil {
go f.UseSkillAt(player, 0, key.ActorIndex, 0)
}
}
return false
} else {
var pid uint32
for _, pid = range []uint32{ourID, oppID} {
if _, exists := actions[pid]; exists {
missingOur := false
missingOpp := false
for key := range expectedSlots {
if _, exists := actions[key]; exists {
continue
}
if f.isOurPlayerID(key.PlayerID) {
missingOur = true
continue
}
missingOpp = true
}
switch {
case missingOur && !missingOpp:
if player := f.primaryOppPlayer(); player != nil {
f.WinnerId = player.GetInfo().UserID
}
case missingOpp && !missingOur:
if player := f.primaryOurPlayer(); player != nil {
f.WinnerId = player.GetInfo().UserID
}
default:
for _, act := range actions {
f.WinnerId = act.GetPlayerID()
break
}
if f.WinnerId == 0 {
f.WinnerId = f.ownerID
}
}
//获胜方是已经出招的
f.WinnerId = f.GetInputByPlayer(f.getPlayerByID(pid), false).Player.GetInfo().UserID
f.Reason = model.BattleOverReason.PlayerOVerTime
f.closefight = true
return true
@@ -287,10 +323,111 @@ func (f *FightC) handleTimeout(ourID, oppID uint32, actions map[uint32]action.Ba
}
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 {
if act != nil {
flattened = append(flattened, act)
}
}
return flattened
}
func (f *FightC) actionSpeed(act action.BattleActionI) alpacadecimal.Decimal {
if act == nil {
return alpacadecimal.Zero
}
attacker := f.GetInputByAction(act, false)
if attacker == nil {
return alpacadecimal.Zero
}
return attacker.GetProp(4)
}
func (f *FightC) sortActions(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.Priority() != b.Priority() {
return a.Priority() > b.Priority()
}
if speedA, speedB := f.actionSpeed(a), f.actionSpeed(b); speedA.Cmp(speedB) != 0 {
return speedA.Cmp(speedB) > 0
}
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()
})
}
func (f *FightC) buildRoundActionPairs(actions []action.BattleActionI) []roundActionPair {
remaining := append([]action.BattleActionI(nil), actions...)
f.sortActions(remaining)
used := make([]bool, len(remaining))
pairs := make([]roundActionPair, 0, len(remaining))
for i, act := range remaining {
if used[i] || act == nil {
continue
}
used[i] = true
pair := roundActionPair{}
if f.isOurPlayerID(act.GetPlayerID()) {
pair.our = act
} else {
pair.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
}
// 根据动作类型执行一回合结算
func (f *FightC) resolveRound(p1Action, p2Action action.BattleActionI) {
if p1Action == nil || p2Action == nil {
cool.Logger.Debug(context.Background(), "某方未选择动作,自动跳过结算")
func (f *FightC) resolveRound(actions []action.BattleActionI) {
if len(actions) == 0 {
cool.Logger.Debug(context.Background(), "当前回合没有可执行动作,自动跳过结算")
return
}
for _, pair := range f.buildRoundActionPairs(actions) {
if f.closefight {
return
}
f.resolveActionPair(pair.our, pair.opp)
}
}
func (f *FightC) resolveActionPair(p1Action, p2Action action.BattleActionI) {
if p1Action == nil && p2Action == nil {
return
}
@@ -324,11 +461,15 @@ func (f *FightC) handleActiveSwitchAction(_ *action.ActiveSwitchAction, otherAct
// handleUseItemAction 处理使用道具动作
func (f *FightC) handleUseItemAction(itemAction *action.UseItemAction, otherAction action.BattleActionI) {
f.setActionAttackValue(itemAction)
f.handleItemAction(itemAction)
input := f.GetInputByAction(itemAction, false)
if input.CurPet[0].Info.Hp <= 0 {
input.CurPet[0].Info.Hp = 1
if input == nil {
return
}
if currentPet := input.PrimaryCurPet(); currentPet != nil && currentPet.Info.Hp <= 0 {
currentPet.Info.Hp = 1
}
if skillAction, ok := otherAction.(*action.SelectSkillAction); ok {
@@ -346,28 +487,32 @@ func (f *FightC) handleUseItemAction(itemAction *action.UseItemAction, otherActi
// 使用道具的逻辑封装
func (f *FightC) handleItemAction(a *action.UseItemAction) {
source := f.GetInputByAction(a, false)
target := f.GetInputByAction(a, true)
if source == nil {
return
}
item, ok := xmlres.ItemsMAP[int(a.ItemID)]
if !ok {
return
}
r := f.GetInputByAction(a, false).Player.(*player.Player).Service.Item.CheakItem(uint32(a.ItemID))
r := source.Player.(*player.Player).Service.Item.CheakItem(uint32(a.ItemID))
if r <= 0 {
return
}
f.GetInputByAction(a, false).Player.(*player.Player).Service.Item.UPDATE(a.ItemID, -1)
source.Player.(*player.Player).Service.Item.UPDATE(a.ItemID, -1)
switch {
case gconv.Int(item.Bonus) != 0:
if f.Opp[0].CanCapture > 0 { //可以捕捉
f.Opp[0].CurPet[0].CatchRate = f.Opp[0].CanCapture
ok, _ := f.Our[0].Capture(f.Opp[0].CurPet[0], a.ItemID, -1)
our := f.Our[0].Player.(*player.Player)
if target != nil && target.CanCapture > 0 && target.PrimaryCurPet() != nil { //可以捕捉
target.PrimaryCurPet().CatchRate = target.CanCapture
ok, _ := source.Capture(target.PrimaryCurPet(), a.ItemID, -1)
our := source.Player.(*player.Player)
if ok {
r := input.GetFunc(int64(item.ID))
if r != nil {
r.Exec(f, &f.Opp[0].Player.GetInfo().PetList[0])
r.Exec(f, &target.Player.GetInfo().PetList[0])
}
f.Reason = model.BattleOverReason.Cacthok
f.closefight = true
@@ -378,24 +523,32 @@ func (f *FightC) handleItemAction(a *action.UseItemAction) {
}
case gconv.Int(item.HP) != 0:
addhp := item.HP
f.GetInputByAction(a, false).Heal(f.GetInputByAction(a, false), a, alpacadecimal.NewFromInt(int64(addhp)))
source.Heal(source, a, alpacadecimal.NewFromInt(int64(addhp)))
f.Broadcast(func(ff *input.Input) {
currentPet := source.PrimaryCurPet()
if currentPet == nil {
return
}
ff.Player.SendPackCmd(2406, &info.UsePetIteminfo{
UserID: f.GetInputByAction(a, false).UserID,
UserID: source.UserID,
ChangeHp: int32(addhp),
ItemID: uint32(item.ID),
UserHp: uint32(f.GetInputByAction(a, false).CurPet[0].Info.Hp),
UserHp: uint32(currentPet.Info.Hp),
})
})
case gconv.Int(item.PP) != 0:
f.GetInputByAction(a, false).HealPP(item.PP)
source.HealPP(item.PP)
f.Broadcast(func(ff *input.Input) {
currentPet := source.PrimaryCurPet()
if currentPet == nil {
return
}
ff.Player.SendPackCmd(2406, &info.UsePetIteminfo{
UserID: f.GetInputByAction(a, false).UserID,
UserID: source.UserID,
ItemID: uint32(item.ID),
UserHp: uint32(f.GetInputByAction(a, false).CurPet[0].Info.Hp),
UserHp: uint32(currentPet.Info.Hp),
})
})
@@ -445,8 +598,15 @@ func (f *FightC) handleSkillActions(a1, a2 action.BattleActionI) {
// 根据玩家ID返回对应对象
func (f *FightC) getPlayerByID(id uint32) common.PlayerI {
if id == f.Our[0].Player.GetInfo().UserID {
return f.Our[0].Player
for _, player := range f.OurPlayers {
if player != nil && player.GetInfo().UserID == id {
return player
}
}
return f.Opp[0].Player
for _, player := range f.OppPlayers {
if player != nil && player.GetInfo().UserID == id {
return player
}
}
return nil
}

View File

@@ -21,7 +21,7 @@ func NewFight(p1, p2 common.PlayerI, b1, b2 []model.PetInfo, fn func(model.Fight
f.OurPlayers = []common.PlayerI{p1}
f.OppPlayers = []common.PlayerI{p2}
f.Switch = make(map[uint32]*action.ActiveSwitchAction)
f.Switch = make(map[actionSlotKey]*action.ActiveSwitchAction)
f.callback = fn //战斗结束的回调
f.quit = make(chan struct{})
f.over = make(chan struct{})
@@ -58,7 +58,6 @@ func NewFight(p1, p2 common.PlayerI, b1, b2 []model.PetInfo, fn func(model.Fight
ff.SetOPP(f.GetInputByPlayer(ff.Player, true))
})
f.FightStartOutboundInfo = f.buildFightStartInfo()
if f.Info.Status == info.BattleMode.FIGHT_WITH_NPC {
f.Opp[0].Finished = true //PVE 默认boss数据直接加载完成
@@ -67,11 +66,10 @@ func NewFight(p1, p2 common.PlayerI, b1, b2 []model.PetInfo, fn func(model.Fight
if f.Opp[0].Player.(*player.AI_player).CanCapture > 0 {
f.Opp[0].CanCapture = f.Opp[0].Player.(*player.AI_player).CanCapture
f.FightStartOutboundInfo.Info2.Catchable = 1 //可以捕捉就置1
}
f.Opp[0].AttackValue.Prop = f.Opp[0].Player.(*player.AI_player).Prop
f.FightStartOutboundInfo.Info2.Prop = f.Opp[0].AttackValue.Prop
}
f.FightStartOutboundInfo = f.buildFightStartInfo()
f.Broadcast(func(ff *input.Input) {