Skip to content

副本脚本编写

副本脚本是地牢各个阶段执行的JavaScript代码,控制地牢的整个生命周期。

常见用途:

  • 地牢初始化(设置环境、变量)
  • 开始脚本(欢迎消息、生成怪物)
  • 完成脚本(发放奖励、评分计算)
  • 失败脚本(安慰消息、清理资源)
  • 死亡事件(复活机制、惩罚)

前置要求:

  • 基础JavaScript语法
  • 了解action命令(参考脚本API文档)
  • 脚本在 scripts.yml 中配置

生命周期脚本说明

地牢生命周期包含6个阶段:

阶段触发时机常用功能是否必需
init世界复制完成后,玩家传送前环境设置、变量初始化
start玩家传送后,满足开始条件欢迎消息、生成怪物
complete调用 action.complete_dungeon()评分、奖励发放
fail调用 action.fail_dungeon() 或超时失败消息、可用 {失败原因} 变量
player_death玩家死亡并重生后死亡提示、设置观察模式、复活机制
all_death所有玩家处于死亡状态全灭提示、调用 fail_dungeon

死亡脚本详解

player_death 脚本:

  • 触发时机:玩家死亡后自动重生,重生后执行此脚本
  • @trigger 指向死亡的玩家
  • 玩家会被标记为"死亡状态",需要通过 action.revive() 复活
  • 如果不设置观察模式也不复活,玩家会保持死亡状态直到触发 all_death

all_death 脚本:

  • 触发时机:所有玩家都处于死亡状态时立即触发
  • 需要在此脚本中调用 action.fail_dungeon('原因') 来触发失败流程

fail 脚本:

  • 触发时机:fail_dungeon 被调用时
  • 可使用 {失败原因}{fail_reason} 变量获取失败原因

示例1:基础地牢脚本

简单的线性地牢流程。

yaml
# 地牢初始化
init: |-
  action.message('@all', '&e地牢正在准备中...')
  action.time('day')
  action.weather('clear')
  
  // 初始化变量
  setVar('total_kills', 0)
  setVar('team_deaths', 0)

# 地牢开始
start:
  # 开始条件(可选)
  condition: []
  
  # 开始脚本
  script: |-
    action.title('@all', '&6&l骷髅巢穴', '&7消灭所有骷髅')
    action.message('@all', '&6&l━━━━━ 骷髅巢穴 ━━━━━')
    action.message('@all', '&7消灭所有骷髅,找到宝藏!')
    action.sound('@all', 'ENTITY_ENDER_DRAGON_GROWL', 1.0, 0.8)
    
    action.wait('3秒')
    action.spawn_group('第一波')

# 地牢完成
complete: |-
  var totalKills = getVar('total_kills') || 0
  var time = dungeon.time
  var deaths = getVar('team_deaths') || 0
  
  action.title('@all', '&6&l胜利', '&e地牢完成')
  action.message('@all', '&a━━━━━ 统计数据 ━━━━━')
  action.message('@all', '&7总击杀: &e' + totalKills)
  action.message('@all', '&7用时: &e' + Math.floor(time / 60) + '分' + (time % 60) + '秒')
  action.message('@all', '&7死亡次数: &e' + deaths)
  
  // 发放奖励
  action.grant_reward('@all', '基础奖励')
  action.complete_dungeon()

# 地牢失败
fail: |-
  action.title('@all', '&c&l任务失败', '&7下次再接再厉')
  action.message('@all', '&c地牢挑战失败')
  action.sound('@all', 'ENTITY_VILLAGER_NO', 1.0, 1.0)

# 玩家死亡
player_death: |-
  var deaths = (getVar('team_deaths') || 0) + 1
  setVar('team_deaths', deaths)
  
  action.message('@all', '&c' + player.name + ' 阵亡了!')
  action.sound('@all', 'ENTITY_LIGHTNING_BOLT_THUNDER', 1.0, 0.8)

# 全队死亡
all_death: |-
  action.title('@all', '&c&l全队覆灭', '')
  action.message('@all', '&c所有玩家都阵亡了')
  action.fail_dungeon()

示例2:评分系统

根据表现给予不同评价和奖励。

yaml
start:
  script: |-
    action.title('@all', '&6&l限时挑战', '&7在30分钟内完成')
    action.message('@all', '&e目标: 在30分钟内完成地牢')
    action.message('@all', '&7评分标准: 用时、击杀、死亡')
    action.spawn_group('第一波')

