跳转到内容

剧情

剧情是通过多选项对话和附加动作构成的丰富交互系统。

添加剧情

要为角色指定默认剧情,请将 id.xlsx 文件放置在 LangMod/**/Dialog/Drama/ 文件夹下,并以角色 ID 作为文件名(例如,tinymita 角色对应 tinymita.xlsx)。

若要使用其他剧情文件,请在角色源表的标签列添加 addDrama(DramaFileId) 标签。

你也可以使用 C# API chara.SetDramaOverride(DramaFileId)chara.ShowDialog(DramaId, step)

制作新剧情表时可参考游戏内置表 Elin/Package/_Lang_Chinese/Lang/CN/Dialog/Drama,或下载 Tiny Mita 示例作为模板。

热重载

剧情表可在游戏运行时编辑。更改将在下次打开对话时自动生效。

定义

剧情表按从上至下顺序读取,由多行构成。每行包含以下字段(由首行定义):

  • step:标记步骤起点;后续行均属于该步骤,直到出现下一个 step
  • jump:本行执行后跳转的目标步骤。
  • if / if2:执行前需检查的条件。若存在 if2,则两者必须同时满足。
  • action:要执行的动作。
  • param:动作参数。
  • actor:动作或对话行的说话者。
    • ?:显示为 ???
    • tg:剧情目标角色。actor 为空时默认使用。
    • narrator:默认旁白。
    • pc:玩家。
  • id:唯一标识,textchoice必须填写。其他行无需填写。
  • text_XX / text_JP / text_EN / text:对话内容。XX 为语言代码(例如 text_CNtext_ZHTW)。若缺少对应非内置语言,则使用 text 作为备选。text_JPtext_EN 是必须填写的,但你可以不提供翻译。

(点击放大) img

步骤

剧情流程由多个步骤组成。每个步骤可包含一行或多行对话、动作与条件。

main 是默认起始步骤,end 用于退出剧情。

创建表时请避免使用以 _flag 开头的步骤名,以免与内部步骤冲突。

内置步骤

执行 inject/Unique 动作后,大量内置剧情步骤将被注入当前剧情表。只需将它们设为 jump 目标即可使用。部分步骤已在默认 inject/Unique 对话中使用,通常无需重复使用。

步骤名用途
_banish结束剧情
_bye结束剧情
_toggleSharedEquip切换 tg 的共享装备状态
_daMakeMaidtg 设为女仆
_joinPartytg 的特质允许加入,则将其设为队伍成员。这不是邀请!
_leavePartytg 从队伍移除,并送回据点区域
_makeLivestocktg 设为派系家畜
_makeResidenttg 设为派系居民
_departtg 从派系移除
_rumor查看流言
_sleepBeside切换 tg 是否在玩家旁睡觉
_disableLoyal切换 tg 忠诚心状态
_sucktg 吸血或吸猫。优先吸血,其次吸猫
_insult切换 tg 嘲讽状态
_makeHome将当前区域分支设为 tg 的家
_invite尝试邀请 tg 成为同伴,会检查玩家属性和 tg 可邀请状态。无条件邀请入队请使用拓展动作 join_party()
_Guide引导玩家前往一系列地点
_tail纯洁的肉体关系
_whore有金钱交易的肉体关系
_bloom加深与 tg 的羁绊
_buytg 购买物品
_buyPlantg 购买研究图纸
_givetg 物品
_blessing对队伍施加祝福
_traintg 进行技能训练
_changeDomain改变 tg 的领域
_revive复活死亡的同伴
_buySlavetg 购买奴隶
_tradetg 交换物品
_identifytg 鉴定物品
_identifyAlltg 鉴定所有物品
_identifySPtg 使用高级技能鉴定物品
_bout发起决斗
_news在地图上生成随机地城
_heal治疗玩家
_foodtg 购买食物
_deposittg 存款
_withdrawtg 取款
_copyItemtg 复制物品
_extraTax缴纳额外税金
_upgradeHearth升级炉石
_sellFame出售声望
_investZone投资当前区域
_investShop投资 tg 的商店
_changeTitle更改玩家称号
_buyLand扩展当前区域地图
_disableMove使 tg 无法移动
_enableMove使 tg 可以移动

