Files
bl/logic/service/fight/input/ai.go
xinian d83cf365ac
Some checks failed
ci/woodpecker/push/my-first-workflow Pipeline failed
更新说明
2026-04-05 23:13:06 +08:00

216 lines
4.6 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() {
next := our.Exec(func(t Effect) bool {
return t.HookAction()
})
scriptCtx := buildBossHookActionContext(our, 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) {
return
}
selfPet := our.FightC.GetCurrPET(our.Player)
if selfPet == nil {
return
}
if selfPet.Info.Hp <= 0 {
for _, v := range our.AllPet {
if v.Info.Hp > 0 {
our.FightC.ChangePet(our.Player, v.Info.CatchTime)
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
}
s.DamageValue = our.CalculatePower(our.Opp, s)
oppPet := our.Opp.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.UseSkill(our.Player, uint32(usedskill.XML.ID))
} else {
our.FightC.UseSkill(our.Player, 0)
}
}
func buildBossHookActionContext(our *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 our.Opp != nil {
if oppPet := our.Opp.CurrentPet(); oppPet != nil {
ctx.Opp = &configmodel.BossHookPetContext{
PetID: oppPet.Info.ID,
CatchTime: oppPet.Info.CatchTime,
Hp: oppPet.Info.Hp,
MaxHp: oppPet.Info.MaxHp,
}
}
if our.Opp.AttackValue != nil {
ctx.OppAttack = convertAttackValue(our.Opp.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) 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.UseSkill(our.Player, ctx.SkillID)
return true
case "switch", "change_pet", "changepet":
our.FightC.ChangePet(our.Player, ctx.CatchTime)
return true
default:
return false
}
}