complete: |-
  var time = dungeon.time
  var kills = getVar('total_kills') || 0
  var deaths = getVar('team_deaths') || 0
  
  // 评分计算
  var baseScore = 1000
  var timeBonus = Math.max(0, 1800 - time)  // 30分钟=1800秒
  var killBonus = kills * 5
  var deathPenalty = deaths * 100
  
  var totalScore = baseScore + timeBonus + killBonus - deathPenalty
  setVar('totalScore', totalScore)
  
  // 显示评分
  action.title('@all', '&6总分', '&e' + totalScore)
  action.message('@all', '&6━━━━━ 评分详情 ━━━━━')
  action.message('@all', '&7基础分: &e+' + baseScore)
  action.message('@all', '&7时间加成: &e+' + timeBonus)
  action.message('@all', '&7击杀加成: &e+' + killBonus)
  action.message('@all', '&7死亡扣分: &c-' + deathPenalty)
  action.message('@all', '&a最终得分: &e' + totalScore)
  
  // 根据分数发放奖励
  if (totalScore >= 1500) {
    action.grant_reward('@all', '评分_S级')
    action.title('@all', '&6&l评价: S级', '&e完美通关')
  } else if (totalScore >= 1000) {
    action.grant_reward('@all', '评分_A级')
    action.title('@all', '&a&l评价: A级', '&e优秀表现')
  } else if (totalScore >= 500) {
    action.grant_reward('@all', '评分_B级')
    action.title('@all', '&b&l评价: B级', '&e继续加油')
  } else {
    action.grant_reward('@all', '评分_C级')
    action.title('@all', '&7&l评价: C级', '&7还需努力')
  }
  
  action.complete_dungeon()

示例3:波次战斗

多波次递进式战斗。

yaml
init: |-
  setVar('current_wave', 0)
  setVar('total_waves', 5)

start:
  script: |-
    action.title('@all', '&c&l守塔战', '&7抵御5波敌人')
    action.message('@all', '&c━━━━━ 守塔战 ━━━━━')
    action.message('@all', '&7抵御所有敌人的进攻!')
    
    action.wait('5秒')
    startNextWave()

complete: |-
  var waves = getVar('total_waves')
  var kills = getVar('total_kills') || 0
  
  action.title('@all', '&6&l防守成功', '&a成功抵御了所有敌人')
  action.message('@all', '&a━━━━━ 胜利 ━━━━━')
  action.message('@all', '&7完成波次: &e' + waves)
  action.message('@all', '&7总击杀: &e' + kills)
  
  action.grant_reward('@all', '防守成功奖励')
  action.complete_dungeon()

自定义函数(functions.js):

javascript
/**
 * 开始下一波
 */
function startNextWave() {
    var wave = (getVar('current_wave') || 0) + 1
    var total = getVar('total_waves') || 5
    
    setVar('current_wave', wave)
    
    action.title('@all', '&c&l第' + wave + '波', '&7共' + total + '波')
    action.message('@all', '&c[波次] 第' + wave + '/' + total + '波来袭!')
    action.sound('@all', 'ENTITY_WITHER_SPAWN', 1.0, 0.8)
    
    // 根据波次生成不同怪物
    if (wave === 1) {
        action.spawn_group('第一波')
    } else if (wave === 2) {
        action.spawn_group('第二波')
    } else if (wave === 3) {
        action.spawn_group('第三波')
    } else if (wave === 4) {
        action.spawn_group('第四波')
    } else if (wave === 5) {
        action.spawn_group('Boss战')
    }
}

示例4:Boss战机制

多阶段Boss战。

yaml
start:
  script: |-
    action.title('@all', '&4&lBoss战', '&c击败骷髅王')
    action.weather('thunder')
    action.time('midnight')
    
    setVar('boss_phase', 1)
    setVar('boss_health', 100)
    
    action.wait('5秒')
    action.spawn_group('Boss战')

complete: |-
  action.weather('clear')
  action.time('day')
  
  action.title('@all', '&6&l击败Boss', '&a骷髅王已被消灭')
  action.sound('@all', 'UI_TOAST_CHALLENGE_COMPLETE', 1.0, 1.0)
  
  // Boss战额外奖励
  action.grant_reward('@all', 'Boss击杀奖励')
  action.grant_reward('@all', '随机礼包')
  
  action.complete_dungeon()

任务系统配合(tasks.yml):

