fix: 修复 Effect201 在单人战斗中误生效的问题
All checks were successful
ci/woodpecker/push/my-first-workflow Pipeline was successful
All checks were successful
ci/woodpecker/push/my-first-workflow Pipeline was successful
This commit is contained in:
@@ -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}
|
||||
|
||||
70
logic/service/fight/effect/none_test.go
Normal file
70
logic/service/fight/effect/none_test.go
Normal file
@@ -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)
|
||||
}
|
||||
}
|
||||
@@ -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 {
|
||||
|
||||
@@ -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")
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user