这是一个测试项目,尝试开发一个小型的聊天用AVG文字冒险游戏制作器,以qq作为平台,依赖nonebot框架互动特性制作文字冒险类游戏。
目前由于nonebot难以支持大型化的文字活动,故将文字冒险游戏剧本制作为xml形式存储,依靠插件读取的方式导入剧本。
app/plugins/Story文件夹下的data_source.py文件为剧本导入与事件处理插件
_init_.py是普通的Nonebot插件文件。data_source定义了Story类作为游戏进度类,在init当中初始化Story类后使用Story.action(msg)来传入用户会话。
目前仅支持nonebot框架(因为用过的qq机器人框架就两个xwx)
会尽早把剧本编辑器肝出来
详见app/plugins/Story文件夹下的剧本模板.xml文件,注意文字编码格式UTF-8,记事本打开
该剧本主要由事件组成,一个事件即为<response>的一个字段,由chapter_name,key,content,flag以及method组成
chapter_name即为章节编号,从1开始计数,用于区分不同章节的事件。同一章节内的事件属于平行事件,会对用户的回复发出同等级别的响应。如果同时存在两个匹配用户回复的事件,则两个事件随机选取一个进行回复。
chapter_name作为玩家状态的一个属性,记录在Story.process中,表示玩家当前所处章节编号。只有符合当前章节号的事件才会响应玩家的回复。
chapyer_name可以在事件的method中由end_chapter、jump_to_chapter两个事件方法来改变。
key是事件响应玩家回复所依据的关键词。简单来说,当玩家回复的语句中包含关键词时,该事件就会被激活,随后依据flag条件来决定最终响应玩家的response
key字段可以使用or and not 三种逻辑来连接。
or逻辑在三个逻辑连接中优先度最低,使用or连接关键词或关键词表达式时,符合or两边的式子任意一个匹配成功即可响应用户回复。
例如 “石头 or 石块” 将会匹配“拿起石头”或“移动石块”等句子。
and逻辑将会把式子两边的关键词结合在一起,当且仅当用户回复中同时包含两边的匹配时才可响应用户回复,且结合优先于or。
例如 “拿起 and 石头 or 移动 and 石块” 将会匹配“拿起这块石头”或“移动这个石块”等句子,但无法匹配“拿起这个石块”这个句子。
(暂时不支持使用括号对表达式进行分割的操作,将会在接下来的更新中尽快实现)
not 逻辑只结合右边的单个关键词,且优先级最高,一个not只负责一个关键词的否定。not后的关键词若出现在玩家回复中,将不会响应该回复。
例如 “拿起 and not 石块 or 移动 and not 石头” 将不会匹配“拿起这个石块”与“移动这个石头”等句子。
not语句支持括号分割,例如 (not 石块) 或是 not (石块) 的写法都可以解析为not 石块,用于方便辨认逻辑。
content是事件响应的主体部分,里面的内容就是事件匹配到用户回复后,QQ机器人发送给用户的文字信息。content支持CQ码,与正常的QQ机器人回复规则相同。
content目前可以使用 %property_name 的格式来格式化回复内容,其中 %property_name 将会被替换为Story.property字典中的属性数值。当前属性仅支持数字。如果想在content当中加入%符号,使用%%来代替。
flag是用于限制已经匹配上key的事件的响应情况的标志。简单来说,玩家在游戏进行过程中可以为其添加或删除flag,而只有满足事件内flag条件的事件才会对玩家的回复产生响应。
flag的匹配式也包含or 与 and,且匹配方式与key相同,但不包括not。原因是flag匹配为精准匹配,not flag即可以省略为不写此flag。匹配的对象为玩家自身属性,只有满足拥有该flag的玩家才会被匹配成功。
灵活使用flag可以让游戏方式与剧情多变化,增加与删除目前是主要的推进剧情发展的方式。(接下来的版本更新中将加入next字段,用于强关联两个线性事件,将简化设置flag推进剧情的工作量)
剧本中的method字段可以用来执行一些变量修改增删等操作,是实现复杂游戏机制的主要手段。method的操作与操作之间用 ; 进行分割,可同时进行多个操作,操作的执行顺序为排列顺序(当前无时序性问题出现)。
举例:flag_on(eat);send(当前状态:血量 %HP 魔法 %MP 金钱 %Money);delay(0.5);send(花费 10 元,购买 饼干x1, 回复血量 10);property_add(HP, 10);send(当前状态:血量 %HP 魔法 %MP 金钱 %Money);
事件响应后执行:玩家获得名为eat的flag;收到消息“当前状态: 血量 90 魔法 80 金钱22”;0.5s后收到消息“花费 10 元,购买 饼干x1, 回复血量 10”;HP属性数值增加10;收到消息“当前状态:血量 100 魔法 80 金钱 12”。
用法 send(发送内容);
例:send(测试用例);
结果:收到消息 “测试用例”使用原则与content一样,但会在content之后再执行,分成不同的消息发送。
当前已支持格式化字符串。
用法 flag_on(flag名称);
例:flag_on(start);
结果:玩家被添加名为start的flag标记不能添加重名的flag
用法 flag_off(flag名称);
例:flag_off(start);
结果:玩家拥有的start标记被移除不能删除不存在的flag,请注意当操作未执行时是否输错flag的名称。
用法 property_create(属性名称, 属性数值);
例:property_create(HP, 100);
结果: 玩家拥有属性HP,数值为100.同时,property_create支持批量添加属性的操作,简化初始化属性的工作量。
具体用法 property_create([属性1:数值1],[属性2:数值2],[属性3:数值3]);
例:property_create([HP:100],[MP:80],[Money:22]);
结果:玩家添加了HP,MP,Money三种属性及数值
用法 property_del(属性名称); 或 property_del(属性名称1, 属性名称2, 属性名称3);
例:property_del(HP, MP);
结果:玩家的HP与MP属性被删除。
用法 property_change(属性名称, 属性数值); 或 property_change([属性1:数值1],[属性2:数值2]);
例:property_change([HP:99],[MP:37]);
结果:玩家的HP被调整为99,MP被调整为37与create基本一样,不同的是create只能创建尚未有的属性,而change不能改变尚未有的属性。
用法 property_add(属性名, 数值);
例:property_add(HP, -10);
结果:玩家HP属性减10。注意:考虑到批量增加看起来太过愚蠢,add没有批量添加的功能,且就三个字母也省不到哪里去。且增加数值可以是负数,即减去相应的数值
用法 end_chapter();
例:end_chapter();
结果:当前玩家所在章节数+1.相当愚蠢的功能,完全是为了给懒得打jump_to_chapter的人用的,滥用可能导致忘记章节到了什么地方,所以推荐使用jump_to_chapter
用法 jump_to_chapter(章节编号);
例:jump_to_chapter(3);
结果:玩家当前章节跳转到3.善用jump_to_chapter可以达到一些特别的效果,比如设计两个非线性的章节,分别作为不同的场景地图,玩家这两个场景地图之间可以通过jump_to_chapter来自由切换跳转
用法 delay(秒数);
例:delay(2.3);
结果:在上一条指令执行之后,延迟2.3s后执行下一条指令。一般用作两个send之间,模拟正常人的打字间隔。用在其他的指令之间不仅毫无意义且可能造成不必要的时间浪费