文本

替换

文本替换值
#tg_histg 的所有格代词
#tg_himtg 的宾格代词
#tgtg 角色名
#last_choice上一次选项的文本
#newline换行符
#costHire雇佣 tg 的花费(数值本地化)
#self / #me角色 tg 的全名(含头衔)
#histg 的所有格代词
#himtg 的宾格代词
#1 ~ #5外部变量 refDrama1~refDrama5(数字自动格式化)
#god神名称(若 tg 不为空用其信仰,否则随机宗教)
#player / #title玩家头衔
#zone当前区域名
#guild_title当前剧情关联公会的头衔
#guild当前剧情公会名
#race玩家种族名
#pc玩家简名
#pc_full玩家全名(含头衔)
#pc_his玩家的所有格代词
#pc_him玩家的宾格代词
#pc_race玩家种族
#aka玩家别名
#bigdaddy本地化字符串 "bigdaddy"
#festival节日名(若区域有节日则用对应节日名,否则通用)
#brother2“兄弟”或“姐妹”(根据玩家性别)
#brother兄弟/姐妹随机称呼(brosis 列表随机)
#onii2哥哥/姐姐随机称呼(列表 onii2/onee2
#onii哥哥/姐姐随机称呼(列表 onii/onee
#gender玩家性别对应的随机称呼(gendersDrama 列表)
#he“他”或“她”(根据玩家性别)
#He同上,首字母大写

动态内容

#eval <此处为 C# 脚本..> 开头并返回 string 类型的文本列,能够动态生成文本内容。

动作

文本行 最常见,仅包含 idtext 列(可选 if 条件)。执行时需要玩家输入(点击或按键)才能继续。

动作行choice 除外)自动执行,无需输入。若同一行同时存在 actiontext,则通常忽略 text

例如,文本行后的动作行需先点击文本行才能执行。

内置动作
动作参数说明
injectUnique插入“来聊聊吧!”及大量实用步骤
choice为上一个文本行添加选项。需配合 textjump 使用
choice/bye插入默认告别选项
cancel设置右键/ESC 键行为。需配合 jump,通常设为 end
setFlagflag 名称,值(可选)设置 flag 值,未提供值时默认为 1
reload重新加载剧情,以便应用当前剧情中所做的 flag 更改。需配合 jump,通常设为 main这不是指热重载,开发时热重载只需保存文件更改并再次打开对话即可
enableTone为整个剧情启用对话语气转换
addActor添加剧情角色以供后续使用,text 可用于设置名称覆盖。当你在 actor 单元格填写新 ID 时会自动触发。actor 需填写角色 ID
invoke方法名调用方法。全部为本体Elin特定用途。
setBG图片名(可选)设置背景图,留空则清除。支持在 Texture 文件夹提供自定义 png
BGMBGM ID切换至指定 BGM。自定义 BGM 详见 音频/BGM 页面
stopBGM停止 BGM 且不继续
lastBGM停止 BGM 并继续播放上一个
sound音频 ID播放指定音频。自定义音频详见 音频/BGM 页面
wait时长暂停执行指定秒数,适合等待动画完成
alphaIn alphaOut时长透明度过渡(秒)
alphaInOut时长,等待时间alphaIn 先执行,等待指定秒数,再执行 alphaOut
fadeIn fadeOut时长,white/black(可选)淡入淡出过渡(秒)
fadeInOut时长,等待时间,white/black(可选)fadeIn,等待指定秒数,再 fadeOut
hideUI过渡时间以过渡效果隐藏 HUD 元素,退出剧情时恢复
hideDialog隐藏剧情对话框以便制作过场动画,但文本行会强制显示对话框,需配合 wait 使用
end直接结束剧情,等同于 jumpend 步骤
addKeyItem关键物品 ID给予玩家关键物品
drop物品 ID在玩家位置掉落奖励物品
addResource资源名称,数量添加家园资源
shake屏幕震动
slap扇剧情所有者角色
destroyItem物品 ID从玩家背包中查找并销毁指定物品
focus立即将镜头聚焦到剧情所有者角色
focusChara角色 ID,速度(可选)将镜头移动并聚焦到同地图角色
focusPC速度(可选)将镜头聚焦到玩家
unfocus重置并取消聚焦镜头
destroy角色 ID移除同地图角色
save存档
setHour小时数设置游戏时间