yaml
tasks:
  Boss血量检测:
    type: "循环"
    interval: 5
    
    script: |-
      var phase = getVar('boss_phase') || 1
      // 假设通过某种方式获取Boss血量
      var healthPercent = 50  // 示例值
      
      if (phase === 1 && healthPercent <= 70) {
        setVar('boss_phase', 2)
        action.title('@all', '&c&l第二阶段', '&7Boss狂暴了')
        action.message('@all', '&c[Boss] 骷髅王进入第二阶段!')
        action.spawn_group('Boss小弟_1')
      } else if (phase === 2 && healthPercent <= 30) {
        setVar('boss_phase', 3)
        action.title('@all', '&4&l最终阶段', '&c全力一击')
        action.message('@all', '&4[Boss] 骷髅王进入最终阶段!')
        action.spawn_group('Boss小弟_2')
      }

示例5:时间挑战

限时完成,超时失败。

yaml
init: |-
  setVar('time_limit', 600)  // 10分钟

start:
  script: |-
    action.title('@all', '&e&l限时挑战', '&710分钟内完成')
    action.message('@all', '&e━━━━━ 限时挑战 ━━━━━')
    action.message('@all', '&7时间限制: &e10分钟')
    action.message('@all', '&c超时将自动失败!')
    
    action.spawn_group('第一波')

complete: |-
  var time = dungeon.time
  var limit = getVar('time_limit')
  var remaining = limit - time
  
  if (remaining > 0) {
    action.title('@all', '&a&l限时完成', '&7剩余 ' + remaining + ' 秒')
    action.message('@all', '&a成功在时限内完成!')
    action.grant_reward('@all', '限时奖励')
  } else {
    action.message('@all', '&7完成地牢,但已超时')
    action.grant_reward('@all', '基础奖励')
  }
  
  action.complete_dungeon()

fail: |-
  action.title('@all', '&c&l超时失败', '&7时间用完了')
  action.message('@all', '&c挑战超时,任务失败')

定时任务配合(tasks.yml):

yaml
tasks:
  超时检测:
    type: "定时"
    timings:
      600:  # 10分钟
        - |-
          action.title('@all', '&c&l时间到', '&4&l任务失败')
          action.message('@all', '&c挑战超时!')
          action.fail_dungeon()

示例6:复活机制

玩家死亡后设置观察模式,通过 action.revive() 复活。

yaml
player_death: |-
  action.setDeathCheckDelay(10)  // 设置死亡检查延迟为10秒
  action.gamemode('@trigger', '旁观')  // 设置为观察者模式
  
  var playerName = player.getName()
  var playerDeaths = (getVar('player_' + playerName + '_deaths') || 0) + 1
  setVar('player_' + playerName + '_deaths', playerDeaths)
  
  var deaths = (getVar('team_deaths') || 0) + 1
  setVar('team_deaths', deaths)
  
  action.message('@all', '&c' + playerName + ' 阵亡了! &8(&c' + playerDeaths + '次&8)')
  
  // 允许复活3次
  if (playerDeaths < 3) {
    action.title('@trigger', '&c&l你死了', '&75秒后复活')
    await action.wait('5秒')
    action.revive('@trigger')
    action.title('@trigger', '&a&l复活成功', '&7剩余 ' + (3 - playerDeaths) + ' 次')
  } else {
    action.message('@trigger', '&c你的复活次数已用完,进入观察模式')
    action.title('@trigger', '&c&l永久死亡', '&7等待队友完成挑战')
  }

all_death: |-
  action.title('@all', '&c&l全队覆灭', '&7没有人能够继续战斗')
  action.message('@all', '&c所有玩家都已阵亡')
  action.fail_dungeon()

复活机制说明:

  • 玩家死亡后会自动重生并执行 player_death 脚本
  • 使用 action.setDeathCheckDelay(秒) 设置死亡检查延迟,给复活脚本执行时间
  • 使用 action.gamemode('@trigger', '旁观') 设置为观察模式(死亡状态)
  • 使用 await action.wait('5秒') 等待指定时间
  • 使用 action.revive('@trigger') 复活玩家,该动作会:
    1. 从死亡状态列表中移除玩家
    2. 自动恢复为生存模式
    3. 恢复满血

TIP

GraalJS async/await 支持:

  • 脚本自动包装为 async 函数,可以直接使用 await
  • await action.wait() 后局部变量仍然有效,无需使用 setVar 保存
  • 循环中也可以正常使用 await action.wait()

脚本配置说明

基础格式

使用 |- 多行字符串格式(推荐):

yaml
script: |-
  action.message('@all', '消息')
  action.spawn_group('怪物组名')

