主题
副本脚本编写
副本脚本是地牢各个阶段执行的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')复活玩家,该动作会:- 从死亡状态列表中移除玩家
- 自动恢复为生存模式
- 恢复满血
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: 变量无法跨脚本使用?
- 使用
setVar和getVar存储变量 - 变量在整个地牢实例中共享
- 玩家相关数据建议加前缀(如
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') || 03. 代码复用
将常用逻辑封装到 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级奖励')
}