Files
bl/modules/config/model/boss_pet.go
xinian d83cf365ac
Some checks failed
ci/woodpecker/push/my-first-workflow Pipeline failed
更新说明
2026-04-05 23:13:06 +08:00

171 lines
5.5 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package model
import (
"blazing/cool"
"fmt"
"strings"
"github.com/dop251/goja"
)
const (
TableNameBossConfig = "config_boss" // BOSS配置表全量包含基础/奖励/护盾/捕捉/特效/世界野怪/地图费用/战斗通用逻辑)
)
// BossConfig BOSS配置模型覆盖所有补充的配置项GBTL/非VIP费用/首场景/战斗通用逻辑)
type BossConfig struct {
*cool.Model // 嵌入通用Model包含ID/创建时间/更新时间等通用字段)
PetBaseConfig
MapID int32 `gorm:"not null;index;comment:'所属地图ID'" json:"map_id" description:"地图ID"`
Ordernum int32 `gorm:"not null;default:0;comment:'排序'" json:"ordernum" description:"排序"`
ParentID int32 `gorm:"not null;default:0;column:parentId;type:int" json:"parentId"` // 父ID
Remark string `gorm:"comment:'BOSS备注'" json:"remark"`
//是否可捕捉MapPit
IsCapture int `gorm:"type:int;default:0;comment:'是否可捕捉'" json:"is_capture"`
Script string `gorm:"size:1024;default:'';comment:'BOSS脚本'" json:"script"` //boss出招逻辑做成js脚本
Rule []uint32 `gorm:"type:jsonb; ;comment:'战胜规则'" json:"rule"`
}
// BossHookSkillContext 为脚本暴露当前精灵技能可用信息。
type BossHookSkillContext struct {
SkillID uint32 `json:"skill_id"`
PP uint32 `json:"pp"`
CanUse bool `json:"can_use"`
}
// BossHookPetContext 为脚本暴露战斗中双方精灵简要信息。
type BossHookPetContext struct {
PetID uint32 `json:"pet_id"`
CatchTime uint32 `json:"catch_time"`
Hp uint32 `json:"hp"`
MaxHp uint32 `json:"max_hp"`
}
// BossHookAttackContext 参考 AttackValue为脚本暴露关键战斗面板/结果字段。
type BossHookAttackContext struct {
SkillID uint32 `json:"skill_id"`
AttackTime uint32 `json:"attack_time"`
IsCritical uint32 `json:"is_critical"`
LostHp uint32 `json:"lost_hp"`
GainHp int32 `json:"gain_hp"`
RemainHp int32 `json:"remain_hp"`
MaxHp uint32 `json:"max_hp"`
State uint32 `json:"state"`
Offensive float32 `json:"offensive"`
Status []int8 `json:"status"`
Prop []int8 `json:"prop"`
}
// BossHookActionContext 为 boss 脚本提供可读写的出手上下文。
type BossHookActionContext struct {
HookAction bool `json:"hookaction"` // effect 链原始 HookAction 判定
Round uint32 `json:"round"` // 当前回合数
IsFirst bool `json:"is_first"` // 是否先手
Our *BossHookPetContext `json:"our"` // 我方当前精灵
Opp *BossHookPetContext `json:"opp"` // 对方当前精灵
Skills []BossHookSkillContext `json:"skills"` // 我方技能
OurAttack *BossHookAttackContext `json:"our_attack"` // 我方AttackValue快照
OppAttack *BossHookAttackContext `json:"opp_attack"` // 对方AttackValue快照
Action string `json:"action"` // auto/skill/switch
SkillID uint32 `json:"skill_id"` // action=skill
CatchTime uint32 `json:"catch_time"` // action=switch
UseSkillFn func(skillID uint32) `json:"-"`
SwitchPetFn func(catchTime uint32) `json:"-"`
}
func (*BossConfig) TableName() string { return TableNameBossConfig }
func (*BossConfig) GroupName() string { return "default" }
func NewBossConfig() *BossConfig { return &BossConfig{Model: cool.NewModel()} }
func init() {
cool.CreateTable(&BossConfig{})
}
// RunHookActionScript 执行 BOSS 脚本 hookAction。
func (b *BossConfig) RunHookActionScript(hookAction any) (bool, error) {
if b == nil || strings.TrimSpace(b.Script) == "" {
return true, nil
}
program, err := goja.Compile("boss_hook_action.js", b.Script, false)
if err != nil {
return false, fmt.Errorf("compile boss script: %w", err)
}
vm := goja.New()
vm.SetFieldNameMapper(goja.TagFieldNameMapper("json", true))
bindBossScriptFunctions(vm, hookAction)
if _, err = vm.RunProgram(program); err != nil {
return false, fmt.Errorf("run boss script: %w", err)
}
var (
callable goja.Callable
ok bool
)
for _, fnName := range []string{"hookAction", "HookAction", "hookaction"} {
callable, ok = goja.AssertFunction(vm.Get(fnName))
if ok {
break
}
}
if !ok {
return false, fmt.Errorf("boss script function not found: hookAction")
}
result, err := callable(goja.Undefined(), vm.ToValue(hookAction))
if err != nil {
return false, fmt.Errorf("execute boss hookAction: %w", err)
}
if goja.IsUndefined(result) || goja.IsNull(result) {
return defaultHookActionResult(hookAction), nil
}
return result.ToBoolean(), nil
}
func bindBossScriptFunctions(vm *goja.Runtime, hookAction any) {
ctx, ok := hookAction.(*BossHookActionContext)
if !ok || ctx == nil {
return
}
_ = vm.Set("useSkill", func(call goja.FunctionCall) goja.Value {
if ctx.UseSkillFn == nil || len(call.Arguments) == 0 {
return goja.Undefined()
}
skillID := call.Arguments[0].ToInteger()
if skillID < 0 {
return goja.Undefined()
}
ctx.UseSkillFn(uint32(skillID))
return goja.Undefined()
})
_ = vm.Set("switchPet", func(call goja.FunctionCall) goja.Value {
if ctx.SwitchPetFn == nil || len(call.Arguments) == 0 {
return goja.Undefined()
}
catchTime := call.Arguments[0].ToInteger()
if catchTime < 0 {
return goja.Undefined()
}
ctx.SwitchPetFn(uint32(catchTime))
return goja.Undefined()
})
}
func defaultHookActionResult(hookAction any) bool {
if ctx, ok := hookAction.(*BossHookActionContext); ok {
return ctx.HookAction
}
if val, ok := hookAction.(bool); ok {
return val
}
return true
}