From 3a7f5931050d5dce41898317683851651f5021c3 Mon Sep 17 00:00:00 2001 From: xinian Date: Sat, 11 Apr 2026 22:22:23 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20=E4=BF=AE=E5=A4=8D=20Effect201=20?= =?UTF-8?q?=E5=9C=A8=E5=8D=95=E4=BA=BA=E6=88=98=E6=96=97=E4=B8=AD=E8=AF=AF?= =?UTF-8?q?=E7=94=9F=E6=95=88=E7=9A=84=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- logic/service/fight/effect/none.go | 4 ++ logic/service/fight/effect/none_test.go | 70 ++++++++++++++++++++ logic/service/fight/input/team.go | 88 ++++++++++++++++++++++--- logic/service/fight/input/team_test.go | 57 ++++++++++++++++ 4 files changed, 211 insertions(+), 8 deletions(-) create mode 100644 logic/service/fight/effect/none_test.go diff --git a/logic/service/fight/effect/none.go b/logic/service/fight/effect/none.go index 270b48973..75cc909be 100644 --- a/logic/service/fight/effect/none.go +++ b/logic/service/fight/effect/none.go @@ -110,6 +110,10 @@ func (e *Effect201) OnSkill() bool { return true } + if !carrier.IsMultiInputBattle() { + return true + } + team := carrier.Team if len(team) == 0 { team = []*input.Input{carrier} diff --git a/logic/service/fight/effect/none_test.go b/logic/service/fight/effect/none_test.go new file mode 100644 index 000000000..bd5fab3f4 --- /dev/null +++ b/logic/service/fight/effect/none_test.go @@ -0,0 +1,70 @@ +package effect + +import ( + "testing" + + fightinfo "blazing/logic/service/fight/info" + "blazing/logic/service/fight/input" + "blazing/modules/player/model" +) + +func newEffect201TestInput(hp, maxHP uint32) *input.Input { + in := &input.Input{ + CurPet: []*fightinfo.BattlePetEntity{{ + Info: model.PetInfo{ + Hp: hp, + MaxHp: maxHP, + }, + }}, + } + in.AttackValue = fightinfo.NewAttackValue(0) + return in +} + +func TestEffect201HealAllIgnoredInSingleInputBattle(t *testing.T) { + carrier := newEffect201TestInput(40, 100) + opponent := newEffect201TestInput(60, 100) + carrier.Team = []*input.Input{carrier} + carrier.OppTeam = []*input.Input{opponent} + + eff := &Effect201{} + eff.SetArgs(carrier, 1, 2) + eff.EffectNode.EffectContextHolder.Ctx = input.Ctx{ + LegacySides: input.LegacySides{Our: carrier, Opp: opponent}, + EffectBinding: input.EffectBinding{Carrier: carrier, Source: carrier}, + } + + if !eff.OnSkill() { + t.Fatalf("expected effect to finish successfully") + } + if got := carrier.CurrentPet().Info.Hp; got != 40 { + t.Fatalf("expected single-input full-team heal to be ignored, got hp %d", got) + } +} + +func TestEffect201HealAllWorksInMultiInputBattle(t *testing.T) { + carrier := newEffect201TestInput(40, 100) + ally := newEffect201TestInput(10, 80) + opponent := newEffect201TestInput(60, 100) + carrier.Team = []*input.Input{carrier, ally} + carrier.OppTeam = []*input.Input{opponent} + ally.Team = carrier.Team + ally.OppTeam = carrier.OppTeam + + eff := &Effect201{} + eff.SetArgs(carrier, 1, 2) + eff.EffectNode.EffectContextHolder.Ctx = input.Ctx{ + LegacySides: input.LegacySides{Our: carrier, Opp: opponent}, + EffectBinding: input.EffectBinding{Carrier: carrier, Source: carrier}, + } + + if !eff.OnSkill() { + t.Fatalf("expected effect to finish successfully") + } + if got := carrier.CurrentPet().Info.Hp; got != 90 { + t.Fatalf("expected carrier hp 90 after full-team heal, got %d", got) + } + if got := ally.CurrentPet().Info.Hp; got != 50 { + t.Fatalf("expected ally hp 50 after full-team heal, got %d", got) + } +} diff --git a/logic/service/fight/input/team.go b/logic/service/fight/input/team.go index 97b0be6bc..725d9dc12 100644 --- a/logic/service/fight/input/team.go +++ b/logic/service/fight/input/team.go @@ -2,6 +2,40 @@ package input import "github.com/gogf/gf/v2/util/grand" +func compactSlots(slots []*Input) []*Input { + if len(slots) == 0 { + return nil + } + ret := make([]*Input, 0, len(slots)) + for _, slot := range slots { + if slot == nil { + continue + } + ret = append(ret, slot) + } + return ret +} + +func uniqueControllerCount(slots []*Input) int { + if len(slots) == 0 { + return 0 + } + seen := make(map[uint32]struct{}, len(slots)) + count := 0 + for _, slot := range slots { + if slot == nil || slot.Player == nil { + continue + } + userID := slot.Player.GetInfo().UserID + if _, ok := seen[userID]; ok { + continue + } + seen[userID] = struct{}{} + count++ + } + return count +} + // TeamSlots 返回当前输入所在阵营的全部有效战斗位视图。 func (our *Input) TeamSlots() []*Input { if our == nil { @@ -10,14 +44,7 @@ func (our *Input) TeamSlots() []*Input { if len(our.Team) == 0 { return []*Input{our} } - slots := make([]*Input, 0, len(our.Team)) - for _, teammate := range our.Team { - if teammate == nil { - continue - } - slots = append(slots, teammate) - } - return slots + return compactSlots(our.Team) } // TeamSlotIndex 返回当前输入在本阵营中的原始站位下标。 @@ -69,6 +96,51 @@ func (our *Input) HasLivingTeammate() bool { return len(our.LivingTeammates()) > 0 } +// TeamInputCount 返回当前阵营有效 input 数量。 +func (our *Input) TeamInputCount() int { + return len(our.TeamSlots()) +} + +// OpponentInputCount 返回敌方阵营有效 input 数量。 +func (our *Input) OpponentInputCount() int { + if our == nil { + return 0 + } + if len(our.OppTeam) == 0 { + if our.Opp != nil { + return 1 + } + return 0 + } + return len(compactSlots(our.OppTeam)) +} + +// IsMultiInputSide 判断当前阵营是否为多 input。 +func (our *Input) IsMultiInputSide() bool { + return our.TeamInputCount() > 1 +} + +// IsMultiInputBattle 判断当前战斗是否包含多 input 站位。 +func (our *Input) IsMultiInputBattle() bool { + if our == nil { + return false + } + return our.TeamInputCount() > 1 || our.OpponentInputCount() > 1 +} + +// TeamControllerCount 返回当前阵营实际操作者数量。 +func (our *Input) TeamControllerCount() int { + if our == nil { + return 0 + } + return uniqueControllerCount(our.TeamSlots()) +} + +// IsSingleControllerMultiInputSide 判断当前阵营是否为“单人控制的多 input”。 +func (our *Input) IsSingleControllerMultiInputSide() bool { + return our.IsMultiInputSide() && our.TeamControllerCount() <= 1 +} + // TeamSlotAt 返回指定己方站位。 func (our *Input) TeamSlotAt(index int) *Input { if our == nil { diff --git a/logic/service/fight/input/team_test.go b/logic/service/fight/input/team_test.go index 1a4bd469c..297ec6c34 100644 --- a/logic/service/fight/input/team_test.go +++ b/logic/service/fight/input/team_test.go @@ -3,10 +3,32 @@ package input import ( "testing" + "blazing/common/socket/errorcode" + "blazing/logic/service/common" fightinfo "blazing/logic/service/fight/info" + spaceinfo "blazing/logic/service/space/info" "blazing/modules/player/model" ) +type teamTestPlayer struct { + info model.PlayerInfo + fightInfo fightinfo.Fightinfo +} + +func (p *teamTestPlayer) ApplyPetDisplayInfo(*spaceinfo.SimpleInfo) {} +func (p *teamTestPlayer) GetPlayerCaptureContext() *fightinfo.PlayerCaptureContext { return nil } +func (p *teamTestPlayer) Roll(int, int) (bool, float64, float64) { return false, 0, 0 } +func (p *teamTestPlayer) Getfightinfo() fightinfo.Fightinfo { return p.fightInfo } +func (p *teamTestPlayer) ItemAdd(int64, int64) bool { return false } +func (p *teamTestPlayer) GetInfo() *model.PlayerInfo { return &p.info } +func (p *teamTestPlayer) InvitePlayer(common.PlayerI) {} +func (p *teamTestPlayer) SetFightC(common.FightI) {} +func (p *teamTestPlayer) QuitFight() {} +func (p *teamTestPlayer) MessWin(bool) {} +func (p *teamTestPlayer) CanFight() errorcode.ErrorCode { return 0 } +func (p *teamTestPlayer) SendPackCmd(uint32, any) {} +func (p *teamTestPlayer) GetPetInfo(uint32) []model.PetInfo { return nil } + func TestLivingTeammatesFiltersSelfAndDeadSlots(t *testing.T) { owner := &Input{CurPet: []*fightinfo.BattlePetEntity{{Info: model.PetInfo{Hp: 10}}}} aliveMate := &Input{CurPet: []*fightinfo.BattlePetEntity{{Info: model.PetInfo{Hp: 5}}}} @@ -58,3 +80,38 @@ func TestRandomOpponentSlotIndexPrefersLivingTarget(t *testing.T) { t.Fatalf("expected opponent slot 1 to return live opponent") } } + +func TestInputModeHelpers(t *testing.T) { + playerA := &teamTestPlayer{info: model.PlayerInfo{UserID: 1001}} + playerB := &teamTestPlayer{info: model.PlayerInfo{UserID: 1002}} + + solo := &Input{} + soloMate := &Input{} + groupMate := &Input{} + + solo.Player = playerA + soloMate.Player = playerA + groupMate.Player = playerB + + solo.Team = []*Input{solo} + solo.OppTeam = []*Input{{}} + if solo.IsMultiInputBattle() { + t.Fatalf("expected single-input battle to be false") + } + + solo.Team = []*Input{solo, soloMate} + if !solo.IsMultiInputBattle() { + t.Fatalf("expected multi-input battle to be true") + } + if !solo.IsSingleControllerMultiInputSide() { + t.Fatalf("expected same-controller double side to be single-controller multi-input") + } + + solo.Team = []*Input{solo, groupMate} + if solo.TeamControllerCount() != 2 { + t.Fatalf("expected group side controller count 2, got %d", solo.TeamControllerCount()) + } + if solo.IsSingleControllerMultiInputSide() { + t.Fatalf("expected grouped side not to be single-controller multi-input") + } +}