All checks were successful
ci/woodpecker/push/my-first-workflow Pipeline was successful
feat(fight): 添加旧组队协议支持并优化战斗系统 - 实现了旧组队协议相关功能,包括GroupReadyFightFinish、GroupUseSkill、 GroupUseItem、GroupChangePet和GroupEscape方法 - 新增组队战斗相关的入站信息结构体定义 - 实现了组队BOSS战斗逻辑,添加groupBossSlotLimit常量 - 重构宠物技能设置逻辑,调整金币消耗时机 - 优化战斗循环逻辑,添加对无行动槽位的处理 - 改进AI行动逻辑,增加多位置目标选择
223 lines
5.0 KiB
Go
223 lines
5.0 KiB
Go
package input
|
|
|
|
import (
|
|
"blazing/logic/service/fight/info"
|
|
"blazing/logic/service/player"
|
|
configmodel "blazing/modules/config/model"
|
|
playermodel "blazing/modules/player/model"
|
|
"strings"
|
|
|
|
"github.com/gogf/gf/v2/util/grand"
|
|
)
|
|
|
|
// Shuffle 打乱切片顺序,使用 Fisher-Yates 洗牌算法,泛型支持任意类型
|
|
func Shuffle[T any](slice []T) {
|
|
for i := len(slice) - 1; i > 0; i-- {
|
|
j := grand.Intn(i + 1)
|
|
slice[i], slice[j] = slice[j], slice[i]
|
|
}
|
|
}
|
|
|
|
func (our *Input) GetAction() {
|
|
actorIndex := our.TeamSlotIndex()
|
|
targetIndex := our.RandomOpponentSlotIndex()
|
|
target := our.OpponentSlotAt(targetIndex)
|
|
|
|
next := our.Exec(func(t Effect) bool {
|
|
return t.HookAction()
|
|
})
|
|
|
|
scriptCtx := buildBossHookActionContext(our, target, next)
|
|
if aiPlayer, ok := our.Player.(*player.AI_player); ok && aiPlayer.BossScript != "" {
|
|
scriptBoss := &configmodel.BossConfig{Script: aiPlayer.BossScript}
|
|
nextByScript, err := scriptBoss.RunHookActionScript(scriptCtx)
|
|
if err != nil {
|
|
return
|
|
}
|
|
next = nextByScript
|
|
}
|
|
|
|
if !next {
|
|
return
|
|
}
|
|
|
|
if applyBossScriptAction(our, scriptCtx, actorIndex, targetIndex) {
|
|
return
|
|
}
|
|
|
|
selfPet := our.FightC.GetCurrPETAt(our.Player, actorIndex)
|
|
if selfPet == nil {
|
|
return
|
|
}
|
|
if selfPet.Info.Hp <= 0 {
|
|
for _, v := range our.AllPet {
|
|
if v.Info.Hp > 0 {
|
|
our.FightC.ChangePetAt(our.Player, v.Info.CatchTime, actorIndex)
|
|
return
|
|
}
|
|
}
|
|
return
|
|
}
|
|
|
|
skills := selfPet.Skills
|
|
if len(skills) == 0 {
|
|
return
|
|
}
|
|
|
|
var usedskill *info.SkillEntity
|
|
for _, s := range skills {
|
|
if s == nil {
|
|
continue
|
|
}
|
|
if !s.CanUse() {
|
|
continue
|
|
}
|
|
if target == nil {
|
|
continue
|
|
}
|
|
s.DamageValue = our.CalculatePower(target, s)
|
|
|
|
oppPet := target.CurrentPet()
|
|
if oppPet == nil {
|
|
continue
|
|
}
|
|
if s.DamageValue.Cmp(oppPet.GetHP()) != -1 {
|
|
if usedskill != nil {
|
|
if s.DamageValue.Cmp(usedskill.DamageValue) != -1 {
|
|
usedskill = s
|
|
}
|
|
} else {
|
|
usedskill = s
|
|
}
|
|
}
|
|
}
|
|
|
|
Shuffle(skills)
|
|
if usedskill == nil {
|
|
for _, s := range skills {
|
|
if s == nil {
|
|
continue
|
|
}
|
|
if !s.CanUse() {
|
|
continue
|
|
}
|
|
usedskill = s
|
|
}
|
|
}
|
|
|
|
if usedskill != nil {
|
|
our.FightC.UseSkillAt(our.Player, uint32(usedskill.XML.ID), actorIndex, targetIndex)
|
|
} else {
|
|
our.FightC.UseSkillAt(our.Player, 0, actorIndex, targetIndex)
|
|
}
|
|
}
|
|
|
|
func buildBossHookActionContext(our, opponent *Input, hookAction bool) *configmodel.BossHookActionContext {
|
|
ctx := &configmodel.BossHookActionContext{
|
|
HookAction: hookAction,
|
|
Action: "auto",
|
|
}
|
|
ctx.UseSkillFn = func(skillID uint32) {
|
|
ctx.Action = "skill"
|
|
ctx.SkillID = skillID
|
|
}
|
|
ctx.SwitchPetFn = func(catchTime uint32) {
|
|
ctx.Action = "switch"
|
|
ctx.CatchTime = catchTime
|
|
}
|
|
|
|
if our == nil || our.FightC == nil || our.Player == nil {
|
|
return ctx
|
|
}
|
|
|
|
overInfo := our.FightC.GetOverInfo()
|
|
ctx.Round = overInfo.Round
|
|
ctx.IsFirst = our.FightC.IsFirst(our.Player)
|
|
|
|
if selfPet := our.CurrentPet(); selfPet != nil {
|
|
ctx.Our = &configmodel.BossHookPetContext{
|
|
PetID: selfPet.Info.ID,
|
|
CatchTime: selfPet.Info.CatchTime,
|
|
Hp: selfPet.Info.Hp,
|
|
MaxHp: selfPet.Info.MaxHp,
|
|
}
|
|
ctx.Skills = make([]configmodel.BossHookSkillContext, 0, len(selfPet.Skills))
|
|
for _, s := range selfPet.Skills {
|
|
if s == nil || s.Info == nil {
|
|
continue
|
|
}
|
|
ctx.Skills = append(ctx.Skills, configmodel.BossHookSkillContext{
|
|
SkillID: s.Info.ID,
|
|
PP: s.Info.PP,
|
|
CanUse: s.CanUse(),
|
|
})
|
|
}
|
|
}
|
|
|
|
if our.AttackValue != nil {
|
|
ctx.OurAttack = convertAttackValue(our.AttackValue)
|
|
}
|
|
if opponent != nil {
|
|
if oppPet := opponent.CurrentPet(); oppPet != nil {
|
|
ctx.Opp = &configmodel.BossHookPetContext{
|
|
PetID: oppPet.Info.ID,
|
|
CatchTime: oppPet.Info.CatchTime,
|
|
Hp: oppPet.Info.Hp,
|
|
MaxHp: oppPet.Info.MaxHp,
|
|
}
|
|
}
|
|
if opponent.AttackValue != nil {
|
|
ctx.OppAttack = convertAttackValue(opponent.AttackValue)
|
|
}
|
|
}
|
|
|
|
return ctx
|
|
}
|
|
|
|
func convertAttackValue(src *playermodel.AttackValue) *configmodel.BossHookAttackContext {
|
|
if src == nil {
|
|
return nil
|
|
}
|
|
status := make([]int8, len(src.Status))
|
|
for i := range src.Status {
|
|
status[i] = src.Status[i]
|
|
}
|
|
prop := make([]int8, len(src.Prop))
|
|
for i := range src.Prop {
|
|
prop[i] = src.Prop[i]
|
|
}
|
|
|
|
return &configmodel.BossHookAttackContext{
|
|
SkillID: src.SkillID,
|
|
AttackTime: src.AttackTime,
|
|
IsCritical: src.IsCritical,
|
|
LostHp: src.LostHp,
|
|
GainHp: src.GainHp,
|
|
RemainHp: src.RemainHp,
|
|
MaxHp: src.MaxHp,
|
|
State: src.State,
|
|
Offensive: src.Offensive,
|
|
Status: status,
|
|
Prop: prop,
|
|
}
|
|
}
|
|
|
|
func applyBossScriptAction(our *Input, ctx *configmodel.BossHookActionContext, actorIndex, targetIndex int) bool {
|
|
if our == nil || ctx == nil {
|
|
return false
|
|
}
|
|
|
|
switch strings.ToLower(strings.TrimSpace(ctx.Action)) {
|
|
case "", "auto":
|
|
return false
|
|
case "skill", "use_skill", "useskill":
|
|
our.FightC.UseSkillAt(our.Player, ctx.SkillID, actorIndex, targetIndex)
|
|
return true
|
|
case "switch", "change_pet", "changepet":
|
|
our.FightC.ChangePetAt(our.Player, ctx.CatchTime, actorIndex)
|
|
return true
|
|
default:
|
|
return false
|
|
}
|
|
}
|