条件判断

javascript
if (condition) {
  // 执行代码
} else if (condition2) {
  // 执行代码
} else {
  // 执行代码
}

循环

javascript
for (var i = 0; i < 5; i++) {
  action.message('@all', '第' + (i + 1) + '次')
}

变量操作

javascript
// 设置变量
setVar('变量名', 值)

// 获取变量(带默认值)
var value = getVar('变量名') || 默认值

// 示例
var kills = (getVar('total_kills') || 0) + 1
setVar('total_kills', kills)

常用选择器

选择器说明
@all所有玩家
@trigger触发者
@random随机玩家
玩家名特定玩家

地牢变量

javascript
dungeon.time       // 已用时间(秒)
dungeon.players    // 玩家列表
player.name        // 当前玩家名称

常用脚本模式

1. 延迟执行

javascript
action.message('@all', '3秒后开始...')
action.wait('3秒')
action.message('@all', '开始!')

2. 条件检查

javascript
var kills = getVar('total_kills') || 0

if (kills >= 50) {
  action.message('@all', '击杀50只怪物!开启隐藏Boss')
  action.spawn_group('隐藏Boss')
}

3. 计算公式

javascript
var baseScore = 1000
var timeBonus = Math.max(0, 1800 - dungeon.time)
var totalScore = baseScore + timeBonus

action.message('@all', '总分: ' + totalScore)

4. 多重奖励

javascript
action.grant_reward('@all', '基础奖励')
action.wait('1秒')
action.grant_reward('@all', '额外奖励')
action.wait('1秒')
action.grant_reward('@all', '随机礼包')

5. 团队统计

javascript
var totalKills = getVar('total_kills') || 0
var totalDeaths = getVar('team_deaths') || 0
var kd = totalDeaths > 0 ? (totalKills / totalDeaths).toFixed(2) : totalKills

action.message('@all', '&7K/D比: &e' + kd)

常见问题

Q: 脚本不执行?

  • 检查YAML格式是否正确(使用 |- 格式)
  • 查看控制台是否有JavaScript错误
  • 确认脚本所在阶段是否触发

Q: 变量无法跨脚本使用?

  • 使用 setVargetVar 存储变量
  • 变量在整个地牢实例中共享
  • 玩家相关数据建议加前缀(如 player_名称_变量

Q: 如何调试脚本?

  • 使用 action.message 输出调试信息
  • 检查控制台日志
  • 分段测试,逐步定位问题

Q: wait命令阻塞脚本?

  • action.wait 会暂停当前脚本执行
  • 后续代码会在等待后继续执行
  • 注意不要在循环中使用过长的wait

Q: 如何调用自定义函数?

  • functions.js 中定义函数
  • 在脚本中直接调用函数名
  • 示例:playVictoryEffects('0,65,0')

最佳实践

1. 变量命名规范

javascript
// 全局变量
setVar('total_kills', 0)
setVar('current_wave', 1)

// 玩家变量
setVar('player_' + player.name + '_deaths', 0)
setVar('player_' + player.name + '_damage', 0)

// 临时变量
var tempScore = 100
var playerList = []

2. 错误处理

javascript
var kills = getVar('total_kills')
if (kills === undefined || kills === null) {
  kills = 0
  setVar('total_kills', 0)
}

// 或使用简写
var kills = getVar('total_kills') || 0

3. 代码复用

将常用逻辑封装到 functions.js

javascript
/**
 * 播放胜利特效
 */
function playVictoryEffects(location) {
    action.effect('FIREWORK', location, 30, 2.0, 0.5)
    action.sound('@all', 'UI_TOAST_CHALLENGE_COMPLETE', 1.0, 1.0)
    action.effect('EXPLOSION_LARGE', location, 10, 1.0, 0.0)
}

/**
 * 宣布波次
 */
function announceWave(wave, title, subtitle) {
    action.title('@all', '&c&l第' + wave + '波', '&7' + subtitle)
    action.message('@all', '&c━━━ ' + title + ' ━━━')
    action.sound('@all', 'ENTITY_WITHER_SPAWN', 1.0, 0.8)
}

4. 注释规范

javascript
// 初始化变量
setVar('total_kills', 0)

// 计算评分
var baseScore = 1000
var timeBonus = 1800 - dungeon.time  // 30分钟时限

// 发放奖励
if (totalScore >= 1500) {
  action.grant_reward('@all', 'S级奖励')
}

基于 MIT 许可发布