提供多个参数时,使用半角逗号(,)分隔,中间不要有空格

条件

条件用于判断该行是否执行。

静态条件

静态条件在加载时仅判定一次

使用 if(可选 if2)列为任意行附加条件。

条件参数说明
hasFlagflag 名称玩家拥有该 flag 且值不为 0
!hasFlagflag 名称玩家没有该 flag 或值为 0
hasMelilithCurse玩家拥有 Melilith 诅咒
merchant玩家在商人公会
fighter玩家在战士公会
thief玩家在盗贼公会
mage玩家在法师公会
hasItem物品 ID玩家背包中拥有该物品
isCompleted任务 ID玩家已完成指定任务

简单值检查(最常用于 flag/计数器):

=,example_flag,1
>,example_counter,20
!,example_flag,69

大多数行只需使用 if 列。若需多条件判断,可添加 if2 列。

动态条件

若要让某行能够动态启用或禁用,请使用:

  • invoke* 条件表达式,或
  • 返回 booleval 动作

分支

jump 设置为剧情步骤即可跳转至该步骤。

jump 设置为 eval_result,并使用返回 stringeval 动作,可动态选择跳转目标。

invoke* 表达式

在剧情表中,可使用特殊动作 invoke*(或简写 i*)调用拓展方法:

语法

伪代码语法简单:将 action 设为 invoke*i*param 设为有效方法之一:

动作参数actor
invoke*/i*honk_honk(arg1, arg2)pc

这将调用名为 honk_honk 的方法,传入 arg1arg2 两个参数。

参数

参数用半角逗号 , 分隔,并写在拓展方法的括号内,类似代码语法。若无参数,请使用空括号 ()

大多数方法还会将 actor 单元格作为目标角色执行,例如 pctg(剧情目标角色)或任何有效的角色 ID。默认值为 tg

若同一行中的 jump 单元格有值,则拓展方法的返回值将决定是否执行该 jump。返回 true 时执行跳转,否则不执行。

数值表达式语法

DramaValueExpression 用于判定或赋值。

示例:

表达式含义
69赋值为 69
=69赋值为 69
+5在原值基础上增加 5
-3在原值基础上减少 3
*10在原值基础上乘以 10
/2在原值基础上除以 2
==69判断是否等于 69
!=114判断是否不等于 114
>10判断是否大于 10
>=20判断是否大于等于 20
<5判断是否小于 5
<=3判断是否小于等于 3

拓展动作

方法参数说明跳转条件
add_item物品 ID, 材质 alias(可选), 等级(可选), 数量(可选)actor 添加指定物品,默认随机材质、自动等级、数量 1总是
equip_item物品 ID, 材质 alias(可选), 等级(可选)actor 装备指定物品,默认随机材质、自动等级总是
destroy_item物品 ID, 数量(可选)actor 销毁指定物品,默认数量 1总是
join_faith信仰 ID(可选)使 actor 加入指定信仰,留空则退出当前信仰成功时
join_party无条件使 actor 加入玩家队伍总是
apply_condition状态 alias, 强度actor 施加状态,默认强度 100总是
remove_condition状态 alias移除 actor 的状态总是

拓展演出

