关于使用铃心完成的文游功能其内部的“代码”结构介绍。
编辑于2020.3.13 转载请注明作者
更像是一个“小黄鸭”帖子,只是我一个人的呓语而已,如果对大家有帮助,那就再好不过了。
核心设计部分
基本的读取文本结构#回复块
因为文游系统是基于大量文本和模组的,因此不能全部储存在配置.ini内,需要一个外部读取标准格式文件的方案。
关于读取外部文件的方式,一般有以下几个方案,分别是:
- 配置文件形式(对于较少和结构简单的文本)
- JSON形式
- 自定义文本形式(. . .嗯)
这里我采用了Json结构,并且写出了一个最简单的样例:
1 2 3 4 5 6 7 8 9 10 11 12 |
{ "data": "2019-3-1 00:00,2019-3-7 24:00", "title": "测试", "text": [ { "step": "0", "reply": "这是一个标准结构的消息[w5]就是这样[h2]很标准的结构", "yt": "1", "nt": "1" } ] } |
其中,我们需要理解几个关键词的含义
1 2 3 4 5 6 |
#step是代表这是第几个回复(数值等于当前是列表中的第几个元素,方便后期更改排序) #reply代表的是在这个回复下调用什么样的文本和等待 #其中[w5]代表延时5秒回复(wait 5) #[h2]代表挂起2分钟回复 #yt代表回复了是之后跳转到哪个回复 #nt代表回复了否之后跳转到哪个回复 |
如果采用配置文件的方式,也可以构建出这样的简单回复系统,并且从某种角度上来说,用配置文件构造出来的更加方便后续的编写。
那么现在就要开发读取这个形式回复的铃心代码了。
1 2 |
#游戏流程读取操作 【赋值变量返回值>=<【反转义【Json解析【常量路径】\main\【常量【发送者QQ】文件名】.json>=<text[【常量【发送者QQ】步数】].reply】】】 |
上面的代码只能够读取reply中的的回复内容,并不能将[w]和[h]给进行变换。
而且其他的回复,虽然表面看起来是可以直接输出然后后面自动延时,但是实际上必须要用【输出流】进行输出,否则会进行同时的延时。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
【赋值变量返回值>=<【反转义【Json解析【常量路径】\main\【常量【发送者QQ】文件名】.json>=<text[【常量【发送者QQ】步数】].reply】】】 #进行输出流的替换转化 【循环20>=< #循环替换其中的保留符号 【判断【赋值变量中间值>=< #对[w]的替换 【正则\[w([\d])+\]>=<【变量返回值】>=<1】】【变量中间值】>=<>=< 【赋值变量返回值>=< 【替换【变量返回值】>=<【变量中间值】>=<#zzk延时【取整【变量中间值】】#yzk】 】 >=< #对[h]的替换 【判断【赋值变量中间值>=<【正则\[h([\d])+\]>=<【变量返回值】>=<1】】【变量中间值】>=<>=< 【赋值变量返回值>=<【替换【变量返回值】>=<【变量中间值】>=<#zzk挂起【取整【变量中间值】】#yzk】】 #如果匹配为空则跳出 >=<【跳出】 】 】 】 #比较弱智的转化方案 【赋值变量返回值>=<#zzk输出流【替换【变量返回值】>=<#yzk||#zzk延时||#zzk挂起>=<#yzk#zzk输出流||#yzk#zzk延时||#yzk#zzk挂起】#yzk】 #在这种方案下,无法使用【】这两个符号 |
在这种方案下,reply内部是可以调用铃心代码的,因为在
1 |
【赋值变量返回值>=<【反转义【Json解析【常量路径】\main\【常量【发送者QQ】文件名】.json>=<text[【常量【发送者QQ】步数】].reply】】】 |
这个读取赋值的过程中存在一个读取、一个反转义。读取的时候,会将reply内部所有的【】转化成#zzk和#yzk,而在反转义执行之后,就又会将读取到的#zzk和#yzk首先转义成【】。
紧接着,根据铃心的执行顺序,在赋值的时候就会执行一遍reply中的代码,因此此时就会产生一个结果。
1 2 3 4 5 6 7 8 |
#比如说reply为下文 "reply":"我好累,要是有【随机数1-100】ml的水就好了[w5]救救我...今天才是【现行日期】,我还不想死——!" #那么读取之后的结果如下 我好累,要是有#zzk随机数1-100#yzkml的水就好了[w5]救救我...今天才是#zzk现行日期#yzk,我还不想死——! #经过反转义后又恢复原样 我好累,要是有【随机数1-100】ml的水就好了[w5]救救我...今天才是【现行日期】,我还不想死——! #赋值之后,返回值的实际值 我好累,要是有53ml的水就好了[w5]救救我...今天才是2020年3月13日,我还不想死——! |
但是尽管可以在reply中调用铃心的代码,但是使用很多的铃心代码难免会遇到使用[]的时候,进而导致一些难以预料的混淆情况。
为了避免出现混淆,我们可以在Json中添加一个run的代码块,在reply执行之前优先处理run中的铃心代码,然后在reply中仅仅调用【变量】之类的简单代码块,简化结构。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
#经过修改的样例 { "data": "2019-3-1 00:00,2019-3-7 24:00", "title": "首次", "text": [ { "step": "0", "reply": "这是一个标准结构的消息,这次的是通向的是【变量是】[w5]还可以挂起[h2]就这样", "run": "【赋值变量是>=<【随取1>=<2】】" "yt": "2", "nt": "1" } ] } |
格式修改成这个样子之后,我们需要进一步完善上面我们已经完成的体系。
1 2 3 |
#运行run代码块并且将返回值赋值给执行值 【赋值变量执行值>=<【反转义【Json解析【常量路径】\main\【常量【发送者QQ】文件名】.json>=<text[【常量【发送者QQ】步数】].run】】】 #在整个运行流程前添加这部分 |
这样,基本的读取方式和执行方式我们就添加完成了!
“是”、“否”跳转结构
初步的想法就是,当输入是之后就会触发关键词,通过已知的常量进行跳转,调用上文1-1提到的基本读取执行模块就可以了!
因此有了以下的代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
#输入是的时候 【判断【读配置【变量路径】\record\开关.ini>=<【发送者QQ】>=<switch>=<0】>=<0 >=< #判断当前是否处于防重发状态 【判断【常量【发送者QQ】防重发开关】>=<1>=<>=<【返回】】 #判断常量中是否赋值有文件名称,如果没有则进行重新赋值 【【判空【常量【发送者QQ】文件名】>=<【赋值常量【发送者QQ】文件名>=<【读配置【常量路径】\record\记录.ini>=<【发送者QQ】>=<title>=<测试】】】】 #将步数重新赋值,防止出现预料外的情况 【赋值常量【发送者QQ】步数>=<【读配置【变量路径】\记录.ini>=<【发送者QQ】>=<step>=<0】】 #从Json文件中获取该步输入“是”之后的步数 【赋值常量【发送者QQ】步数>=<【判空【Json解析【常量路径】\main\【常量【发送者QQ】文件名】.json>=<text[【常量【发送者QQ】步数】].yt】>=<1】】 #写出step,为下一次输入做准备 【写配置【常量路径】\record\记录.ini>=<【发送者QQ】>=<step>=<【常量【发送者QQ】步数】】 #调用回复块 【赋值常量【发送者QQ】防重发开关>=<1】 【回复#回复块】 】 |
首先解释这里的【防重发开关】的作用。
要知道,以时间为推进的文游,很容易出现一种情况——机器人话还没说完,玩家就输入了下一步的“是”或者“否”,进而导致了提前触发的情况出现,最后游戏混乱爆炸——
因此,需要一个防重发开关,在检测到重发之后,就直接【返回】(相当于!null)终止掉后文的调用。
为了让防重发开关能够被正常重置,在1-1的输出流结尾应该有一个赋值常量部分
1 2 3 |
【赋值变量返回值>=<#zzk输出流【替换【变量返回值】>=<#yzk||#zzk延时||#zzk挂起>=<#yzk#zzk输出流||#yzk#zzk延时||#yzk#zzk挂起】[over]#zzk赋值常量#zzk发送者QQ#yzk防重发开关#fgf0#yzk#yzk】 #最后的结构为 【输出流xxxx】【延时5】【输出流xxx】【挂起2】【输出流xxxxx[over]【赋值常量【发送者QQ】防重发开关>=<0】】 |
除了防重发开关之外,细心的你可能也注意到了——在最外层套了另一个开关系统。这个开关的目的是为了解决文游结束时,触发“是”和“否”对于游戏的影响。
对于“否”跳转,跟“是”跳转采用的是同样的逻辑,只是把Json读取改成了读取nt而已。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
#输入否的时候 【判断【读配置【变量路径】\record\开关.ini>=<【发送者QQ】>=<switch>=<0】>=<0 >=< #判断当前是否处于防重发状态 【判断【常量【发送者QQ】防重发开关】>=<1>=<>=<【返回】】 #判断常量中是否赋值有文件名称,如果没有则进行重新赋值 【【判空【常量【发送者QQ】文件名】>=<【赋值常量【发送者QQ】文件名>=<【读配置【常量路径】\record\记录.ini>=<【发送者QQ】>=<title>=<测试】】】】 #将步数重新赋值,防止出现预料外的情况 【赋值常量【发送者QQ】步数>=<【读配置【变量路径】\记录.ini>=<【发送者QQ】>=<step>=<0】】 #从Json文件中获取该步输入“是”之后的步数 【赋值常量【发送者QQ】步数>=<【判空【Json解析【常量路径】\main\【常量【发送者QQ】文件名】.json>=<text[【常量【发送者QQ】步数】].nt】>=<1】】 #写出step,为下一次输入做准备 【写配置【常量路径】\record\记录.ini>=<【发送者QQ】>=<step>=<【常量【发送者QQ】步数】】 #调用区块循环 【赋值常量【发送者QQ】防重发开关>=<1】 【回复#回复块】 】 |
文游开始&结束声明
那么,我们有了基本的读取模块和跳转模块,剩下的就是结束模块和开启模块了。
结束模块的书写可以说是相当简单——你只需要让跳转模块最外层的开关关闭,就可以达到结束模块的目的了(当然你也可以进行一些恢复初始量的操作,不过定义初始量我还是建议在开始的时候声明而非结束)
或者你可以干脆像我一样把其作为一个保留词装在run块里
1 2 3 4 |
#运行run代码块并且将返回值赋值给执行值 【赋值变量执行值>=<【反转义【Json解析【常量路径】\main\【常量【发送者QQ】文件名】.json>=<text[【常量【发送者QQ】步数】].run】】】 #如果含有[end],就重置开关 【判含【变量执行值】>=<[end]>=<>=<【写配置【常量路径】\record\开关.ini>=<【发送者QQ】>=<switch>=<0】】 |
这样,只需要在run声明一个[end],整个文游就会终止于这个回复——或者说,文游就可以在此重新开始。
以下为进入文游的模块。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
#反向判断,保证在开关未关闭的情况下,游戏不会重开 【判断【读配置【常量路径】\record\开关.ini>=<【发送者QQ】>=<switch>=<0】>=<0 >=<【返回】 >=< #进入文游部分 【赋值常量路径>=<【运行目录】\app】 #将开关切换为开启状态,即1 【写配置【常量路径】\record\开关.ini>=<【发送者QQ】>=<switch>=<1】 #随机取出一个剧本 【写配置【常量路径】\record\记录.ini>=<【发送者QQ】>=<title>=<【取出一行【常量路径】\record\随机表.txt>=<0】】 #给予回复块一个初始值 【赋值常量【发送者QQ】文件名>=<【取出一行【常量路径】\record\随机表.txt>=<【行序】】】 【赋值常量【发送者QQ】步数>=<0】 【写配置【常量路径】\record\记录.ini>=<【发送者QQ】>=<step>=<0】 #执行回复块 【回复#回复块】 】 |
附加设计部分
人物状态部分
在制作这个的过程中我一直都在想怎么增加游戏性——随后我就意识到,可以让玩家随时查询对象的状态(state)来猜测对方有没有说谎之类的。为整个剧本带来更多的活力。
因此,就有了下文的代码。
注意:由于我是在编辑器上进行编写,因此函数部分仍旧是以{@}的形式表述。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 |
#参数1为指定的类别,参数2为输入的文本 {@单个字符处理}={ #为等号时进行第一步处理 【赋值变量处理值>=<【子正则\[[参数1]\=([^\]]+)\]>=<[参数2]>=<1】】 【判断【变量处理值】>=<>=< 【写配置【常量路径】\record\记录.ini>=<【发送者QQ】>=<[参数1]>=<【变量处理值】】 >=< 】 #为加减乘除时进行第二步处理 【赋值变量处理值>=<【子正则\[[参数1]([\+\-\#xh\/][^\]]+)\]>=<[参数2]>=<1】】 【判断【变量处理值】>=<>=< 【写配置【常量路径】\record\记录.ini>=<【发送者QQ】>=<[参数1]>=< 【计算【读配置【常量路径】\record\记录.ini>=<【发送者QQ】>=<[参数1]>=<0】【变量处理值】】 】 >=< 】} #保留字符处理函数 {@字符处理}={ #心跳中心值设定 #心跳浮动值设定 【判含[参数1]>=<hb>=<>=<{@单个字符处理hb>=<[参数1]}{@单个字符处理hbf>=<[参数1]}】 #体温中心值设定 #体温浮动值设定 【判含[参数1]>=<pt>=<>=<{@单个字符处理pt>=<[参数1]}{@单个字符处理ptf>=<[参数1]}】 #精神压力中心值设定 #精神压力浮动值设定 【判含[参数1]>=<mp>=<>=<{@单个字符处理mp>=<[参数1]}{@单个字符处理mpf>=<[参数1]}】 #状态描述 【判含[参数1]>=<state>=<>=< 【赋值变量处理值>=<【子正则\[state\=([^\]]+)\]>=<[参数1]>=<1】】 【判断【变量处理值】>=<>=< 【写配置【常量路径】\record\记录.ini>=<【发送者QQ】>=<state>=<【变量处理值】】 >=< 】】 #地点描述 【判含[参数1]>=<loc>=<>=< 【赋值变量处理值>=<【子正则\[loc\=([^\]]+)\]>=<[参数1]>=<1】】 【判断【变量处理值】>=<>=< 【写配置【常量路径】\record\记录.ini>=<【发送者QQ】>=<loc>=<[CQ:location,【变量处理值】]】 >=< 】】 } #查看人物状态 【判断【读配置【常量路径】\record\开关.ini>=<【发送者QQ】>=<switch>=<0】>=<0 >=< HeartBeats: 【计算【读配置【常量路径】\record\记录.ini>=<【发送者QQ】>=<hb>=<90】【随取+>=<-】【随机数1-【读配置【常量路径】\record\记录.ini>=<【发送者QQ】>=<hbf>=<5】】】 beats per minute【换行】 PhysicalTemperature: 【计算【读配置【常量路径】\record\记录.ini>=<【发送者QQ】>=<pt>=<36.5】【随取+>=<-】【计算【随机数1-【计算【读配置【常量路径】\record\记录.ini>=<【发送者QQ】>=<ptf>=<0.5】×10】】/10】】 ℃【换行】 MentalPressure: 【计算【读配置【常量路径】\record\记录.ini>=<【发送者QQ】>=<mp>=<0】【随取+>=<-】【随机数1-【读配置【常量路径】\record\记录.ini>=<【发送者QQ】>=<mpf>=<5】】】 mHU【换行】 State: 【读配置【常量路径】\record\记录.ini>=<【发送者QQ】>=<state>=<Fine】 >=<——无法访问—— 】 |
在这里,制作者可以通过一个state块来声明角色的当前状态,并且将其保存在文件中。
玩家可以在任何时候查看这些state并且猜测作者的意图。
除此之外,还加入了对一些卡片的兼容。
位置卡片兼容
1 2 3 4 5 |
#人物所在地点反馈 【判断【读配置【常量路径】\record\开关.ini>=<【发送者QQ】>=<switch>=<0】>=<0 >=< 【读配置【常量路径】\record\记录.ini>=<【发送者QQ】>=<loc>=<[CQ:location,lat=0,lon=0,title=Unknown,content=Error]】 】 |
接着上面的想法,后续可以做一个查询人物当前地理位置的功能——又或者说让对方主动地发送位置给玩家?
不管怎么样,位置卡片会让整个游戏变得更加真实且可信,更容易产生互动感。
相类似的,制作者也可以加入音乐卡片之类的来提供BGM——不过关于BGM我更推荐将其直接放在reply中发送出去。
于是乎,我们完整的Json结构终于确定下来了:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
{ "data": "2019-3-1 00:00,2019-3-7 24:00", "title": "首次", "text": [ { "step": "0", "reply": "这是一个标准结构的消息【常量是】[w5]还可以挂起[h2]就这样", "state": "[hb=80][hbf=30][lat=0,lon=0,title=Unknown,content=Error]", "run":"【赋值常量是>=<【随取1>=<2】】", "yt": "【常量是】", "nt": "1" } ] } |
至此,关于新版本的文游系统介绍,就可以告一段落了。
Comments | 1 comment
我学到了很多.jpg