2025-12-21 17:18:33 +00:00
|
|
|
|
package model
|
|
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
|
"blazing/cool"
|
2026-04-05 21:59:22 +08:00
|
|
|
|
"fmt"
|
|
|
|
|
|
"strings"
|
|
|
|
|
|
|
|
|
|
|
|
"github.com/dop251/goja"
|
2025-12-21 17:18:33 +00:00
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
const (
|
2026-01-19 18:51:56 +08:00
|
|
|
|
TableNameBossConfig = "config_boss" // BOSS配置表(全量包含基础/奖励/护盾/捕捉/特效/世界野怪/地图费用/战斗通用逻辑)
|
2025-12-21 17:18:33 +00:00
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
// BossConfig BOSS配置模型(覆盖所有补充的配置项:GBTL/非VIP费用/首场景/战斗通用逻辑)
|
|
|
|
|
|
type BossConfig struct {
|
|
|
|
|
|
*cool.Model // 嵌入通用Model(包含ID/创建时间/更新时间等通用字段)
|
|
|
|
|
|
PetBaseConfig
|
2026-02-24 22:10:49 +08:00
|
|
|
|
|
2026-02-25 13:20:38 +08:00
|
|
|
|
MapID int32 `gorm:"not null;index;comment:'所属地图ID'" json:"map_id" description:"地图ID"`
|
|
|
|
|
|
|
2026-02-25 19:46:31 +08:00
|
|
|
|
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
|
2026-02-25 13:20:38 +08:00
|
|
|
|
Remark string `gorm:"comment:'BOSS备注'" json:"remark"`
|
|
|
|
|
|
//是否可捕捉MapPit
|
2026-03-21 01:06:59 +08:00
|
|
|
|
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"`
|
2025-12-21 17:18:33 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-04-05 22:27:38 +08:00
|
|
|
|
// 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"`
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-04-05 22:27:38 +08:00
|
|
|
|
// 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快照
|
2026-04-05 22:27:38 +08:00
|
|
|
|
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()} }
|
2025-12-21 17:18:33 +00:00
|
|
|
|
|
|
|
|
|
|
func init() {
|
|
|
|
|
|
cool.CreateTable(&BossConfig{})
|
|
|
|
|
|
}
|
2026-04-05 21:59:22 +08:00
|
|
|
|
|
2026-04-05 22:27:38 +08:00
|
|
|
|
// RunHookActionScript 执行 BOSS 脚本 hookAction。
|
2026-04-05 21:59:22 +08:00
|
|
|
|
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()
|
2026-04-05 22:27:38 +08:00
|
|
|
|
vm.SetFieldNameMapper(goja.TagFieldNameMapper("json", true))
|
|
|
|
|
|
bindBossScriptFunctions(vm, hookAction)
|
|
|
|
|
|
|
2026-04-05 21:59:22 +08:00
|
|
|
|
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) {
|
2026-04-05 22:27:38 +08:00
|
|
|
|
return defaultHookActionResult(hookAction), nil
|
2026-04-05 21:59:22 +08:00
|
|
|
|
}
|
|
|
|
|
|
return result.ToBoolean(), nil
|
|
|
|
|
|
}
|
2026-04-05 22:27:38 +08:00
|
|
|
|
|
|
|
|
|
|
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
|
|
|
|
|
|
}
|