方法参数说明跳转条件
move_next_to角色 ID使 actor 移动到同地图角色身旁总是
move_tileX 偏移, Y 偏移使 actor 进行相对坐标移动,例如 1,12,-1总是
move_toX, Y使 actor 进行绝对坐标移动,例如 64,4412,0总是
move_zone区域 ID, 层数(可选)传送 actor 到指定区域,默认 0成功时
move_zone_2区域全名使用区域全名语法传送 actor,例如 derphy@-1成功时
play_anime动画 ID使 actor 执行动画总是
play_effect特效 ID使 actor 播放特效总是
play_effect_at特效 ID, X, Y在地图指定位置播放特效总是
play_emote表情 ID使 actor 显示表情总是
play_screen_effect屏幕特效 ID播放屏幕特效总是
pop_text文本使 actor 发出喊叫文本(气泡框)总是
set_portrait头像 ID(可选)设置 actor 对话时使用的头像,留空则重置。支持 Portrait 文件夹自定义头像,例如 UN_myChara_happy.png 可使用 happyUN_myChara_happy总是
set_portrait_override头像 ID(可选)设置 actor 对话外使用的头像,留空则重置。支持 Portrait 文件夹自定义头像,必须使用全名。此设置不会影响当前对话头像总是
set_sprite贴图 ID(可选)设置 actor 的自定义贴图,留空则重置。从 Texture 文件夹获取总是
show_book分类/书籍 ID打开一本书,支持 LangMod/**/Text 文件夹,例如使用 Book/ok 对应 Text/Book/ok.txt成功时

拓展修改

方法参数说明跳转条件
mod_affinity数值表达式使用数值表达式调整 actor 好感度成功时
mod_currency货币种类, 数值表达式使用数值表达式修改 actor 的货币。money money2 plat medal influence casino_coin ecopo总是
mod_element元素 alias, 强度(可选), 潜能(可选)actor 修改指定元素(特质/抗性/技能等),默认强度 1,潜能 100。不同类型元素的强度缩放规则不同总是
mod_element_exp元素 alias, 数值表达式修改 actor 指定元素的经验值成功时
mod_fame数值表达式使用数值表达式修改玩家声望总是
mod_flagflag, 数值表达式使用数值表达式修改 actor 的 flag 值,例如 +1=10。支持非玩家角色总是
mod_keyitem关键物品 alias, 数值表达式(可选)使用表达式修改玩家关键物品值,默认 =1成功时

拓展条件

这些也是通过 invoke* 动作调用的拓展方法,但返回值具有特殊意义。

方法参数说明跳转条件
if_affinity数值表达式使用表达式检查 actor 好感度,例如 <5>=90!=0满足时
if_condition状态 alias检查 actor 是否拥有指定状态拥有时
if_currency货币种类, 数值表达式使用表达式检查 actor 的货币。money money2 plat medal influence casino_coin ecopo满足时
if_element元素 alias, 数值表达式使用表达式检查 actor 的元素满足时
if_faith信仰 ID, 奉献等级(可选)检查 actor 是否加入特定信仰且等级不低于指定值,默认 >0满足时
if_fame数值表达式使用表达式检查玩家声望满足时
if_flagflag 名称, 数值表达式使用表达式检查 actor 的 flag 值,例如 =51!=0满足时
if_lv数值表达式使用表达式检查 actor 的等级满足时
if_has_item物品 ID, 数值表达式(可选)检查 actor 是否拥有符合表达式的物品数量,默认 >=1满足时
if_hostility阵营数值表达式检查 actor 是否符合特定阵营,例如 =Ally>Enemy。阵营值从小到大依次为 EnemyNeutralFriendAlly满足时
if_in_partytruefalse(可选)检查 actor 是否在玩家队伍中,默认 true满足时
if_keyitem关键物品 alias, 数值表达式(可选)检查玩家是否拥有符合表达式的关键物品,默认 >0满足时
if_race种族 ID检查 actor 是否为对应种族满足时
if_tag标签检查 actor 是否拥有 Chara 行中定义的标签满足时
if_zone区域 ID, 层数(可选)检查 actor 是否处于指定区域(可选检查层数)满足时
if_zone_2区域全名使用区域全名语法检查 actor 是否处于指定区域,例如 derphy@-1满足时

特殊方法

方法参数说明跳转条件
console_cmd控制台命令 参数1 参数2...执行控制台命令总是
evalC# 脚本或使用 <<<path.cs 语法指定文件执行 C# 脚本。推荐使用 eval 动作(而非 invoke* 调用 eval()返回 true
andand(if_flag(flag1, >0), if_flag(flag2, <0)...)将 invoke* 表达式作为参数,评估全部条件全部满足时
oror(if_race(lich), if_race(snail)...)将 invoke* 表达式作为参数,评估全部条件任意满足时
notnot(if_zone(dungeon), if_zone(field), if_zone(underground)...)将 invoke* 表达式作为参数,评估全部条件全部不满足时

