Files
bl/modules/config/model/boss_pet.go

171 lines
5.5 KiB
Go
Raw Normal View History

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"`
}
2026-04-05 23:13:06 +08:00
// 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"` // 我方技能
2026-04-05 23:13:06 +08:00
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:"-"`
}
2026-04-05 23:13:06 +08:00
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
}