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
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if !carrier.IsMultiInputBattle() {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
team := carrier.Team
|
team := carrier.Team
|
||||||
if len(team) == 0 {
|
if len(team) == 0 {
|
||||||
team = []*input.Input{carrier}
|
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"
|
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 返回当前输入所在阵营的全部有效战斗位视图。
|
// TeamSlots 返回当前输入所在阵营的全部有效战斗位视图。
|
||||||
func (our *Input) TeamSlots() []*Input {
|
func (our *Input) TeamSlots() []*Input {
|
||||||
if our == nil {
|
if our == nil {
|
||||||
@@ -10,14 +44,7 @@ func (our *Input) TeamSlots() []*Input {
|
|||||||
if len(our.Team) == 0 {
|
if len(our.Team) == 0 {
|
||||||
return []*Input{our}
|
return []*Input{our}
|
||||||
}
|
}
|
||||||
slots := make([]*Input, 0, len(our.Team))
|
return compactSlots(our.Team)
|
||||||
for _, teammate := range our.Team {
|
|
||||||
if teammate == nil {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
slots = append(slots, teammate)
|
|
||||||
}
|
|
||||||
return slots
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// TeamSlotIndex 返回当前输入在本阵营中的原始站位下标。
|
// TeamSlotIndex 返回当前输入在本阵营中的原始站位下标。
|
||||||
@@ -69,6 +96,51 @@ func (our *Input) HasLivingTeammate() bool {
|
|||||||
return len(our.LivingTeammates()) > 0
|
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 返回指定己方站位。
|
// TeamSlotAt 返回指定己方站位。
|
||||||
func (our *Input) TeamSlotAt(index int) *Input {
|
func (our *Input) TeamSlotAt(index int) *Input {
|
||||||
if our == nil {
|
if our == nil {
|
||||||
|
|||||||
@@ -3,10 +3,32 @@ package input
|
|||||||
import (
|
import (
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"blazing/common/socket/errorcode"
|
||||||
|
"blazing/logic/service/common"
|
||||||
fightinfo "blazing/logic/service/fight/info"
|
fightinfo "blazing/logic/service/fight/info"
|
||||||
|
spaceinfo "blazing/logic/service/space/info"
|
||||||
"blazing/modules/player/model"
|
"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) {
|
func TestLivingTeammatesFiltersSelfAndDeadSlots(t *testing.T) {
|
||||||
owner := &Input{CurPet: []*fightinfo.BattlePetEntity{{Info: model.PetInfo{Hp: 10}}}}
|
owner := &Input{CurPet: []*fightinfo.BattlePetEntity{{Info: model.PetInfo{Hp: 10}}}}
|
||||||
aliveMate := &Input{CurPet: []*fightinfo.BattlePetEntity{{Info: model.PetInfo{Hp: 5}}}}
|
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")
|
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