简体中文
变量, 表达式与占位符上下文矩阵
CoreLib 和各模块里, 表达式变量, 模板变量, PlaceholderAPI 占位符, 运行时上下文分别在哪用, 各自能读到哪些键, 汇总如下
内容按当前线上实现里的 parser, service, model 和 PlaceholderAPI expansion 整理, 覆盖 CoreLib, Attribute, Strengthen, Skills, Item, Forge, Cooking, Gem, Level, Codex 这些模块
先区分四种同形语法
很多地方都写成 %key%, 但它们其实不是同一套系统:
| 类型 | 用途 | 解析时机 |
|---|---|---|
| 表达式变量 | 用来算数值, 布尔和随机配置 | 在 ExpressionEngine 或模块自己的解析器求值时 |
| 内部模板变量 | 用于 Action, 名称, Lore, 消息这类文本替换 | 在对应模块构造文本或 ActionContext 时 |
| PlaceholderAPI 占位符 | 给计分板, 菜单, 聊天这类外部插件读 | 在 PlaceholderAPI 请求时 |
| 运行时上下文 | 伤害, 玩法事件, 技能调用等过程中随身带的数据 Map | 由用到这个上下文的表达式, 条件或模板读取 |
同名的变量只在它自己的作用域里有效. 别因为某个 PAPI 占位符存在, 就以为同名的短变量也能用在模块公式里
总览
| 插件 | 主要表达式上下文 | 内部模板 / Action 上下文 | PlaceholderAPI |
|---|---|---|---|
| CoreLib | 调用方的 Map, 以及随机配置自己的 variables | 玩家, 阶段, 循环与调用方动态键 | 无自有扩展, 只负责最后转交 PAPI |
| Attribute | 伤害阶段, 恢复, DamageContext | 伤害消息 | emakiattribute |
| Strengthen | 货币成本; 效果值只有限引用 | 强化展示, 成功/失败动作 | emakistrengthen |
| Skills | 技能参数, 自定义变量, 升级成本 | 技能脚本文本, 升级消息 | emakiskills |
| Item | 物品构建, EA 属性, 修复成本 | 物品动作, 套装显示 | emakiitem |
| Forge | 材料 stat/EA 属性/容量随机值 | 最终物品展示, 结果动作 | emakiforge |
| Cooking | 营养 Action 数量表达式 | 食谱奖励与营养消息 | emakicooking |
| Gem | 宝石 stat/EA 属性, 成本, 镶嵌率 | 镶嵌/升级/提取结果 | emakigem |
| Level | 升级需求, 来源经验, 属性, 成本 | 等级操作与升级结果 | emakilevel |
| Codex | 没有自己的公式环境; 使用 GameplayEvent 条件变量 | 触发器与成就完成文本 | 无自有扩展 |
CoreLib
通用 ExpressionEngine
变量由调用方的 Map 注入, 写法是 %name%. CoreLib 不会给所有公式自动塞一组全局业务变量
- 运算符:
+,-,*,/,%,^, 括号 - 函数:
ceil,floor,round,log10,min,max,pow - 最大表达式长度 256 字符, 最大递归深度 10
- 禁止反引号,
$, 反斜杠 - 缺失变量, 自引用, 非数值变量,
NaN或无限结果会让详细求值失败; 简化接口遇到这类情况会退回0 - 随机配置自己的
variables会盖住外部同名变量; 动态变量按实际 Map 的迭代顺序求值, 后一项可以引用前面已经解析出来的项
实现依据: ExpressionEngine, ExpressionRules, RandomExpressionEvaluator, BooleanExpressionEvaluator
ActionContext 内部变量
| 变量 | 说明 |
|---|---|
%player%, %player_name% | 玩家名称 |
%player_uuid% | 玩家 UUID |
%player_world% | 世界名 |
%player_x%, %player_y%, %player_z% | 玩家坐标 |
%phase% | 当前动作阶段 |
<show_item> | 独立的内联 token; 只在上下文里存在 show_item 时才替换 |
以上 8 个变量是 CoreLib 唯一自动注入的一组. 没有 %template_<key>%, 也没有 %loop_*% 系列变量: 传给命名序列的额外参数, 在序列体内读作 %var.<名字>%
调用模块还可以通过 ActionContext.placeholders() 注入任意小写键. attributes() 不会自动变成模板变量
实现依据: PlaceholderRenderer, ActionInlineTokenResolver
GameplayEvent 变量
| 事件 | 变量 |
|---|---|
| 方块破坏 / 放置 | block_type |
| 实体击杀 / 驯服 | entity_type |
| MythicMobs 击杀 | mythic_id, mythic_level |
| 合成 / 熔炉取出 | result_type, result_amount |
| 钓鱼 | fish_state |
| 酿造完成 | potion_type |
这些键来自真实 Bukkit 事件, 由 Level, Codex 等模块按事件类型来用
PlaceholderAPI 边界
CoreLib 不注册自己的 PlaceholderAPI expansion. 解析时先处理内部变量和内联 token, 如果装了 PlaceholderAPI 又有玩家上下文, 再转给外部去解析
Attribute
自定义伤害阶段 stages[].expression
只有 CUSTOM 阶段用表达式
| 变量 | 含义 / 单位 |
|---|---|
input, base | 进入当前阶段时的伤害点 |
flat | 固定属性的聚合值 |
percent | 百分比点数 |
chance | 触发概率, 0..100 |
multiplier | 倍率属性的百分比点 |
roll | 当前判定随机值, 0..100 |
crit | 是否触发, 取 1 或 0 |
| DamageContext 任意键 | 调用方或伤害服务写进去的运行时上下文 |
一条重要的界限: 实现会先写入上面这些固定键, 再合并 DamageContext. 所以 DamageContext 里的同名键会盖住固定键. ATTACKER / TARGET 来源先读当时的属性值, 读不到时可以退回上下文里的同名数值; CONTEXT 来源则只读上下文
实现依据: StageCalculator, DamageContextVariables
DamageContext 标准键
- 实体:
attacker,attacker_name,attacker_type,attacker_uuid,source,source_name,target,target_name,target_type,target_uuid - 伤害类型:
damage_type,damage_type_name,damage_type_id - 伤害数值:
source_damage,input_damage,base_damage,damage,final_damage - 原因:
cause,cause_name,cause_id,damage_cause,damage_cause_name,damage_cause_id - 投射物:
projectile_type,projectile_uuid - 控制:
allow_critical,allow_target_dodge,calculate_target_defense - 随机:
damage_roll,damage_roll_flat,damage_roll_percent,damage_roll_chance,damage_roll_multiplier, 范围0..1 - 闪避:
dodge_chance,dodge_roll,dodged, 概率/随机值范围0..100 - 摔落:
fall_distance,jump_boost_level,vanilla_fall_damage,fall_damage_formula
这些键在 DamageContextVariables 里会被整理成小写的 identifier
恢复公式 recovery.expression
| 变量 | 说明 |
|---|---|
input, base, damage, final_damage | 最终伤害点 |
flat, healing_flat | 固定恢复点数 |
percent, healing_percent | 恢复百分比点 |
percent_amount, healing_percent_amount | 按伤害折算出来的恢复量 |
gross, healing_gross | 抗性前的总恢复量 |
resistance, healing_resistance | 恢复抗性的百分比点 |
恢复的固定键是在继承 DamageContext 之后写入的, 所以会盖住上下文里的同名键. 默认结果是 gross * (1 - resistance / 100), 之后套用最小/最大值, 并把结果限制为非负
实现依据: DamageRecoveryCalculator
伤害消息模板
message, attacker_message, target_message 用普通的 %key% 文本替换. 除了上面那些实体, 伤害类型, 伤害数值与原因键, 还提供:
source_type, source_uuid, critical, critical_text, critical_suffix, roll, attacker_health, attacker_max_health, target_health, target_max_health, distance
健康值和距离只从 DamageContext 读; 没注入时显示 0
实现依据: DamageMessageDispatcher
PlaceholderAPI: emakiattribute
%emakiattribute_<attribute_id>%%emakiattribute_power%%emakiattribute_resource_<id>_current%%emakiattribute_resource_<id>_max%%emakiattribute_resource_<id>_default%%emakiattribute_resource_<id>_bonus%%emakiattribute_resource_<id>_percent%%emakiattribute_resource_<id>_regen%
解析资源时会把第一个下划线之前的部分当成资源 ID, 所以资源 ID 最好不要带下划线
Strengthen
成功率
成功率不走 ExpressionEngine 公式. 实际算法是: 目标星级的基础成功率, 加上有效锻印等级乘以每级加成, 最后限制在 0..success_chance_cap
实现依据: ChanceCalculator
货币成本 cost_formula
| 变量 | 说明 |
|---|---|
base_cost | 货币的基础成本 |
star | 目标星级, 也就是当前星级 + 1 |
公式结果必须有限且大于 0, 然后四舍五入成整数成本; 不然这一项成本会被跳过
效果值解析边界
stars.<n>.variables, effects[type=variables].variables, ea_attributes 的线上实现只支持:
- Number;
- 数值字符串;
- 整个值恰好为
%name%的引用
%star% * 2 这种算术文本不会进 CoreLib ExpressionEngine, 会退回 0. 初始固定变量只有 star, 后面的动态键能不能引用前面的项, 取决于实际 Map 的迭代顺序
实现依据: StrengthenRecipe.resolveExpressions, StrengthenApiValues.evaluateNumber/evaluateMixed
展示与动作变量
展示提供动态 stat 键, 以及 star, temper, max_temper, temper_color; 属性行另有 id, sign, value
成功/失败 Action 提供:
operation_id, strengthen_operation_id, strengthen_recipe_id, strengthen_recipe, strengthen_star, strengthen_temper, strengthen_show_item, strengthen_result_slot, strengthen_max_star, strengthen_success_rate, show_item, star, temper, dropped, protected, was_star
实现依据: StrengthenSnapshotBuilder, StrengthenActionCoordinator
PlaceholderAPI: emakistrengthen
mainhand_star, mainhand_temper, mainhand_recipe, mainhand_eligible, mainhand_success_count, mainhand_failure_count, mainhand_max_star, mainhand_success_rate, mainhand_crack_level
Skills
技能变量
variables.* 使用:
| 变量 | 说明 |
|---|---|
level, target_level, max_level | 技能等级上下文 |
skill_id, trigger_id | 技能与触发器 ID |
is_passive | 是否被动技能, 1/0 |
player_level | 原版经验等级 |
player_health, player_max_health | Bukkit 生命点 |
sneaking, has_target | 布尔量, 1/0 |
技能自定义的 variables 还可以引用已经解析的参数和之前定义的自定义变量. 参数本身不会自动拿到其他参数的求值结果, TriggerInvocation.extraVariables() 目前也还没有并进这套解析环境
实现依据: SkillParameterResolver, SkillVariableResolver
升级成本与文本
升级货币 cost_formula 用上面这套固定上下文, 并额外提供 base_cost; 调用时 trigger_id=upgrade. 升级成功率来自静态成功率表, 不是公式
技能脚本文本可以用 level, skill_id, trigger_id, has_target, 以及已解析的参数和自定义动态键. 升级消息提供 player, skill_id, skill (技能显示名), level, current_level, target_level, max_level, success_rate, success_rate_raw, roman_level 和成本/结果动态键
PlaceholderAPI: emakiskills
- 全局:
cast_mode,global_cooldown,unlocked_count,slot_count,level_total - 技能:
level_<skill_id>,cooldown_<skill_id> - 槽位:
slot_<index>_skill|name|trigger|empty - 资源:
resource_<id>_current|max|percent
冷却单位是秒, 槽位用从 0 开始的索引
Item
物品构建变量
effects[type=variables].variables 没有固定的内置变量, 从空 Map 按顺序解析. 名称, Lore, 结构化展示与原版 attribute modifier 都能读这些动态键. EmakiItem 物品定义以前提供的顶层 variables 简写已经下线, 现在只有 effects 一个入口, 见物品定义与套装
effects[type=ea_attribute].ea_attributes 用一套独立的空上下文按顺序解析, 不继承构建变量. 不要在 EA 属性表达式里引用构建变量
实现依据: EmakiItemDefinitionParser, EmakiItemFactory, EmakiItemPdcWriter
修复成本
| 变量 | 说明 |
|---|---|
max_damage | 最大耐久 |
damage, current_damage, missing_durability | 当前缺失的耐久点 |
missing_ratio | 缺失比例, 0..1 |
restore_amount | 本次恢复的耐久点 |
restore_ratio | 恢复比例, 0..1 |
base_cost | 货币的基础成本 |
实现依据: ItemRepairService
动作, 套装与 PlaceholderAPI
物品 Action: player, item_id, item_trigger, item_name
套装上下文: item_id, set_id, piece_id, set_name, active, active_count, total, total_pieces, active_thresholds
emakiitem PAPI: held_id, held_name, held_components, held_components_raw, loaded_count, 以及 held_component_has_<id>, held_component_value_<id>, held_component_json_<id>
Forge
材料数值上下文
capacity_cost与capacity_bonus.value可以用随机数值配置, 但没有变量上下文; 结果四舍五入成容量整数effects[type=variables].variables每种材料都从空 Map 开始, 后一项可以引用当前材料前面解析出的 stat 动态键effects[type=ea_attribute].ea_attributes用另一套独立空 Map, 不继承 stat 变量
未发现独立的锻造成功率, 品质概率或货币成本 ExpressionEngine 公式
展示与动作
最终名称/Lore 提供所有最终 stat 动态键, 以及 quality, quality_name, quality_multiplier, multiplier
结果 Action 提供: forge_recipe_id, forge_recipe_name, forge_source_item_name, forge_result_item_name, forge_quality, forge_multiplier, forge_multiplier_raw, forge_error_key, forge_failure_reason, forge_show_item, show_item
实现依据: ForgeMaterial, ForgeStatContributionBuilder, ForgeResultPostProcessor, ForgeActionCoordinator
PlaceholderAPI: emakiforge
recipe_count, craft_count_<recipe_id>, has_crafted_<recipe_id>, total_crafts, last_crafted, guarantee_<key>
Cooking
表达式与内部变量
食谱的生产流程里没有通用的 ExpressionEngine 公式. 营养操作 Action 的 amount 可以写表达式, 变量来自当前 ActionContext 里能转成数字的 placeholder 和 attribute 动态键; 没有额外的固定变量. 单位是营养点, 出错时退回 0
食谱奖励 Action: recipe_id, recipe_name, station_type, cooking_recipe_id, cooking_recipe_name, cooking_station_type, cooking_outcome
营养消息: 动态的 nutrition_<type>, nutrition_<type>_max, nutrition_<type>_min, 以及 nutrition_type, nutrition_value, nutrition_threshold, nutrition_combo_count, nutrition_combo_required
实现依据: NutritionOperationAction, RunRecipeRewardAction, NutritionService
PlaceholderAPI: emakicooking
- 配方数:
recipe_count,recipe_count_<station_type> - 营养:
nutrition_<type>,nutrition_<type>_max|min,nutrition_combo_count_<rule_id> - 工位:
station_[<type>_]type|type_name|world|x|y|z|location|block|heat_block|burning|burning_seconds|heat|moisture|steam|input|input_source|input_amount|ingredient_count|recipe|recipe_id|progress|progress_target|progress_percent|completed|fluid|fluid_amount|player
fluid_amount 单位是 ml, burning_seconds 单位是秒
Gem
宝石数值与成本
| 入口 | 变量 |
|---|---|
宝石 variables / ea_attributes | level, 以及前面已经解析的同类动态键 |
| 共鸣 EA 属性 | 没有固定的初始键, 只有前面解析出的共鸣属性键 |
inlay_success.rate_formula | default_chance, level_chance, configured_chance, level |
镶嵌/提取/升级 cost_formula | level, current_level, target_level, base_cost |
镶嵌率单位是百分比点, 结果限制在 0..100. 升级成功率来自静态的等级成功率表, 不是公式. 成本在 amount >= 0 固定时优先用固定值, 否则把公式结果钳制为非负
实现依据: GemDefinition, GemResonanceLoader, GemInlayService, GemExtractService, GemUpgradeService
操作变量与 PlaceholderAPI
升级, 镶嵌, 提取链按场景提供 player, gem_id, gem (宝石显示名), level, current_level, target_level, success_rate, slot 等; 宝石展示上下文另有 display_name, old_display_name, gem_type 与各等级 stat 动态键. 货币消息提供 provider, currency, currency_id, name, display_name, required, available; 材料消息提供 item, material, display_name, required, available
emakigem PAPI: mainhand_opened_slots, mainhand_total_slots, mainhand_filled_slots, mainhand_item_definition_id, mainhand_resonance_count, 以及 mainhand_slot_<index>_gem|level|opened|type
Level
升级需求与成本
| 入口 | 变量 |
|---|---|
升级经验需求 formula | current_level, target_level, max_level, exp, total_exp |
等级属性 attributes.<id> | level, exp, total_exp |
| 升级货币/材料成本 | target_level, type, base_cost, base_amount |
固定的 values.<target_level> 优先于需求公式. 需求和货币结果都钳制为非负, 材料数量四舍五入成非负整数
来源经验
exp_formula 只拿到当前 GameplayEvent 实际携带的键, 不会自动补上统一的玩家/等级变量:
- 实体:
entity_type - MythicMobs:
mythic_id,mythic_level - 方块:
block_type - 合成/熔炉:
result_type,result_amount - 钓鱼:
fish_state - 酿造:
potion_type
Mythic drop 公式另有 amount, drop_amount, metadata_amount, generations, tick, 并携带文本上下文 player, type, drop
Level operation Action 的 amount 与 Cooking 营养动作一样: 只读 ActionContext 里能转成数字的动态键, 没有额外的固定变量
实现依据: RequirementService, SourceExperienceService, LevelGameplaySubscriber, LevelAttributeBridge, PlayerLevelService, MythicLevelDropBridge, LevelOperationAction
PlaceholderAPI: emakilevel
- 玩家:
level_<type>,exp_<type>,total_exp_<type>,totalexp_<type>,total_<type>,required_exp_<type>,requiredexp_<type>,required_<type>,progress_<type>,progress_percent_<type> - 排行:
top_<type>_<rank>_name|level|total_exp|totalexp|total
排行榜 parser 按下划线分段, 类型 ID 最好不要带下划线
Codex
Codex 不调用自己的 ExpressionEngine, 也不注册 PlaceholderAPI expansion
成就触发条件
触发条件用 CoreLib 的 ConditionEvaluator, 变量来自 GameplayEvent, 并额外提供 player, world:
block_type, entity_type, mythic_id, mythic_level, result_type, result_amount, fish_state, potion_type, player, world
实际能用哪些键取决于事件类型, 不会同时全部存在
触发器与完成文本
触发/action 行可以读当前事件的键. 成就完成上下文另有 advancement_id, advancement_node, advancement_title, advancement_page (四个固定键)
Codex 先做本地的 %key% 替换; 有玩家且装了 PlaceholderAPI 时, 再消费其他插件注册的外部占位符
实现依据: CodexGameplaySubscriber, CodexTriggerService, AdvancementListener
配置时的通用检查清单
- 先确认当前位置属于公式, 内部模板, PAPI 还是运行时上下文
- 只使用对应作用域列出的固定键与该配置明确产生的动态键
- 百分比可能使用
0..1或0..100; 以对应模块说明为准 - 动态键按 Map 顺序求值的模块, 不要依赖未经验证的 YAML/Map 重排行为
- Attribute, Level 等动态 ID parser 对下划线支持不一致, 资源/排行 ID 优先使用不含下划线的形式
- 若变量来自事件, 必须为不同事件配置分别处理缺失键