API

Elin 提供了简单的 API,允许你从自己的脚本 DLL 中添加自定义拓展方法。

动作解析器

解析器在加载剧情时被调用,负责处理非内置的已注册动作行,并创建事件。

cs
[ElinDramaActionParser("my_action")]
public static bool ExampleParser(DramaManager dm, Dictionary<string, string> line) 
{
    dm.AddEvent(new DramaEventTalk(line["actor"], () => {
        // 处理逻辑
        return "一行文本!";
    }));
    // 该行已处理
    return true;
}
// 或手动注册
CustomDramaExpansion.AddDramaActionParser("my_action", ExampleParser);

动作调用

调用方法会自动编译为 invoke* 表达式,并在剧情执行该行时被调用。

cs
[ElinDramaActionInvoke]
public static bool add_item(DramaManager dm, Dictionary<string, string> line,
                            string itemId,
                            string materialAlias = "wood",
                            int lv = -1,
                            int count = 1)
{
    var chara = dm.GetChara(line["actor"]);

    var mat = sources.materials.alias.TryGetValue(materialAlias, "wood");
    var item = ThingGen.Create(itemId, mat.id, lv).SetNum(count);
    chara.Pick(item);

    return true;
}

[ElinDramaActionInvoke("nodiscard")]
public static bool if_element(DramaManager dm, Dictionary<string, string> line,
                              string elementAlias,
                              DramaValueExpression expr) 
{
    var chara = dm.GetChara(line["actor"]);
    return chara.HasElement(elementAlias) && expr.Compare(chara.Evalue(elementAlias));
}
cs
[ElinDramaActionInvoke]
public static bool console_cmd(DramaManager dm, Dictionary<string, string> line, 
                               params string[] parameters)
{
    string.Join(' ', parameters).EvaluateAsCommand();
    return true;
}

剧情调用方法必须为 static,返回 bool,参数以 DramaManager dm, Dictionary<string, string> line 开头。

实际表达式参数可由 Elin 自动转换,或以 string[] 形式传入。可自动转换的参数包括内置数据类型、DramaValueExpression 或具有 static bool TryParse(string, out T) 方法的自定义类型。

更多示例请参阅 CustomDramaExpansion 的实现。

脚本

你可以在剧情表中使用 eval 动作直接运行 C# 代码

它提供与常规 C# 相同的脚本能力,但存在以下差异:

  • 脚本状态与当前剧情实例绑定(会持续到剧情结束,然后自动重置)。
  • 快捷方式:dm = DramaManager,line = 当前行(Dictionary<string, string>),tg = 目标 Charapc = 玩家 Chara

返回值行为:

  • 返回 bool + 有效的 jump 目标 → 决定是否执行跳转。
  • 返回 string + jump 单元格设置为 eval_result → 使用该字符串作为新的跳转目标。
  • 没有返回值 → 被视为普通动作执行。

可使用以下方式从同一文件夹导入脚本文件:<<<script_snippet.cs

传递变量

使用共享的 Script 字典:

cs
// 第一次 eval
var value = EClass.rnd(100) * 5;
Script["random_value"] = value;

// 后续 eval
var value = (int)Script["random_value"];

常用示例

功能代码
跳转到指定步骤dm.Goto("my_new_step");
添加“来聊聊吧!”选项dm.InjectUniqueRumor();
添加临时对话dm.AddTempTalk("topic", "actor", "jump");
获取 Chara 实例var chara = dm.GetChara("tg");
招募到队伍chara.MakeAlly();
修改等级chara.SetLv(chara.LV + 5);

需要帮助?请在 Elona Discord 上联系:@freshcloth通过邮件

This project is an unofficial documentation site and is not affiliated with, endorsed by, or associated with Elin or Lafrontier / Noa. All trademarks are the property of their respective owners.