Skip to content
This repository has been archived by the owner on Nov 3, 2023. It is now read-only.

Latest commit

 

History

History
5500 lines (3840 loc) · 181 KB

SealDice指令手册.md

File metadata and controls

5500 lines (3840 loc) · 181 KB

SealDice: 指令手册

image-20220313120222613

概述

一个简单易用的跑团骰子系统。

形象是海豹,可以被叫做海豹骰、豹骰,豹子骰之类。

现在试试 (官网首页,点在线试用)

跑团速通攻略 (我是PL,告诉我怎么用就好)

搭建攻略 (我是骰主,告诉我怎么搭建)

MOD和自定义 (我是MOD作者,有什么好玩的?)

特性:

  • 点击运行,一键部署
  • 非常友好的界面
  • 与现有主流骰系极为相似的指令
  • coc7 dnd5e fate指令支持
  • 简单强大的自定义功能
  • 牌堆等娱乐功能
  • 极低的资源占用,程序包20MB,占用内存约30MB
  • 支持Windows/Linux/macOS系统,x64和arm64架构
  • 每12小时自动备份(间隔可调整),也可以通过指令备份,不丢失任何数据!
  • Golang编写

目前已经基本实现了主流骰子提供的指令,主要参考 ZhaoDice(赵骰)Dice!(溯洄系)SinaNya(塔系)三系骰子的手册和表现。

为了方便使用,默认随项目附带了 go-cqhttp的二进制文件和许可协议。

快速跳转

指令快速上手

查看特色功能

搭建海豹核心

联系方式&官方群

骰子自定义

COC团使用

DND团使用

已知支持规则

手册信息

本手册适用于1.0.2及以上版本

最后更新: 2023-09-17

手册使用: CC BY-NC-SA 4.0协议

源码地址: https://github.com/sealdice/manual

手册由制作组以及网友共同编写。其中难免错漏以及不实之处,或随版本更迭过时之处,欢迎在源码地址提交issue指出。或提交PR来补充手册内容,非常感谢

提要

手册中的所有指令都将以标准格式书写,实际使用时宽松许多。

但适当空格是好习惯,不是吗?

本手册的指令使用这样的格式来进行介绍:

.指令 <参数A> <参数B> (参数C)

其中,<>括起来的内容代表”某个概念“,实际指令不需要加 <>

()括号括起来的内容代表这是一个可选参数,实际指令也不需要加 ()

以骰点指令举例:

.r <表达式> (<原因>)

<表达式>是必填内容,譬如是d100,原因是可选内容

.r d100 高于50就打扫卫生

会得到这样的结果:

由于高于50就打扫卫生,<木落>掷出了 d100=[1d100=90]=90

接下来,本手册的指令示例都将以这种形式书写:

.r d100 高于50就打扫卫生  // 这部分是解说,不算在指令里
Dice: 由于高于50就打扫卫生,<木落>掷出了 d100=[1d100=90]=90

第一行为指令,第二行Dice:开头的代表骰子的回复,直到末尾。

阅览建议

强烈建议在阅读本文档时,展开左侧的目录

image-20220313162007619

有些文档是面向骰主的,可以快速跳过自己不需要的章节。

常见问题 (FAQ)

> 和其他核心的不同之处

参见特色功能一节

角色卡有一个区别就是每个群都有一张单独的空白卡,所以单群很快跑完的短团,你可以直接st录入属性再nn录入角色名之后就开跑。

详见 角色卡指令,即pc的区别

> 为啥海豹不理我?

可能性一:私聊非好友

可能性二:遭到了风控发不出来。

可能性三:请检查当前开启的扩展,例如当coc7扩展关闭时,sc等指令是不会回应的。请运行 .ext coc7 on开启对应扩展。

骰主可以后台查看骰子接收到的指令和反馈,风控也会提示。

另外骰主在海豹后台可以直接试指令。

如果确实是没反应!!请加入海豹群反馈!

> 可以关闭暗骰吗?

海豹不会去私聊非好友帐号,所以不必担心暗骰会影响帐号安全。

> 退群指令?

.bot bye 或 .bot exit 或 .dismiss

骰主也可以后台在群组管理中操作退群。

> 登录服务器开浏览器使用好麻烦!能远程直接访问UI吗?

可以,对于大部分VPS,可以使用http://IP:3211 (IP是你服务器的IP)直接访问后台

像是腾讯云轻量服务器,需要进行设置“服务器管理 - 防火墙 - 添加端口 3211,TCP

白嫖的Oracle Linux服务器,不仅需要后台设置,还需要放行端口:

sudo iptables -I INPUT -s 0.0.0.0/0 -p tcp --dport 3211  -j ACCEPT

Windows服务器也一样,需要额外关闭内置防火墙,或者设置端口放行。

千万记得在基本设置中设置密码!!!

> 在服务器上登录时说“需要在同一WIFI下扫码”怎么办

首先确认使用安卓协议,并且输入密码登录(不要使用无密码的扫码登录,现在已经很不好用了)。

有几种办法:

  1. [首选] 让手机跟服务器真的处于同一个网络

    此方法本来是方案3,随着时间推移以及审核的严格,现在逐渐成为了最佳方案。

    这个办法略微繁琐,不过好在一劳永逸。

    步骤一: 配置防火墙

    以腾讯云轻量服务器为例,点入“服务器”,再点击卡片。

    image-20221009004030395

    进入后点击“防火墙”,随后“添加规则”,这里选择TCP协议,端口输入13325(演示图片中为13326),点击确定。

    image-20221009003612068

    搞定,其他服务商大同小异。

    已知需要做此配置的服务商有:腾讯云轻量服务器、阿里云、华为云

    步骤二:服务器端运行工具

    在V1.1.0 v20221231更新后,在海豹骰中集成了socks工具。

    点击图中 辅助工具-13325端口按钮。

    image-20230109202133901

    稍等片刻后,出现如下窗口。

    image-20230109203431758

    点击OK。

    V1.1.0 v20221231以下版本可以手动下载socks工具。

    如果你是windows服务器就下载 socks5_0.2_windows_amd64.zip,地址:

    https://github.com/fy0/socks5-tool/releases

    运行起来之后,显示一串这样的文本:

http/socks5简易工具©sealdice.com
将在服务器上开启一个socks5服务,端口13325,默认持续时长为25分钟
将在服务器上开启一个http代理服务,端口13326,默认持续时长为25分钟

可能的公网IP:  12.34.56.78
请于服务器管理面板放行你要用的端口(一般为13325即socks),协议TCP
如果是Windows Server 2012R2及以上系统,请再额外关闭系统防火墙或设置规则放行

正在启动服务: 0.0.0.0:13326
正在启动服务: 0.0.0.0:13325

特别的,如果是Windows Server 2012R2及以上系统,请再额外关闭系统防火墙或设置规则放行。具体做法搜索关键字 Windows server 关闭防火墙

步骤三:手机端连接

你需要一个代理软件,比如postern程序,如果可能的话你可以从Google play下载,你也可以在用户群里找到它。

然后在你将要扫码的手机上启动它,弹出的窗口都点击确定

点击默认存在在的代理,将他删除,然后点击添加代理服务器,按照下图填写。

image-20230109205906109

保存后点击左上角Postern的图标,点击配置规则,类似前文删除代理,删除掉默认的规则1和规则2,然后点击添加规则。如图选择匹配类型和动作,之后会自动填入代理/代理组。

image-20230109210448115

保存后重启软件。

现在就配置完成了,手机上打开浏览器进入 https://ip138.com/ 如果显示的IP地址和服务器一样,那么配置正确。如果打不开网页或提示"当前wifi不能访问网络",可能是防火墙没配置好。如果IP不一样,可能是代理没配置上。

如果你是IOS手机,可以使用Potatso替代Postern。

现在可以进入海豹UI,重新进行登录流程了。如果以后再出类似情况,直接重复步骤二、三即可

  1. 本机上完成扫码登录,然后将登录过的配置文件覆盖到服务器上。

    具体目录是 data/default/extra/go-cqhttp-qq12345

    当然如果你没做过自定义配置,可以整个复制。

  2. 使用密码登录,重试3次左右,有概率直接上号

> @其他骰子做检定的时候,海豹也会回应,怎么办

首先更新到0.99.8以后的版本。

使用这个指令将其他骰子进行标记:

.botlist add @骰子A @骰子B

会有如下效果:

  1. 当有人@该骰发指令,海豹不会做出回应
  2. 该骰发出的指令和消息,海豹不会回应,这可以有效避免关键词触发的无限复读

之所以不保留传统@效果,是因为海豹基于@功能做了一项扩展,其适用场景远多于同群多骰。

具体可以参见文档的这一节:ra/rc 指令 -> 对他人做检定

> 为啥好像反应慢一拍

首先确定是否为网络问题。其实指令计算是非常快的(可以在日志面板观察到)。

但是发送消息会有随机延迟,这是为了防止引起企鹅的警惕。

随机延迟间隔:0.4-0.9秒,可以在设置中调整。

> 我希望在进群或者被踢出时候通知我

见界面的“综合设置-基本设置”

在“消息通知列表”中可以添加。目标可以是群,也可以是个人。

通知内容:加群邀请、好友邀请、进入群组、被踢出群、被禁言、自动激活、指令退群。

> 支持QQ频道吗?

支持,邀请入频道后 @骰子 .bot on 即可开启。

默认不记录频道消息,也不自动开启(bot off状态)。

见“综合设置-基本设置”

特别的,骰子不会同意频道邀请,需要骰主上号同意。

> 为什么总是不出二维码就登录失败,或者提示 message:not found ?

请将软件完全解压后使用!

> 登录失败,code45/235/237?

推荐使用签名服务器。海豹在最近的更新后支持了签名服务器的配置。

详见使用签名服务器

> 怎么同时运行多个海豹?

以运行两个为例:

我们假设你第一只海豹已经配置好了,当前正在运行,端口为默认的3211。

现在,你需要找到安装包换个目录再解压一份海豹,我们叫他“海豹2”。

你进入海豹2目录直接双击执行,它会自动检测端口,因为3211端口已经被占用,所以他会弹窗问你要不要换端口,你直接点是就可以了。

如果你想自定义端口,就有点复杂:

在海豹1目录下创建一个"启动.cmd",用文本编辑器编辑,里面写:

sealdice-core --address=0.0.0.0:3211 -m

然后在在海豹2目录下如法炮制,也弄一个“启动.cmd”,端口号改一下:

sealdice-core --address=0.0.0.0:3212 -m

分别意思是第一只海豹开在3211端口,第二只开在3212端口。

分别使用"启动.cmd"运行两只海豹即可。首次运行后会记住配置。

如果运行更多,以此类推。

> Mac/Linux/树莓派可以搭建吗?

可以,见 Linux 部署Mac 部署

> 用iPad/安卓手表登录?

可能目前使用gocqhttp填写的设备参数不太对,导致iPad登录比较容易失败。

可以先在指定设备上,使用安卓协议登录,随后删除账号换用iPad协议登录,有概率成功。

如果有人熟悉gocqhttp上iOS设备的型号数据,希望不吝赐教。

> 我机器上启动非常慢,而且托盘点不出来右键菜单(1.0.0以后版本)?

请确认你的硬盘状态是否还好,一般来说是磁盘IO性能过低导致的,常见于软路由设备。

这种情况可以删除 data/helpdoc 来进行提速,代价是.find/.查询 查询指令出不来东西。

> 能在手机上搭建吗?

手机海豹已经公测啦!详见在手机上运行一章。

> 我是Master,怎么帮人取出log?

骰主代取LOG

骰主具有很大的权限,海豹一开始对骰主看LOG做了一定的限制,不过后来觉得这并无意义——因为骰主有终极方案,即上号。

所以还请各位选择自己信任的骰主,也请各位骰主谨慎对待大家的信任!

> 我忘了UI密码!

关闭海豹,用文本编辑器打开dice/dice.yaml,删除uiPasswordHash那一行重新启动。

速通攻略

如果你需要以PL身份参团,并快速掌握常用指令,可以遵循此节内容。

或者你是其他骰系的使用者,想要快速过一遍常用指令用法,这篇文档也适用于你。

当前内容以实际跑团过程为蓝本编写,适用于多数团。

快速上手

SealdiceRoll.png

COC玩家

开启COC模式(默认开启): .set coc // 注意中间有空格

1. 车卡录卡

临时创建人物/天命

.coc5

录卡 - 常规团

// 海豹各群数据独立,如果单群跑完全程,不需要开团前存卡!!!

.nn 角色名
.st 力量70str70敏捷70dex70意志70pow70体质70con70外貌50app50教育70知识70edu70体型70siz70智力70灵感70int70san55san值55理智55理智值55幸运50运气50

// 开始跑团 ....
// 模组跑完了,想在别的群用此人物,就存卡,否则不需要:
.pc save 角色名

录卡 - 秘密团

// 小群录卡
.pc new 角色名
.st 力量70str70敏捷70dex70意志70pow70体质70con70外貌50app50教育70知识70edu70体型70siz70智力70灵感70int70san55san值55理智55理智值55幸运50运气50

// 大群绑定人物
.pc tag 角色名

改群名片(骰子必须有管理员权限)

.sn coc

2. 骰点

.r 3d6*5

3. 检定

.ra 聆听
.ra 幸运70
.ra 3#p射击

4. 理智检定、掉血

.sc 0/1d4
.st hp-1d4

DND玩家

感谢逛街提供的指令思路和部分文案。

开启dnd模式: .set dnd

1. 录卡

.st 模板 // 或 .dst 模板

// 根据模板录入数值,下面只是示例。冒号不好写的话,可以写为等号
.dst 力量:10 体质:10 敏捷:10 智力:10 感知:10 魅力:10 hp:10 hpmax:10 ....

2. 骰点

.rd //掷一个默认骰(默认20面)

.rd+(<数量>) //掷一个默认骰,并+(<数量>)

.rd(<数量>) //投掷一颗数量指定面术的骰子

.r(<数量>) d(<数量>) //投掷指定数量、指定面数的骰子

.rd优势 //投掷两次骰子取优势

.rd劣势 //投掷两次骰子取劣势

.r(<数量>)#(<数量>) d(<数量>) +(<数量>) //掷指定数量、指定数值的骰子指定次

.rd
Dice:<木落>掷出了 d+1=3[1d20=3] + 1=4

.rd+1
Dice:<木落>掷出了 d+1=3[1d20=3] + 1=4

.r2d8+1
Dice:<木落>掷出了 2d8+1=3[2d8=3, 2+1] + 1=4

.rd8优势+1
Dice:<木落>d8优势+1=4[{4 | 3 }] + 1=5

.r 5#2d8+1
<木落>掷骰5:
2d8+1=13[2d8=13, 7+6] + 1=14
2d8+1=13[2d8=13, 7+6] + 1=14
2d8+1=11[2d8=11, 3+8] + 1=12
2d8+1=11[2d8=11, 3+8] + 1=12
2d8+1=8[2d8=8, 7+1] + 1=9

3. 检定

.rc 运动 // 结果为: 8[1d20=8] + 6[运动=6] = 14
.rc 优势 力量+4 // 结果为: 17[{17 | 11 }] + 12[力量=12] + 4 = 33

更多详情请参阅扩展-dnd5e一节,如死亡豁免、法术位、长休等指令。

核心指令

核心指令是无法被关闭的指令。

与之相对的概念是 扩展指令,扩展指令可以被关闭。

举例来说,输入 .ext coc7 off关闭coc7扩展,那么相关的指令,例如 .st .sc .ra .setcoc等就不会响应了。

关于扩展的概念,请阅读下方“扩展”一节。

特别提醒:

你可能会觉得指令很复杂,但其实,现在的骰子就是如此,我自己也被需求的规模吓了一跳。

如果你很熟悉赵系、溯回系、塔系,特别是前两者,那么90%的内容是一样的!

.r 骰点

格式: .r <表达式> (<原因>)

别名: .roll .rd

可用范围: 群内、私聊

常用示例

特别提醒一下,如果不写骰子算符(d/b/p,概念见下),r指令相当于计算器

.r
Dice: <木落>掷出了 D100=69

.r d50
Dice: <木落>掷出了 d50=[1d50=48]=48

.r d50 天气不错
Dice: 由于天气不错,<木落>掷出了 d50=[1d50=4]=4

.r 5d24 骰5个24面骰
Dice: 由于骰5个24面骰,<木落>掷出了 5d24=[5d24=60, 7+20+15+1+17]=60

.r 4d6k3 骰4个6面骰,选3个最大的
Dice: 由于骰4个6面骰,选3个最大的,<木落>掷出了 4d6k3=[{6 5 3 | 1 }]=14

.r 100 + 3 * 2
Dice: <木落>掷出了 100 + 3 * 2=100 + 6=106

奖励骰/惩罚骰

一类特殊的骰子,线下游戏时,D100是两个10面骰骰出来的,一个是个位,一个是十位。

如果十位骰为10,个位骰为1-9,那么结果是1-9(十位视为0)

如果两个10面骰都骰到10,那结果就是100

奖惩骰的机制是,多指定数量个十位骰,例如获得2个奖励骰,那么就丢3个十位骰。

假设十位骰结果是{4,5,8},个位骰结果是2,那么最终结果就是42(取好的结果)

如果是惩罚骰,那么最终结果是82(取坏的结果)

.r b
Dice: <木落>掷出了 b=[D100=72, 奖励 4]=42

.r b3
Dice: <木落>掷出了 b3=[D100=96, 奖励 4 6 3]=36

.r p4 惩罚骰
Dice: 由于惩罚骰,<木落>掷出了 p4=[D100=87, 惩罚 5 7 8 7]=87

多轮骰点

.r 4#d10
Dice:
<木落>掷骰4次:
d10=[1d10=9]=9
d10=[1d10=3]=3
d10=[1d10=7]=7
d10=[1d10=8]=8

DND5E优势骰劣势骰

.set 20
Dice:
设定默认骰子面数为 20
提示: 20面骰。如果要进行DND游戏,建议先执行.ext dnd5e on和.ext coc7 off以避免指令冲突

.r d20优势
Dice: <木落>掷出了 d20优势=[{7 | 6 }]=7

.r d劣势
<木落>掷出了 d劣势=[{16 | 18 }]=16

d20优势 = 2d20k1,即2个d20取结果高的一个

d20劣势 = 2d20q1,即2个d20取结果低的一个

fvtt骰点兼容

.r d20kh  // 优势骰(其实不是特别兼容)
<木落>掷出了 d20kh=[{10 | 3 }]=10

.r d20kl  // 劣势骰
<木落>掷出了 d20kl=[{6 | 15 }]=6

.r 4d6dl1 // 丢4排除1个最低值
<木落>掷出了 4d6dl1=[{5 3 2 | 1 }]=10

.r 4d6dh1 // 丢4排除1个最高值
<木落>掷出了 4d6dh1=[{3 3 5 | 6 }]=11

fate 命运骰

一种特殊的六面骰,六个面分别为 - - 0 0 + +,分别代表-1 0 1

骰点时投掷4次,加在一起为结果。例如某次结果是[-+++],为-1 + 1 + 1 + 1 = 2

.r f
Dice: <木落>掷出了 f=[---+]=-2

带补正的情况:

.r f+1
<木落>掷出了 f+1=0[+0-0] + 1=1

WOD/无限骰点

这是WOD骰点规则,国内更多见于无限团,默认使用d10。

这个规则需要设定几个参数:骰池数量X、加骰线Y、成功线N,其中XY是必须的,成功线默认为8

这是一个多轮骰点规则,骰X个d10,每有一个≥成功线的骰,成功数+1,每有一个≥加骰线的骰,加骰数+1,随后下一轮骰点使用上一轮的加骰数作为骰池数量。

.r 10a6k4m9
<木落>掷出了 10a6k4m9=[成功11/16 轮数:3 {1,<6*>,5*,3,<8*>,5*,<8*>,<6*>,2,<8*>},{5*,2,<9*>,1,4*},{5*}]=11

语法 XaYmZkN // X骰池数量,Y加骰线,m面数,N成功线(≥N记为一个成功)

特别的,若Y=0则不加骰,这也可以用到其他一些TRPG游戏中去。

同时,额外支持q参数(与k参数相对),可使用q5参数计算≤5的骰数

双十字骰点

这是一种多轮的骰点规则。一般为骰若干个d10,并指定暴击值。

如果达到暴击值,则进入下一轮,直到全部骰子都不能暴击。

最终的骰点结果为: 暴击轮数 * 10 + 最后一轮中最大的点数

.r 4c3m7
<木落>掷出了 4c3m7=[出目32/9 轮数:4 {<4>,2,<4>,<5>},{<7>,1,2},{<7>},{2}]=32

骰4个d7,暴击值为3。若不设置m则为4个d10

混合运算

.r 30 + (-1d20) + 49
Dice: <木落>掷出了 30 + (-1d20) + 49=30 + -1[1d20=1] + 49=78

.r d50 * 3 + 2
Dice: <木落>掷出了 d50 * 3 + 2=21[1d50=21] * 3 + 2=65

.r d50 * 3 + (2 - p2) 多项式
Dice: 由于多项式,<木落>掷出了 d50 * 3 + (2 - p2)=25[1d50=25] * 3 + -64[D100=6, 惩罚 6 5]=11

事实上,海豹核心支持了这些算符:

常用数学算符: 加减乘除(+-*/)、乘方(**)、括号

骰子算符: 多面骰(d, <次数>d<面数>(k<个数>)(q<个数>)),奖励骰(b, b<个数>)、惩罚骰(p, p<个数>)

用字母表示的骰子算符有些反直觉,其实它们和加减乘除是一样的:

.r 1d1d1d1d1d1d1d1d1d1d1d1d1d1d(20+1d3*4)
Dice: <木落>掷出了 1d1d1d1d1d1d1d1d1d1d1d1d1d1d(20+1d3*4)=13

.r 1d10+(1+32)d(4*6)d20
Dice: <木落>掷出了 1d10+(1+32)d(4*6)d20=1[1d10=1] + 3941[33d24=384,384d20=3941]=3942

.rh 暗骰

格式: .rh <表达式> (<原因>)

别名: .rhd

可用范围: 群内

这个指令的格式与 .r完全相同,只不过效果是在群里撂下一句提示,然后把骰点结果私聊发给你。

不过注意!由于企鹅的严打,陌生人私聊极其容易触发风控,所以私聊发送只会发给好友。如果你不是骰子的好友,看不到任何结果。

// 群内
.rh d50
Dice: 命运正在低语!

// 私聊
Dice:
来自群<某个无聊团>(123)的暗骰:
<木落>掷出了 d10=[1d10=3]=3

.rx / .rxh 特殊骰点

格式: .rx <表达式> (<原因>)

别名: 无

可用范围: 群内、私聊

这个指令和r指令的唯一区别是允许代骰,因此可以在骰点时读出属性:

.rx 聆听+d10 @木落
Dice: <木落>掷出了 聆听=[聆听=60] + 8[1d10=8]=68

.rx 侦查+1d20**2
Dice: <木落>掷出了 侦查+1d20**2=75[侦查=75] + 324[1d20=18]=399

.rx d(侦查)
Dice: <木落>掷出了 d(侦查)=[侦查=60,1d60=52]=52

.bot 骰子管理

格式: .bot on/off/bye/about

别名: 无

可用范围: 群内、私聊

较为简单的指令,直接看例子:

.bot on // 开启
Dice: <海豹bot> 已启用 SealDice 0.99内测版 v20220312

.bot off // 关闭
Dice: <海豹bot> 停止服务

.bot bye // 退群
Dice: 收到指令,5s后将退出当前群组

.bot // 或者 .bot about
Dice: SealDice 0.99内测版 v20220312
供职于8个群,其中3个处于开启状态
上次自动保存时间: 2022-03-13 04:57:55 UTC
群内工作状态: 开启

.nn 角色名设定

格式: .nn // 查看当前角色名

格式: .nn (<角色名>) // 修改角色名

格式: .nn clr // 重置角色名

别名: 无

可用范围: 群内

若有角色名参数,则进行设定。若无角色名,重置回默认名字。

.nn 简·拉基·茨德
Dice: <木落>(303451945)的昵称被设定为<简·拉基·茨德>

.r
Dice: <简·拉基·茨德>掷出了 D100=16

.nn
Dice: 玩家的当前昵称为: <简·拉基·茨德>

.nn clr
Dice: <木落>(303451945)的昵称已重置为<木落>

.set 设定游玩规则/骰子面数

格式: .set // 查看当前面数设置

格式: .set dnd // 设置群内骰子面数为20,并自动开启对应扩展

格式: .set coc/coc7 // 设置群内骰子面数为100,并自动开启对应扩展

格式: .set <面数> // 设定群内骰子面数

格式: .set <面数> --my // 设定个人专属骰子面数

格式: .set clr // 清除群内骰子面数设置

格式: .set clr --my // 清除个人骰子面数设置

别名: 无

可用范围: 群内、私聊

.set 20
Dice: 设定默认骰子面数为 20

.set coc
设定群组默认骰子面数为 100
提示:已切换至100面骰,并自动开启coc7扩展

.set dnd
设定群组默认骰子面数为 20
提示:已切换至20面骰,并自动开启dnd5e扩展。

.set info
个人骰子面数: 0
群组骰子面数: 20
当前骰子面数: 20

设定骰子面数,默认值是100

优先级: 个人 > 群 > 默认(100)

如果通过“规则模板”机制添加了自设规则,那么如有相关配置的话,可以使用.set切换为对应规则。

安装规则插件后,你可以通过.set help来查看新增的关键字。

.pc 角色卡管理

格式:.pc new <角色名> // 新建角色并绑卡

格式:.pc tag <角色名> // 当前群绑卡/解除绑卡(不填角色名)

格式:.pc untagAll <角色名> // 全部群解绑

格式:.pc list // 列出当前角色

格式:.pc save <角色名> // [不绑卡]保存角色,角色名可省略

格式:.pc load <角色名> // [不绑卡]加载角色

格式:.pc del/rm <角色名> // 删除角色

可用范围: 群内、私聊

请特别注意这个指令!!

海豹各群数据是独立的,也就是说相当于每个群都有一张空白卡。

因此如果你在单群进行游戏,那么并不需要录角色!也不会影响其他群的游戏!

.st clr // 如果这群有你的角色数据,先清掉
.nn 角色名
.st 力量50敏捷60

这样就可以直接开始游戏了。

如果跑完团想要在其他群使用这个角色,在使用 pc save 角色名进行存卡。

如果你跑秘密团,那么需要用到绑卡功能,在小群:

.pc new 角色名 // 创建角色,并绑卡
.st 力量50敏捷60

然后在主群:

.pc tag 角色名

就可以了!这样两个群的角色数据是联动的。

.find/.查询 查询指令

格式: .find <关键字> // 关键字可以多个,用空格分割

格式: .find <数字ID> // 显示该ID的词条

格式: .find --rand // 显示随机词条

别名: .查询

可用范围: 群内、私聊

海豹搭载了两个查询核心:

  • 全文搜索 - 提供强大的索引功能,能够查询文档内容中的关键字,但只能支持x86设备。文档多的话会拖慢启动速度。
  • 快速文档查找 - 只在标题中进行查找,速度很快。用于性能/内存受限的场景,以及手机端。

全文搜索!

来看看全文搜索吧,它非常强大,这是搭载了DicePP(梨骰)项目整理的查询资料后的效果:

.查询 30尺 法术

Dice:
全部结果:
[序号3066]【术士:超魔法:远程法术】 匹配度 0.16
[序号3015]【游侠:驯兽师:法术共享】 匹配度 0.13
[序号2396]【法术详述:迷踪步】 匹配度 0.12
[序号1319]【法术详述:阿迦纳萨喷火术】 匹配度 0.12
[序号507]【法术详述:智能堡垒/智力堡垒/智慧堡垒/智能壁垒/心智堡垒/心智壁垒】 匹配度 0.12
[序号2514]【法术详述:水下呼吸/水中呼吸】 匹配度 0.11
[序号2212]【法术详述:原力法阵】 匹配度 0.11
[序号1403]【法术详述:众星冠冕/星辰冠冕/星之冠冕】 匹配度 0.11
[序号2243]【法术详述:造水/枯水术/造水术/枯水术】 匹配度 0.11
[序号2176]【法术详述:秘法眼】 匹配度 0.11

(本次搜索由全文搜索完成)

因为多个文本匹配度相近,因此没有列出最佳匹配条目的正文内容。用这条指令可以查看:

.查询 2212
Dice:
词条: 法术详述:原力法阵
原力法阵 Circle of Power
圣武士
5环 防护
施法时间:1动作
施法距离:自身(30尺半径)
法术成分:V
持续时间:专注,至多10分钟
你身上发出神圣能量并以扭曲散溢的魔法能量构成一个半径30尺的球状力场。法术持续时间内力场将以你为中心随你移动。力场范围内的友方生物(包括你自己)为对抗法术或其他魔法效应而进行的豁免检定具有优势。此外,受本法术效应影响的生物在对抗豁免成功则伤害减半的法术或魔法效应时,若成功则不受伤害。

这么好用,那代价是什么呢?

更多的内存占用。变慢的启动速度。

给个参考:搭载了怪物之锤文本,以及梨骰DND查询包之后,海豹核心占据了80MB内存。

大致可以这样换算:每1MB文本占15MB内存

快速文档查找

指令是一样的,因此善乏可陈。

示例如下:

.find 测试词条
dice:
最优先结果:
词条: 测试:测试词条
他在命运的沉浮中随波逐流, 扮演着受害与加害者的双重角色

全部结果:
[序号2]【测试:测试词条】 匹配度 67.00

(本次搜索由快速文档查找完成)

致谢

默认搭载的几个示例文档:

其中COC《怪物之锤查询》的整理者为:蜜瓜包October

COC魔法大典的整理者为:魔骨、NULL、Dr.Amber

DND系列资料的整理者主要为DicePP项目组成员,包括Farevell梨子花作噫邪恶惠惠赵小安

这些资料的原始出处和译者很多已经不可考,此处无法一一列出,甚为遗憾。

也在此感谢一代又一代无名作者和译者做出的工作。

.help 帮助指令

格式: .help (<词条名>)

格式: .help reload // 重新装载帮助文档,仅master可用

别名: 无

可用范围: 群内、私聊

显示帮助

.botlist 机器人列表

格式: .botlist add @A @B @C // 标记群内其他机器人,以免发生误触和无限对话

格式: .botlist add @A @B --s // 同上,不过骰子不会做出回复

格式: .botlist del @A @B @C // 去除机器人标记

格式: .botlist list // 查看当前列表

别名: 无

可用范围: 群内

这个指令用于标记群内的其他骰子或娱乐机器人,以便于各司其职。

被标记后会有如下效果:

  1. 当有人@该骰发指令,海豹不会做出回应
  2. 该骰发出的指令和消息,海豹不会回应,这可以有效避免关键词触发的无限复读

之所以不保留传统@效果,是因为海豹基于@功能做了一项扩展,其适用场景远多于同群多骰。

具体可以参见文档的这一节:ra/rc 指令 -> 对他人做检定

示例:

.botlist add @某公骰
Dice:
新增标记了1个帐号,这些账号将被视为机器人。
因此别人@他们时,海豹将不会回复。
他们的指令也会被海豹忽略,避免发生循环回复事故

此外,被标记的当事人可以发出指令解除自己的标记。

.ext 扩展管理

格式: .ext <扩展名> on/off //开关

格式: .ext <扩展名> // 查看信息

格式:.ext list // 查看扩展列表

可用范围: 群内

海豹除了核心指令之外,都被设计为可插拔的,目前有7个内置扩展。

关闭对应扩展会连同扩展带来的指令一块关闭。

注意!每个群可以分别开启不同的扩展。

.ext
Dice:
检测到以下扩展:
1. [开]coc7 - 版本:1.0.0 作者:木落
2. [开]log - 版本:1.0.0 作者:木落
3. [开]fun - 版本:1.0.0 作者:木落
4. [开]deck - 版本:1.0.0 作者:木落
5. [关]reply - 版本:1.0.0 作者:木落
6. [开]dnd5e - 版本:1.0.0 作者:木落
7. [开]story - 版本:1.0.0 作者:木落
使用命令: .ext <扩展名> on/off 可以在当前群开启或关闭某扩展。
命令: .ext <扩展名> 可以查看扩展介绍及帮助


.ext coc7 on
Dice: 打开扩展 coc7

.ext reply
Dice:
> [reply] 版本1.0.0 作者木落
> [尚未实现]智能回复模块,支持关键字精确匹配和模糊匹配
提供命令:
.reply


.ext reply off
Dice: 关闭扩展 reply

可以批量开启或关闭,如
.ext fun deck reply off

开启或关闭扩展能够直接改变骰子响应的命令。

例如coc7扩展提供了 .coc /.st / .ra / .rc / .sc / .setcoc / .ti / .li一系列指令。

而dnd5e扩展提供 .st .rc .ri

扩展关闭后,这些指令也会失效,可以有效避免指令混淆(每个群都可以启用不同的扩展

例如我在A群打开 coc7deck两个扩展,B群可以只开启 deck扩展,等等。

未来将制作更多官方扩展,以及提供自定义扩展脚本接口。

特别的,js插件可以创建扩展了,如果骰子安装了js插件,他可能会出现更多的扩展。

.master 骰主指令

格式: .master add me // 将自己标记为骰主

格式: .master add @A @B // 将别人标记为骰主

格式: .master del @A @B @C // 去除骰主标记

格式: .master unlock <密码(在UI中查看)> // (当Master被人抢占时)清空骰主列表,并使自己成为骰主

格式: .master list // 查看当前骰主列表

格式: .master relogin // 30s后重新登录,有机会清掉风控(仅master可用)

格式: .master list // 查看当前骰主列表

格式: .master reboot // 重新启动(需要二次确认)

格式: .master checkupdate // 检查更新(需要二次确认)

别名: 无

可用范围: 群内、私聊

补充:这条指令做了@排除,如果存在@,且自己没有被@,不作回应。

对于一个无主骰,任何人都可以进行标记。

一旦有人成为骰主,此命令将只有骰主可以使用。

所有骰主都可以踢掉其他骰主,包括自己在内。

骰子的真正所有者,可以从后台看到解锁口令,来通过 .master unlock命令直接取得骰主权限。

.send 对骰主留言

格式: .send <想说的话>

格式: .send to <对方ID> 要说的话 // 骰主回复,举例. send to QQ:12345 感谢留言

别名: 无

可用范围: 群内、私聊

补充:这条指令做了@排除,如果存在@,且自己没有被@,不作回应。

给骰主留言吧!

不过首先,必须存在一名骰主,正如必须有一个巫妖王。

除了留言之外,骰主将看到你的名字和帐号。

如果是群内留言,还将看到群名和群号。

同样,骰主可以通过这个指令进行回复,对方会看到这样的回复内容:

本消息由骰主<木落>通过指令发送:
感谢反馈!

.welcome 迎新设置

格式: .welcome on // 开启

格式: .welcome off // 关闭

格式: .welcome show // 查看当前欢迎语

格式: .welcome set <欢迎语> // 设定欢迎语

扩展: coc7

.coc 制卡指令

格式: .coc (<数量>) // 制卡指令,返回<数量>组人物属性

别名: 无

可用范围: 群内、私聊

.coc 3
Dice:
<木落>的七版COC人物作成:
力量:40 敏捷:80 意志:30
体质:80 外貌:50 教育:45
体型:55 智力:60
HP:13 幸运:50 [440/490]

力量:55 敏捷:75 意志:45
体质:30 外貌:65 教育:60
体型:65 智力:50
HP:9 幸运:25 [445/470]

力量:45 敏捷:55 意志:50
体质:55 外貌:60 教育:85
体型:50 智力:55
HP:10 幸运:60 [455/515]

.en 技能成长

格式: .en <技能名称>(技能点数) (+(<失败成长值>/)<成功成长值>) // 整体格式,可以直接看下面几个分解格式

格式: .en <技能名称> // 骰D100,若点数大于当前值,属性成长1d10

格式: .en <技能名称>(技能点数) // 骰D100,若点数大于 技能点数,属性=技能点数+1d10

格式: .en <技能名称>(技能点数) +<成功成长值> // 骰D100,若点数大于当前值,属性成长 成功成长值

格式: .en <技能名称>(技能点数) +<失败成长值>/<成功成长值> // 骰D100,若点数大于当前值,属性成长 成功成长值点,否则增加 失败成长值

别名: 无

可用范围: 群内

.en 侦查
Dice:
<木落>的“侦查”成长检定:
D100=68/66 成功
“侦查”增加了1d10=3点,当前为69点
角色信息已经变更,别忘了使用.ch save来进行保存!

// 以下例子省略掉骰子的回复
.en 侦查30
.en 侦查 +1d20 // 若成功,成长1d20
.en 侦查 +1d5/1d10

.en 侦查 +-1d5/1d10
.en 侦查 +(-1d5)/1d10 // 跟上一句相同,带括号容易理解一点

.st 属性设置

格式: .st 格式: .st show // 展示个人属性 格式: .st show <属性1> <属性2> ... // 展示特定的属性数值 格式: .st show <数字> // 展示高于<数字>的属性,如.st show 30 格式: .st clr/clear // 清除属性 格式: .st del <属性1> <属性2> ... // 删除属性,可多项,以空格间隔 格式: .st help // 帮助 格式: .st <属性><值> // 例:.st 敏捷50 格式: .st <属性>±<表达式> // 例:.st 敏捷+1d50,请注意目前+或-要跟在属性后面,不得空格

别名: 无

可用范围: 群内

(待补完)

.ra/rc 技能检定

格式: .ra/rc <属性表达式> (@某人)

格式: .ra <难度><属性> (@某人) // 如 .ra 困难侦查

格式: .ra b <属性表达式> (@某人) // 奖励骰或惩罚骰

格式: .ra p <属性表达式> (@某人) // 奖励骰或惩罚骰

格式: .ra b<数字或括号> <属性表达式> (@某人)

别名: 无

可用范围: 群内

属性检定指令,当<检定表达式>算出的值小于等于<属性表达式>,如计算 D100 < 聆听,

若小于等于,则检定通过。当@某人时对此人做检定。

rc是规则书检定。而ra是房规检定。

区别主要在于对大成功/大失败的判定方式。

.setcoc 0时,ra和rc等价。

可以使用类似多轮骰点的多轮检定。

举例

.ra 聆听
Dice: <木落>的聆听检定结果为: d100=59/76, ([1d100=59]) 成功

.ra 困难侦查
Dice: <折影>的困难侦查检定结果为: D100=7/12 成功了!这要费点力气

.ra b 射击
Dice: <木落>的射击检定结果为: b=60/60, ([D100=60, 奖励 0]) 成功

.ra 3# b 射击
Dice: 
<木落>的射击进行了3次检定,结果为:
b=17/60, ([D100=17, 奖励 1]) 成功(极难)
b=32/60, ([D100=92, 奖励 3]) 成功
b=49/60, ([D100=49, 奖励 6]) 成功

// 其实指令可以连写,不过不是很建议,例如这样的写法(溯洄骰)
.ra3#b射击
// 也支持这样的写法(赵骰)
.rab3#射击

他们都与上面的写法等价

对他人做检定

如果kp/dm希望对某人做检定,可以检定时@对方,同时适用于暗中检定。

.ra 侦查 @猛男
Dice: <猛男>的侦查检定结果为: d100=95/55, ([1d100=95]) 失败!

.ra 侦查 // 不@是自己
Dice: <木落>的侦查检定结果为: d100=48/88, ([1d100=48]) 成功

.rah/rch 暗中检定

格式: .rah/rch <属性表达式> (@某人)

别名: 无

可用范围: 群内

与.ra/.rc完全相同,只不过会将结果私聊发送

.rav/rcv 对抗检定

格式: .rav/.rcv <技能> (@某人) // 自己和某人进行对抗检定

格式: .rav <技能1> <技能2> @某A @某B // 对A和B两人做对抗检定,分别使用输入的两个技能数值

别名: 无

可用范围: 群内

<技能>写法举例: 侦查、侦查40、困难侦查、40、侦查+10

请注意,海豹核心的rav指令是不需要后手确认的,相当于面团中RP说明行为后,再骰对抗。这与塔系rav指令不同

一段典型(雾)用法:

Alice: 唔,劲啊!可我今日注定将你轰杀至渣啊!吃我一拳,Bob!
KP: Bob,Alice要打你,你要反击还是躲避?
Bob: 呵,汝今日便要葬在这里呀!反击!给我败罢!
Alice: .rav 斗殴 @Bob

Dice:
对抗检定:
<Alice> 40-> 属性值:40 判定值:36 成功
<Bob> 20-> 属性值:20 判定值:34 失败
<Alice>胜利!

这里给出一些详细的用法示例:

.rav/.rcv 技能 @某人 // 自己和某人进行对抗检定

.rav 技能,b1 @某人 // 同上,我方有一个奖励骰 

.rav 困难技能60 @某人 // 同上,但是困难等级,技能值60。

.rav 困难技能+10 @某人 // 同上,但是有10的加成。

.rav 技能 @某A @某B // 对A和B两人做对抗检定

.rav 技能 技能 @某A @某B // 对A和B两人做对抗检定,分别使用输入的两个技能数值

.rav 技能 困难技能 @某A @某B // 同上,带难度

.rav 技能,b1 困难技能 @某A @某B // 同上,A带一个奖励骰

.rav 60 40 @某A @某B // 最简化版本,A的60点对抗B的40点

.sc 理智检定

格式: .sc <成功时掉san>/<失败时掉san>

格式: .sc <失败时掉san>

格式: .sc b <成功时掉san>/<失败时掉san>

别名: 无

可用范围: 群内

对理智进行一次D100检定,根据结果扣除理智。

如“.sc 0/1d3”为成功不扣除理智,失败扣除1d3。大失败时按掷骰最大值扣除。

支持复杂表达式。如.sc 1d2+3/1d(知识+1)

大成功/大失败判定遵循房规。

本指令支持使用**对他人做检定**。

.setcoc 设置房规

格式: .setcoc 0-5

别名: 无

可用范围: 群内

序号 规则
0[默认] 出1大成功,不满50出96-100大失败,满50出100大失败(COC7规则书)
1 不满50出1大成功,不满50出96-100大失败,满50出100大失败
2[常用] 出1-5且判定成功为大成功,出96-100且判定失败为大失败
3 出1-5大成功,出96-100大失败(即大成功/大失败时无视判定结果)
4 出1-5且≤(成功率/10)为大成功,不满50出>=96+(成功率/10)为大失败,满50出100大失败
5 出1-2且≤(成功率/5)为大成功,不满50出96-100大失败,满50出99-100大失败
dg 出1或检定成功基础上个位十位相同为大成功,出100或检定失败基础上个位十位相同为大失败,此规则无困难成功或极难成功

.ti 抽取临时性疯狂症状

格式: .ti

别名: 无

可用范围: 群内

.ti
<木落>的疯狂发作-即时症状:
1D10=8
竭嘶底里:调查员表现出大笑,哭泣,嘶吼,害怕等的极端情绪表现,持续 1D10=10 轮。

.li 抽取总结性疯狂症状

格式: .li

别名: 无

可用范围: 群内

.li
<木落>的疯狂发作-总结症状:
1D10=8
逃避行为:调查员恢复清醒时发现自己在很远的地方,也许迷失在荒郊野岭,或是在驶向远方的列车或长途汽车上。

扩展: dnd5e

注: 使用dnd5e指令可能需要先开启dnd5e扩展,或直接使用 .set dnd变更游戏模式。

.dnd 制卡指令

格式: .dnd (<数量>) // 制卡指令,返回<数量>组人物属性,最高为10次

格式: .dndx (<数量>) // 制卡指令,但带有属性名,最高为10次

别名: 无

可用范围: 群内、私聊

自由分配模式:

.dnd 5
Dice:
<木落>的DnD5e人物作成(自由分配模式):
[16, 16, 14, 11, 10, 9] = 76
[16, 15, 15, 12, 9, 9] = 76
[16, 15, 14, 13, 13, 12] = 83
[18, 17, 15, 14, 13, 8] = 85
[15, 14, 13, 13, 10, 8] = 73
获取带属性名的预设请用.dndx

预设模式:

.dndx 5
<木落>的DnD5e人物作成(预设模式):
力量:16 体质:9 敏捷:14 智力:10 感知:13 魅力:10 共计:72
力量:13 体质:12 敏捷:8 智力:8 感知:14 魅力:9 共计:64
力量:16 体质:10 敏捷:11 智力:14 感知:12 魅力:10 共计:73
力量:9 体质:17 敏捷:15 智力:9 感知:10 魅力:13 共计:73
力量:12 体质:6 敏捷:8 智力:13 感知:10 魅力:8 共计:57
自由分配模式请用.dnd

.ri 先攻设置

格式: .ri <先攻值> <角色名> // 角色名省略为当前角色

格式: .ri +2 <角色名> // 先攻值格式1,解析为D20+2

格式: .ri 12 <角色名> // 先攻值格式2,解析为12

格式: .ri =D20+3 <角色名> // 先攻值格式3,解析为D20+3

格式: .ri <单项>, <单项>, ... // 允许连写,逗号分隔

别名: 无

可用范围: 群内

先攻

// 单独设置
.ri +2 // 为自己设定
.ri +3 狗头人1 // D20加值方式
.ri =D20+2 狗头人1 // 表达式方式
.ri 30 狗头人1  // 数值方式


// 批量设置
.ri +2 狗头人1, 3 狗头人2, =d30 狗头人3 +3
Dice:
先攻点数设置: 
 1. 狗头人1: 10[1d20=10] + 2=12
 2. 狗头人2: 3
 3. 狗头人3: 1
 4. 木落: 1[1d20=1] + 3=4

.init 先攻列表

格式: .init // 查看先攻列表

格式: .init del <单位1> <单位2> ... // 从先攻列表中删除

格式: .init set <单位名称> <先攻表达式> // 设置单位的先攻

格式: .init clr // 清除先攻列表

格式: .init help // 显示本帮助

别名: 无

可用范围: 群内

// 设置先攻
.ri +2 狗头人1, 4 狗头人2, 12 木落
Dice:
先攻点数设置: 
 1. 狗头人1: 8[1d20=8] + 2=10
 2. 狗头人2: 4
 3. 木落: 12

// 查看列表
.init
Dice:
当前先攻列表为:
 1. 木落: 12
 2. 狗头人2: 10
 3. 狗头人1: 4

.st DND属性设置

格式: .st 模板 // 录卡模板

格式: .st show // 展示个人属性

格式: .st show <属性1> <属性2> ... // 展示特定的属性数值

格式: .st show <数字> // 展示高于<数字>的属性,如.st show 30

格式: .st clr/clear // 清除属性

格式: .st del <属性1> <属性2> ... // 删除属性,可多项,以空格间隔

格式: .st export // 导出,包括属性和法术位

格式: .st help // 帮助

格式: .st <属性>:<值> // 设置属性,技能加值会自动计算。例:.st 感知:20 洞悉:3

格式: .st <属性>±<表达式> // 例:.st 生命+1d4

格式: .st hp-1d6 --over // 不计算临时生命扣血

别名: .dst

可用范围: 群内

DND的录卡,和属性操作指令。

这个指令最好在关闭COC扩展时使用,如果一起用,可以使用 .dst来规避指令冲突。

目前支持技能调整值的自动计算,包括属性带来的加值,和熟练带来的加值。

使用模板录卡:

// 查看模板
.st 模板
Dice:
人物卡模板(第二行文本):
.dst 力量:10 体质:10 敏捷:10 智力:10 感知:10 魅力:10 hp:10 hpmax: 10 熟练:2 运动:0 特技:0 巧手:0 隐匿:0 调查:0 奥秘:0 历史:0 自然:0 宗教:0 察觉:0 洞悉:0 驯养:0 医疗:0 生存:0 说服:0 欺诈:0 威吓:0 表演:0
注意: 技能只填写修正值即可,属性调整值会自动计算。熟练写为“运动*:0”

// 录卡
.st 力量:12 体质:10 敏捷:10 智力:10 感知:10 魅力:10 hp:10 hpmax: 10 熟练:2 运动*:3 特技:1 巧手:0 隐匿:0 调查:0 奥秘:0 历史:0 自然:0 宗教:0 察觉:0 洞悉:0 驯养:0 医疗:0 生存:0 说服:0 欺诈:0 威吓:0 表演:0
Dice:
<木落>的dnd5e人物属性设置如下:
读入: 力量:12, 体质:10, 敏捷:10, 智力:10, 感知:10, 魅力:10, hp:10, hpmax:10, 熟练:2, 运动:3[技能, 熟练], 特技:1[技能], 巧手:0[技能], 隐匿:0[技能], 调查:0[技能], 奥秘:0[技能], 历史:0[技能], 自然:0[技能], 宗教:0[技能], 察觉:0[技能], 洞悉:0[技能], 驯养:0[技能], 医疗:0[技能], 生存:0[技能], 说服:0[技能], 欺诈:0[技能], 威吓:0[技能], 表演:0[技能]

查看

.st show // 查看属性
<木落>的个人属性为:
力量: 12    敏捷: 10    体质: 10    智力: 10  
感知: 10    魅力: 10    hp: 10    hpmax: 10  
EXP: 0    熟练: 2    特技: 1[基础值1]    运动: 6[基础值3]  
... // 还有一堆 这里不写了

// 如果想查看部分,使用
.st show 力量 运动 熟练
<木落>的个人属性为:
力量: 12    熟练: 2    运动: 6[基础值3]

修改属性

.st 力量 + 2
Dice:
人物属性设置如下:
修改: 力量(12 ➯ 14)

属性修改后,属性调整值会自动变更。不用再行关注。

退游小技巧,黑暗录卡:

.st 力量:4d6k3 体质:4d6k3 敏捷:4d6k3 智力:4d6k3 感知:4d6k3 魅力:4d6k3 hp:10 hpmax: 10 熟练:2 运动:0 特技:0 巧手:0 隐匿:0 调查:0 奥秘:0 历史:0 自然:0 宗教:0 察觉:0 洞悉:0 驯养:0 医疗:0 生存:0 说服:0 欺诈:0 威吓:0 表演:0

对生命值的特殊支持:

  • 当存在临时生命(buff血量),自动优先消耗。若临时生命不足以抵挡伤害,剩余会扣减血量
  • 当生命值归零,若伤害大于等于血量上限,判定人物死亡
  • 当生命值归零,若伤害小于血量上限,人物昏迷
  • 当人物已经昏迷,不足致死的伤害自动折算为死亡豁免失败

一次性进行多个修改:

.st hp-3 hp-3 hp-3
<木落>的dnd5e人物属性设置如下:
修改: hp(2 ➯ 0), hp(0 ➯ 0), hp(0 ➯ 0)
<木落>遭受了1点过量伤害,生命值降至0,陷入了昏迷!
<木落>在昏迷状态下遭受了3点过量伤害,死亡豁免失败+1!
<木落>在昏迷状态下遭受了3点过量伤害,死亡豁免失败+1!

此外,这个指令可以通过@来选定其他人(仅限一名),调整对方的属性。

特别注意:.st clr会同时清除法术位

.rc DND检定

格式: .rc <属性> // .rc 力量

格式: .rc <属性>豁免 // .rc 力量豁免

格式: .rc <表达式> // .rc 力量+3

格式: .rc 优势 <表达式> // .rc 优势 力量+4

格式: .rc 劣势 <表达式> (原因) // .rc 劣势 力量+4 推一下试试

格式: .rc <表达式> @某人 // 对某人做检定

别名: .drc

可用范围: 群内

示例

.rc 运动
Dice:
<木落>的“运动”检定结果为:
8[1d20=8] + 6[运动=6] = 14

优势检定和劣势检定

.rc 优势 力量+4
Dice:
<木落>的“力量+4”检定结果为:
17[{17 | 11 }] + 12[力量=12] + 4 = 33

带原因的检定:

.rc 劣势 力量+4 推一下试试
Dice:
<木落>的“推一下试试”检定结果为:
17[{17 | 18 }] + 12[力量=12] + 4 = 33

此外,这个指令可以通过@来选定其他人(仅限一名)操作。

奇妙运用

你可以通过如下方式记录武器的熟练项。 .st 长剑=熟练加值(数字) .rc 长剑

.st长剑=4
Dice:
<木落>的属性录入完成,本次录入了1条数据。
.rc 长剑
Dice:
<木落>的“长剑”检定(dnd5e)结果为:
16[1d20=16] + 4[长剑=4] = 20

.buff DND临时属性

格式: .buff // 展示当前buff

格式: .buff clr // 清除buff

格式: .buff del <属性1> <属性2> ... // 删除属性,可多项,以空格间隔

格式: .buff help // 帮助

格式: .buff <属性>:<值> // 设置buff属性,例:.buff 力量:4 奥秘*:0,奥秘临时熟练加成

格式: .buff <属性>±<表达式> // 修改属性,例:.buff hp+1d4

格式: .buff <属性>±<表达式> @某人 // 修改他人buff属性,例:.buff hp+1d4

别名: .dbuff

可用范围: 群内

目前这个指令的行为和st指令基本一致。

用于设置临时属性,例如虚假生命:

.st hp:10
.buff hp : 1d4+4

Dice:
人物Buff属性设置如下:
读入: hp:5

.st show hp
hp: 16[10]

也可以用来设置其他属性:

.st 力量:10  运动:0
.buff 力量:6
.st show 力量 运动

Dice:
<木落>的个人属性为:
力量: 16[10]    运动: 3[0]

临时的技能熟练(装备或法术效果):

.st 力量:10  运动:0 熟练:3
.buff 运动*:0
.st show 力量 运动

Dice:
<木落>的个人属性为:
力量: 16[10]    运动: 6[0]

删除和清空

.buff del hp
Dice: <木落>的如下buff属性被成功删除:hp,失败0项

.buff clr
Dice: <木落>的BUFF数据已经清除,共计0条

此外,这个指令可以通过@来选定其他人(仅限一名)操作。

.ss 法术位(spell slots)

格式: .ss // 查看当前法术位状况

格式: .ss init 4 3 2 // 设置1 2 3环的法术位上限,以此类推到9环

格式: .ss set 2环 4 // 单独设置某一环的法术位上限,可连写多组,逗号分隔

格式: .ss clr // 清除法术位设置

格式: .ss rest // 恢复所有法术位(不回复hp)

格式: .ss 3环 +1 // 增加一个3环法术位(不会超过上限)

格式: .ss lv3 +1 // 增加一个3环法术位 - 另一种写法

格式: .ss 3环 -1 // 消耗一个3环法术位,也可以用.cast 3

别名: .法术位 .dss

可用范围: 群内

.ss init 3 4 5
为<木落>设置法术位: 1环3个, 2环4个, 3环5个

.ss 1环 -1
<木落>的1环法术位消耗至2个,上限3个

.ss
Dice: <木落>的法术位状况: 1环:2/3, 2环:4/4, 3环:5/5

.ss rest
Dice: <木落>的法术位已经完全恢复

.ss set 4环 3
Dice: 为<木落>设置法术位: 4环3个

此外,这个指令可以通过@来选定其他人(仅限一名)操作。

特别注意:.st clr会同时清除法术位

.cast 使用法术位

格式: .cast 1 // 消耗1个1环法术位

格式: .cast 1 2 // 消耗2个1环法术位

别名: .dcast

可用范围: 群内

.cast 2  // 或者写2环也是可以的
Dice: <木落>的2环法术位消耗至3个,上限4个

此外,这个指令可以通过@来选定其他人(仅限一名)操作。

.longrest / .长休

格式: .长休 // 恢复生命值(必须设置hpmax且hp>0)和法术位

格式: .longrest // 另一种写法别名: .dcast

可用范围: 群内

有hpmax,有法术位的情况:

.长休
Dice: <木落>的长休: hp得到了恢复,现为12 法术位得到了恢复

两个都没有的情况:

.长休
<木落>的长休: 没有设置hpmax,无法回复hp

此外,这个指令可以通过@来选定其他人(仅限一名)操作。

.ds / .死亡豁免

格式: .死亡豁免 // 恢复生命值(必须设置hpmax且hp>0)和法术位

格式: .ds // 别名

格式: .ds +1d4 // 检定时添加1d4的加值

格式: .ds 成功±1 // 死亡豁免成功±1,可简写为.ds s±1

格式: .ds 失败±1 // 死亡豁免失败±1,可简写为.ds f±1

格式: .ds stat // 查看当前死亡豁免情况

格式: .ds help // 查看帮助

可用范围: 群内

注意,st指令对hp的操作也会自动触发死亡豁免。

.st hp:3 hpmax:12
Dice:
<木落>的dnd5e人物属性设置如下:
读入: hp:3, hpmax:12

陷入昏迷:

.st hp-10
<木落>的dnd5e人物属性设置如下:
修改: hp(3 ➯ 0)
<木落>遭受了7点过量伤害,生命值降至0,陷入了昏迷!

昏迷后被攻击:

<木落>的dnd5e人物属性设置如下:
修改: hp(0 ➯ 0)
<木落>在昏迷状态下遭受了1点过量伤害,死亡豁免失败+1!

死亡:

<木落>的死亡豁免检定: [1d20=3]=3 有些不妙!死亡豁免失败+1
你获得了3次死亡豁免检定失败,不幸去世了!

医学奇迹:

<木落>的死亡豁免检定: [1d20=20]=20 你觉得你还可以抢救一下!HP回复1点!

此外,这个指令可以通过@来选定其他人(仅限一名)操作。

扩展: 故事模块

.name 随机名字

格式: .name cn/en/jp // 抽取随机名字

格式: .name cn 10 // 指定抽取数量

可用范围: 群内、私聊

.name cn
Dice: 为<木落>生成以下名字:
乌天雪、孟微微、香佳宁、岩章联、宓永玲

.name en
Dice: 为<木落>生成以下名字:
Jayce Jenkins (杰斯·詹金斯)、Marie Abshire (玛丽·阿布希尔)、Delaney Thompson (德莱尼·汤普森)、Leora Yundt (莱奥拉·杨特)、Vallie Padberg (瓦利·帕德贝格)

.name jp
Dice: 为<木落>生成以下名字:
宮原 笑和(みやはら えみな)、林 勝利(はやし しょうり)、福本 衣宮(ふくもと いく)、石黒 翠紀(いしぐろ みのり)、宮原 梓生(みやはら しき)

.namednd 随机DND名字

格式: .name 达马拉人/卡林珊人/莱瑟曼人/受国人/精灵/矮人/兽人/海族/地精 // 抽取随机名字

格式: .name 达马拉人 10 // 指定抽取数量

可用范围: 群内、私聊

.namednd 精灵 3
Dice: 为<木落>生成以下名字:
Wysalana Sunbrand (维萨拉那·炙痕)、Keyfina Sunhawk (凯菲娜·炎鹰)、Olakrana Dawnstar (奥拉克拉纳·晨星)

.who 顺序重排

格式: .who a b c // 重新排序

可用范围: 群内、私聊

.modu 魔都模组查询

格式:.modu search <关键字> (<页码>) // 搜索关键字

格式:.modu rec <关键字> (<页码>) // 搜索编辑推荐

格式:.modu author <关键字> (<页码>) // 搜索指定作者

格式:.modu luck (<页码>) // 查看编辑推荐

格式:.modu get <编号> // 查看指定详情

格式:.modu roll // 随机抽取

格式:.modu help // 显示帮助

别名: .cnmods

可用范围: 群内、私聊

扩展: 日志

.log 跑团日志

格式: .log new/on/off/end/get/halt/list/del/stat

别名: 无

可用范围: 群内

.log new (<日志名>) // 新建日志并开始记录

.log on (<日志名>) // 开始记录,日志名不写则为最近一次日志

.log off // 暂停记录

.log end // 完成记录并发送日志文件

.log get // 重新上传日志,并获取链接

.log halt // 关闭当前记录,不发送日志文件

.log list // 查看当前群的日志列表

.log del // 删除一份日志

.log stat // 查看统计

.log masterget <群号> <日志名> // 重新上传日志,并获取链接(无法取得日志时,找骰主做这个操作)

特别提醒:海豹核心的日志记录,会在bot off时自动停止。

骰主代取LOG

比如说有一个团,群已经解散了,这时候有人找你来帮忙取log,骰主可以这样操作:

首先,找到任意交互界面,可以是海豹后台的指令测试界面(你要将UI:1001添加为骰主),然后执行下面的命令

.log list QQ-Group:12345 // 至少要知道群号

Dice:
正在列出存在于此群的记录:
- test
- 测试
- 测试2

接下来再做一步:

.log masterget QQ-Group:12345 测试

就可以了。

.ob 观众

格式: .ob // 成为观众

格式: .ob exit // 取消观众身份

可用范围: 群内、私聊

没有什么实际作用,只是修改PC名字和群名片(如果有权限)

.sn 自动跑团名片

格式: .sn coc // COC名片

格式: .sn dnd // DND名片

格式: .sn none // 空名片

格式: .sn off // 关闭自动名片

可用范围: 群内、私聊

当骰子是管理员或群主时,通过这个指令,能够自动为非管理/群主设置群名片。

COC名片:角色名 SAN70 HP14/14 DEX60

DND名片:角色名 HP10/10 AC8 DC6 PW8

自设规则模板允许自定义名片,如有规则xxx,经配置后可以通过.sn xxx来切换至对应名片个事。

.stat/.hiy 统计

格式: .stat log // 查看当前日志的统计

可用范围: 群内、私聊

海豹不支持.hiy来查询你使用这个骰子的所有检定的成功率。

扩展: 娱乐

.jrrp 今日人品

格式: .jrrp

别名: 无

可用范围: 群内、私聊

.jrrp
Dice: <折影>的今日人品为68

.gugu 人工智能鸽子

格式: .gugu

别名: .咕咕

可用范围: 群内、私聊

.gugu
Dice: 🕊️:今天发版本,领导说发不完不让走

P.S. 灵感来自于狗头人网站的同名功能,测试版本中有部分语料来自此网站,但是由于一直联系不上网站作者,无法拿到授权,所以在正式版本时将会全部替换。

.rsr 暗影狂奔骰点

格式: .rsr <骰数>

别名: 无

可用范围: 群内、私聊

每个被骰出的五或六就称之为一个成功度 如果超过半数的骰子投出了一被称之为失误 在投出失误的同时没能骰出至少一个成功度被称之为严重失误

.rsr 20
<木落>骰点20D6:
成功度:9/5

.dx/dxh 双重十字骰点

格式: .dx <骰数>

格式: .dx 3c7

别名: 无

可用范围: 群内、私聊

.dx 3
<木落>掷出了 3c10=[出目8/3 {1,7,8}]=8

建议使用 .r 3c10 替代

.w/ww/wwh 双重十字骰点

格式: .ww <骰数>

格式: .ww 10a10

别名: 无

可用范围: 群内、私聊

.ww 7
<木落>掷出了 7a10=[成功4/9 轮数:3 {9*,2,4,2,3,1,<10*>},{<10*>},{8*}]=4

.ek 共鸣性怪异检定

格式:.ek <技能名称>(+<奖励骰>) 判定值

格式:.ek 检索 // 骰“检索”等级个d10,计算成功数

格式:.ek 检索+2 // 在上一条基础上加骰2个d10

格式:.ek 检索 6 // 骰“检索”等级个d10,计算小于6的骰个数

格式:.ek 检索 知力+检索 // 骰”检索“,判定线为”知力+检索“

格式:.ek 5 4 // 骰5个d10,判定值4

格式:.ek 检索2 // 未录卡情况下判定2级检索

格式:.ek 共鸣 6 // 共鸣判定,成功后手动st共鸣+N

别名: 无

可用范围: 群内、私聊

.st 检索3 知力4
.ek 检索
<木落>的“检索”共鸣性怪异规则检定:
{3+2+4}
判定值: 7{3 + 4[知力=4]}
成功数: 3[极限成功]

.ekgen 共鸣性怪异角色制成

格式: .ekgen <数量>

别名: 无

可用范围: 群内、私聊

.ekgen
<木落>的共鸣性怪异人物做成:
身体:6 灵巧:2 精神:6 五感:3 知力:3 魅力:1 社会:4 运势:2 hp:16 mp:9

.text 自定义文本命令

格式: .text <文本表达式>

别名: 无

可用范围: 群内、私聊

这个命令的使用方式很简单:

.text Hello World!
Dice: Hello World!

它支持变量,比如有一些内置变量(角色属性也是变量):

.text Hello {$t玩家},你的理智属性为{san}
Dice: Hello <木落>,你的理智属性为50

还可以做赋值操作,例如

.text 你直面了未知。扣除理智{$tSan=1d10},理智变化: {san} ➯ {san=san-$tSan}
Dice: 你直面了未知。扣除理智5,理智变化: 50 ➯ 45

一个较为复杂的例子,coc7角色做成:

.text 力量:{$t1=3d6*5} 敏捷:{$t2=3d6*5} 意志:{$t3=3d6*5} 体质:{$t4=3d6*5} 外貌:{$t5=3d6*5} 教育:{$t6=(2d6+6)*5} 体型:{$t7=(2d6+6)*5} 智力:{$t8=(2d6+6)*5} 幸运:{$t9=3d6*5} 生命值:{($t4+$t7)/10} 总数:{$t1+$t2+$t3+$t4+$t5+$t6+$t7+$t8}
Dice: 力量:40 敏捷:65 意志:60 体质:55 外貌:30 教育:65 体型:50 智力:40 幸运:30 生命值:10 总数:405
注: .coc命令内部也是如此实现

你可能注意到了有的变量带 $t前缀,这是“临时变量”的意思。

想了解更多关于变量,见“变量机制”一节。

扩展: 牌堆

.draw抽牌指令

格式: .draw help // 显示本帮助

格式: .draw keys // 查看可抽取的牌组列表

格式: .draw keys <牌堆> // 查看特定牌堆可抽取的牌组列表

格式: .draw search <牌组名称> // 搜索相关牌组

格式: .draw desc <牌组名称> // 查看某牌组详细信息

格式: .draw <牌组名称> // 进行抽牌

格式: .draw reload // 重新加载牌堆,仅master可用

格式: .draw list // 查看载入的牌堆文件

别名: .deck

可用范围: 群内、私聊

扩展: 自定义回复

海豹核心提供了强大的自定义回复功能。

支持关键字的精确、模糊、正则、前缀、后缀匹配方式,同时可以进行条件组合。

对于回复方式则支持普通回复、私聊回复、群聊回复,后两者的典型组合方式就是暗骰(群聊回复一句提示发言,并私聊回复骰点结果)。

.reply 自定义回复

格式: .reply on // 开启

格式: .reply off // 关闭

可用范围: 群内、私聊

此指令仅用于开关。

录入文本请在GUI界面进行。

案例:前缀+后缀,匹配AxxxxxxxB类型文本

image-20220422011021889

应用案例:随机看图

image-20220422011148013

应用案例:单人模组

image-20220423201445963

具体实现,第一段

image-20220423201525064

第二段

image-20220423201535766

这两段较为常规,第三段开始变形了!

image-20220423201558007

{%
    if $mStory == 4 { $mStory = 5 };
    if $mStory == 3 { $mStory = 4 };
    if $mStory == 2 { $mStory = 3 };

         $mStory == 3 ? '这个村子有一户人家,门前有两棵树',
         $mStory == 4 ? '一棵是函树,一棵是反函树',
         $mStory == 5 ? '我讲完了。',
         1 ? '?'
%}

{% if $mStory == 5 { $mStory=0 } %}

匹配条件

image-20220422011257640

自定义脚本指令

用户可通过自定义回复实现组合掷骰,例如手册中,text写的coc属性投掷部分,可以通过复制进自定义回复后,设置关键词的方式实现自定义指令。执行效果如下:

自定义
Dice:
力量:55敏捷:60意志:45体质:60外貌:50教育:50体型:55智力:60幸运:60生命值:11总数:435

自定义文本

SealDice可以对骰子进行几乎完全的自定义,绝大部分指令的文本都可以进行修改。

同时还支持插入图片和CQ码。

界面概览

一图胜千言:

image-20220313114539506

这是SealDice的自定义文案界面,左侧是分类:

image-20220313114711301

右侧是具体的文本,我们以“大成功”文本来举例:

image-20220313114758488

这里有两行文本,骰子会随机抽取其中的一行文本来使用。例如:

.ra 力量
Dice: <木落>的力量检定结果为:D100=3/30=([1d100=3]) 大成功!

.ra 力量
Dice: <木落>的力量检定结果为:D100=2/30=([1d100=3]) 运气不错,大成功!11111

如果想要添加一行,那么点击左侧加号按钮:

image-20220313115106066

如果你觉得某一行不满意,点左侧删除按钮。

image-20220313115219234

最后,如果你想回到初始设置,点击右上角的刷子图标。

image-20220313115310985

随后会弹出确认框,点击确定后,一切就是刚开始的样子了。

image-20220313115344706

此时,刷子图标也会消失。

当然,修改好之后,不要忘记保存!!注意保存前不要切换左侧的分类!

image-20220313115441015

骰子进群和成为好友的问候语

这两个词条:

核心:骰子进群

核心:骰子成为好友

插入图片和CQ码

将图片放在骰子的适当目录(建议放在data/images),再写这样一句话即可:

[图:data/images/sealdice.png]

例如骰子进群的文本:

image-20220313115721100

全文本为:

<{核心:骰子名字}> 已经就绪。可通过.help查看指令列表\n[图:data/images/sealdice.png]

效果如下:

image-20220313115848799

变量标签

你可能注意到,有些词条下面有这样的内容:

image-20220313115957701

文本下方的标签代表了被默认文本所使用的特殊变量,你可以使用 {变量名} 来插入他们,例如 {$t判定值}

除此之外,有些变量可以在所有文本中使用,具体列表可以见下方“变量机制”一节。

以及,所有的自定义文本都可以嵌套使用,例如:

这里是{核心:骰子名字},我是一个示例

默认会被解析为:

这里是海豹,我是一个示例

注意!千万不要递归嵌套,会发生很糟糕的事情。

辅助机制

属性同义词

例如在COC录卡的时候,有这样的指令:

.st 力量50str50
Dice: <木落>的属性录入完成,本次共记录了2条数据 (其中1条为同义词)

很显然 力量str是同一个属性,所以只会录入一条属性。

.st show
Dice: <木落>的个人属性为:力量: 50

同理,在 .ra判定以及其他场景中,力量str也一样会当作同一个值处理。

此外,同义词大小写不敏感。sTR视同str

同义词表遵循以下原则(以CO7为例):

  • 中文和英文缩写等价,如 力量str
  • 同一概念的不同常用称呼,如 san san值 理智 理智值
  • 易混词汇,如 侦查 侦察
  • 不同版本翻译区别,如COC6版和7版 话术 快速交谈
  • 简繁区别,如 体质 體質
  • *少量游戏性等值属性,如 枪械 射击智力 灵感教育 知识

COC7同义词具体内容如下:

{
    "理智": {"san", "san值", "理智值", "理智点数", "心智", "心智点数", "心智點數", "理智點數"},
    "力量": {"str"},
    "体质": {"con", "體質"},
    "体型": {"siz", "體型", "体型", "体形", "體形"},
    "敏捷": {"dex"},
    "外貌": {"app", "外表"},
    "意志": {"pow"},
    "教育": {"edu", "知识", "知識"}, // 教育和知识等值而不是一回事,注意
    "智力": {"int", "灵感", "靈感"}, // 智力和灵感等值而不是一回事,注意

    "幸运":     {"luck", "幸运值", "运气", "幸運", "運氣", "幸運值"},
    "生命值":    {"hp", "生命", "体力", "體力", "血量", "耐久值"},
    "魔法值":    {"mp", "魔法", "魔力", "魔力值"},
    "护甲":     {"装甲", "護甲", "裝甲"},
    "枪械":     {"火器", "射击", "槍械", "射擊"},
    "会计":     {"會計"},
    "人类学":    {"人類學"},
    "估价":     {"估價"},
    "考古学":    {"考古學"},
    "魅惑":     {"取悦", "取悅"},
    "攀爬":     {"攀岩", "攀登"},
    "计算机使用":  {"电脑使用", "計算機使用", "電腦使用", "计算机", "电脑", "計算機", "電腦"},
    "信用评级":   {"信誉", "信用", "信誉度", "cr", "信用評級", "信譽", "信譽度"},
    "克苏鲁神话":  {"cm", "克苏鲁", "克苏鲁神话知识", "克蘇魯", "克蘇魯神話", "克蘇魯神話知識"},
    "乔装":     {"喬裝"},
    "闪避":     {"閃避"},
    "汽车驾驶":   {"汽車駕駛", "汽车", "驾驶", "汽車", "駕駛"},
    "电气维修":   {"电器维修", "电工", "電氣維修", "電器維修", "電工"},
    "电子学":    {"電子學"},
    "话术":     {"快速交谈", "話術", "快速交談"},
    "历史":     {"歷史"},
    "恐吓":     {"恐嚇"},
    "跳跃":     {"跳躍"},
    "母语":     {"母語"},
    "图书馆使用":  {"圖書館使用", "图书馆", "图书馆利用", "圖書館", "圖書館利用"},
    "聆听":     {"聆聽"},
    "锁匠":     {"开锁", "撬锁", "钳工", "鎖匠", "鉗工", "開鎖", "撬鎖"},
    "机械维修":   {"机器维修", "机修", "機器維修", "機修"},
    "医学":     {"醫學"},
    "博物学":    {"自然", "自然学", "自然史", "自然學", "博物學"},
    "领航":     {"导航", "領航", "導航"},
    "神秘学":    {"神秘學"},
    "操作重型机械": {"重型操作", "重型机械", "重型", "重机", "操作重型機械", "重型機械", "重機"},
    "说服":     {"辩论", "议价", "演讲", "說服", "辯論", "議價", "演講"},
    "精神分析":   {"心理分析"},
    "心理学":    {"心理學"},
    "骑术":     {"騎術"},
    "妙手":     {"藏匿", "盗窃", "盜竊"},
    "侦查":     {"侦察", "偵查", "偵察"},
    "潜行":     {"躲藏"},
    "投掷":     {"投擲"},
    "追踪":     {"跟踪", "追蹤", "跟蹤"},
    "驯兽":     {"动物驯养", "馴獸", "動物馴養"},
    "读唇":     {"唇语", "讀唇", "唇語"},
    "炮术":     {"炮術"},
    "学识":     {"学问", "學識", "學問"},
    "艺术与手艺":  {"艺术和手艺", "艺术", "手艺", "工艺", "技艺", "藝術與手藝", "藝術和手藝", "藝術", "手藝", "工藝", "技藝"},
    "美术":     {"美術"},
    "伪造":     {"偽造"},
    "摄影":     {"攝影"},
    "理发":     {"理髮"},
    "书法":     {"書法"},
    "木匠":     {"木工"},
    "厨艺":     {"烹饪", "廚藝", "烹飪"},
    "写作":     {"文学", "寫作", "文學"},
    "歌剧歌唱":   {"歌劇歌唱"},
    "技术制图":   {"技術製圖"},
    "裁缝":     {"裁縫"},
    "声乐":     {"聲樂"},
    "喜剧":     {"喜劇"},
    "器乐":     {"器樂"},
    "速记":     {"速記"},
    "园艺":     {"園藝"},
    "斗殴":     {"鬥毆"},
    "剑":      {"剑术", "劍", "劍術"},
    "斧":      {"斧头", "斧子", "斧頭"},
    "链锯":     {"电锯", "油锯", "鏈鋸", "電鋸", "油鋸"},
    "链枷":     {"连枷", "連枷", "鏈枷"},
    "绞索":     {"绞具", "絞索", "絞具"},
    "手枪":     {"手槍"},
    "步枪":     {"霰弹枪", "步霰", "步枪/霰弹枪", "散弹枪", "步槍", "霰彈槍", "步霰", "步槍/霰彈槍", "散彈槍"},
    "弓":      {"弓术", "弓箭", "弓術"},
    "火焰喷射器":  {"火焰噴射器"},
    "机枪":     {"機槍"},
    "矛":      {"投矛"},
    "冲锋枪":    {"衝鋒槍"},
    "天文学":    {"天文學"},
    "生物学":    {"生物學"},
    "植物学":    {"植物學"},
    "化学":     {"化學"},
    "密码学":    {"密碼學"},
    "工程学":    {"工程學"},
    "司法科学":   {"司法科學"},
    "地质学":    {"地理学", "地質學", "地理學"},
    "数学":     {"數學"},
    "气象学":    {"氣象學"},
    "药学":     {"藥學"},
    "物理学":    {"物理", "物理學"},
    "动物学":    {"動物學"},
    "船":      {"开船", "驾驶船", "開船", "駕駛船"},
    "飞行器":    {"开飞行器", "驾驶飞行器", "飛行器", "開飛行器", "駕駛飛行器"},
    "科学":     {"科學"},
    "海洋":     {"海上"},
    "极地":     {"極地"},
    "语言":     {"外语", "語言", "外語"},
}

同义词列表可以在骰子运行后,查看骰子目录下 data/default/extensions/coc7/attribute.yaml

DND同义词列表:

{
    "力量": {"str", "Strength"},
    "敏捷": {"dex", "Dexterity"},
    "体质": {"con", "Constitution", "體質"},
    "智力": {"int", "Intelligence"},
    "感知": {"wis", "Wisdom"},
    "魅力": {"cha", "Charisma"},

    "ac":    {"AC", "护甲等级", "护甲值", "护甲", "護甲等級", "護甲值", "護甲", "装甲", "裝甲"},
    "hp":    {"HP", "生命值", "生命", "血量", "体力", "體力", "耐久值"},
    "hpmax": {"HPMAX", "生命值上限", "生命上限", "血量上限", "耐久上限"},
    "dc":    {"DC", "难度等级", "法术豁免", "難度等級", "法術豁免"},
    "hd":    {"HD", "生命骰"},
    "pp":    {"PP", "被动察觉", "被动感知", "被動察覺", "被动感知"},

    "熟练": {"熟练加值", "熟練", "熟練加值"},
    "体型": {"siz", "size", "體型", "体型", "体形", "體形"},

    // 技能
    "运动": {"Athletics", "運動"},

    "体操": {"Acrobatics", "杂技", "特技", "體操", "雜技"},
    "巧手": {"Sleight of Hand"},
    "隐匿": {"Stealth", "隱匿", "潜行", "潛行"},

    "调查": {"Investigation", "調查"},
    "奥秘": {"Arcana", "奧秘"},
    "历史": {"History", "歷史"},
    "自然": {"Nature"},
    "宗教": {"Religion"},

    "察觉": {"Perception", "察覺", "觉察", "覺察"},
    "洞悉": {"Insight", "洞察"},
    "驯兽": {"Animal Handling", "馴獸", "驯养", "馴養"},
    "医药": {"Medicine", "醫藥", "医疗", "醫療"},
    "求生": {"Survival", " 生存"},

    "游说": {"Persuasion", "说服", "话术", "遊說", "說服", "話術"},
    "欺瞒": {"Deception", "唬骗", "欺诈", "欺骗", "诈骗", "欺瞞", "唬騙", "欺詐", "欺騙", "詐騙"},
    "威吓": {"Intimidation", "恐吓", "威嚇", "恐嚇"},
    "表演": {"Performance"},
}

目前不可自定义。如有建议欢迎反馈。

变量机制

你可能注意到,这里出现了一些 san$t玩家之类的东西,这些是SealDice中的变量

内置变量有:

变量名 内容 示例结果
$t玩家 当前人物卡的名字,如果不存在则为群昵称或QQ昵称。 <木落>
$t玩家_RAW 同上,但没有<> 木落
$tQQ昵称 QQ昵称 <木落>
$t账号ID 海豹格式的ID QQ:123456789
$t账号ID_RAW 原始格式的ID 123456789
$tQQ 海豹格式的ID QQ:123456789
$t群名 群名 海豹核心·SealDice用户群
$t群号 海豹格式的ID QQ-Group:987654321
$t群号_RAW 原始格式的ID 987654321
$t个人骰子面数 个人骰子面数 100
$tDate 数字格式的现日期 20230109
$tYear 数字格式的年份 2023
$tMonth 数字格式的现月份 1
$tDay 数字格式的现日期 9
$tWeekday 数字格式的星期(1-7) 1
$tHour 数字格式的现时间(小时) 15
$tMinute 数字格式的现时间(分钟) 41
$tSecond 数字格式的现时间(秒) 55
$tTimestamp 数字格式的10位时间戳 1673250115
$t文本长度 触发消息的文本,汉字长度为3,英文字母和数字长度为1。 6
$t平台 触发的平台 QQ
$t游戏模式 随.set coc/dnd改变 coc7
$t消息类型 触发位置为群还是私聊。(group/private) group
娱乐:今日人品 自定义文案 <木落>的今日人品为0
常量:APPNAME 软件名 SealDice
常量:VERSION 版本号 0.91测试版 v20220228
$tMsgID 消息ID,仅自定义回复中可用。 -123

变量的名称可以是汉字、字母和数字,$t是一个特殊的变量前缀。

*注: 所有自定义文案均为可用变量。

变量名字 用途 举例
普通名字 玩家的角色属性 理智、力量、智力
$t开头 个人临时变量,不存数据库 $t随机点数
$m开头 个人变量,跨群存在 $m今日人品
$g开头 群变量,群内所有人共享 $g群主体重

所有变量均可以在 .rx / .rxh / .ra/.text等指令中、以及在自定义文案(text-template.yaml)中使用

如果一部分变量无效,请检查海豹是否为最新版本。

附录:常用变量名表

$t判定结果 // 大成功 成功 失败 $t判定值 $t增量 $t新值 $t旧值 $t变化量 $t表达式文本 $t数量 $t失败数量 $t成功数量 $t属性 $t结果文本 $t计算过程

内置脚本语言

语法

变量机制

见上方 辅助机制-变量机制 一节

数据类型和赋值语句

// 文本类型 <- 注意,目前并不支持写注释,此为教程
$t0 = '文本'
$t0 = "也是文本"
$t0 = `特殊文本类型,可以插入表达式,例如,玩家的力量数值: {力量}`
$t0 = `另一种插入表达式的写法 {% 力量 %} `

// 数字类型
$t0 = 1

// 布尔 没有专门的布尔类型,0或空字符串被视为False,非零和非空字符串为True
$t0 > 1
$t0 >= 1
$t0 == 1
$t0 != 1
$t0 < 1
$t0 <= 1

语句

多个语句可以用 ; 分隔,取分隔后的最后一项的值,为整个表达式的值,例如:

$t0 = 1;2;3

此时$t0的值为3

运算符

数学运算
加减乘除余 + -* / %
乘方 ^ ** // 2 ** 3 或 2 ^ 3 即2的3次方
骰子算符

d 常规骰子算符,用法举例 d20 2d20k1 d20优势

f 命运骰,随机骰4次,每骰结果可能是-1 0 1,记为- 0 +

b 奖励骰(COC)

p 惩罚骰(COC)

c 双十字

条件算符 (?)

灵视 >= 40 ? '如果灵视达到40以上,你就能看到这句话'

可以用这个指令测试,下同:

.st 灵视41
.text {灵视 >= 40 ? '如果灵视达到40以上,你就能看到这句话'}
多重条件算符 (? ,)
灵视 >= 80 ? '看得很清楚吗?',
灵视 >= 50 ? '不错,再靠近一点……',
灵视 >= 30 ? '仔细听……',
灵视 >= 0 ? '呵,无知之人。'

应用举例,默认的jrrp

{$t玩家} 今日人品为{$t人品},{%
    $t人品 > 95 ? '人品爆表!',
    $t人品 > 80 ? '运气还不错!',
    $t人品 > 50 ? '人品还行吧',
    $t人品 > 10 ? '今天不太行',
    1 ? '流年不利啊!'
%}
三目运算符 (? :)
灵视 >= 40 ? '如果灵视达到40以上,你就能看到这句话' : '无知亦是幸运'

条件语句

注意:如果下面的代码输出“格式化错误”,那是因为你的$t0不是数值;字符串当然不能和数值比较大小,所以会报错。 解决方法:.text {$t0=0}

if $t0 > 10 {
    $t1 = "aaa"
} else {
    $t1 = 'bbb'
}
实际测试:
.text {% if $t0 > 10 { $t1="aaa"} else { $t1 = 'bbb' }; $t1 %}

如何使用(成为骰主)

下载安装

首先下载对应系统的压缩包,例如我是Windows系统,选择 sealdice-core_0.0.1_windows_amd64.zip

image-20220313105011020

解压后的目录是这样:

image-20220313105400686

请务必完成解压后运行!千万不要在压缩包里双击!

双击运行 sealdice-core.exe

请注意!!!建议使用Chrome或Edge浏览器,以保证WebUI功能的正常!!

Windows服务器系统请尤为注意,海豹并不支持IE浏览器!

登录平台账号

如果是windows系统,会直接弹出一个Web界面,也可以手动访问:

http://localhost:3211/

image-20220313104349755

随后,点击左侧面板的“帐号设置”,再点击右边的蓝色加号添加一个账号:

image-20220313104448999

输入完成后,点击下一步,根据提示照做即可。这里是需要手机QQ扫码:

image-20220313104522588

如果你看到了“已连接”就说明成功了。

image-20220313105525215

如果没来得及扫,也不要慌,点击“重新登录”,或者”删除“后重新添加帐号即可。

image-20220313105439352

注意登录时不建议使用安卓以外的任何协议,因为极其容易失败,如果想要iOS或手表登录可以登录成功后再换协议。

如果遇到提示需要同WIFI扫码,请参见 [常见问题 - 在服务器上登录时说“需要在同一WIFI下扫码”怎么办](#> 在服务器上登录时说“需要在同一WIFI下扫码”怎么办)

若登录过程出现任何意料之外的结果,请刷新页面,将日志截图,加入企鹅群 524364253将信息发送给我。

成功了之后就可以进行测试了。

骰子的大部分指令只在群内工作,请把骰子拉进群进行使用。

默认骰子进群时会自动开启并发送问候语,如果没有的话请执行 @骰子 .bot on试试

使用签名服务器

签名服务器,即qsign,是用来绕过某大厂封锁的有效手段,对code45/235/237有奇效。

对qsign的支持是海豹1.3.0的最新特性,请旧版本用户升级以体验新功能。

我需要怎么做?

你可以自己在本地搭一个qsign服务器,也可以使用别人搭好的。

Tip:如果你的动手能力足够强或者有足够的电脑知识,强烈推荐自己搭建本地签名服务器。

使用他人的qsign服务可能会泄漏以下信息:

  • 登录账号
  • 登录时间
  • 登录后发送的消息内容
  • 登录后发送消息的群号/好友ID

不会泄露的信息:

  • 账号密码
  • 账号 session
  • 群列表/好友列表
  • 接收的消息
  • 除发送消息外的任何历史记录
本地搭建
windows下搭建

感谢社区,我们现在有了一键qsign

下面的话我是直接引用了一键qsign里的说明

点开以后删掉文件夹里的 go-cqhttp.bat及 go-cqhttp_windows_386.exe,然后运行里面的 Start_Qsign.bat 启动qsign,按照提示依次键入 txlib_version 参数、设定 hostportkey的值。 (👈如果不知道这些是干什么的,请直接依次按下Enter)

Linux下搭建

这里有完整的教程。

Mac下搭建

又双叒叕要感谢社区了(

来自一位天才般的程序员大人的作品。

GitHub - Verplitic/AutoQSignForMac: 为 macOS 构建的 QSign 一键包,受到 Xiangze-Li/qsign-gocq-deploy-pack 和 rhwong/unidbg-fetch-qsign-onekey 的启发。

在终端运行 start.sh 即可配置和启动签名服务器。如果提示 zsh: access denied,需要先运行 chmod -x start.sh 来给予权限。

初次启动会选择 txlib 版本,及运行 QSign 的主机、端口和 API Key。通常情况下,可以回车跳过而使用默认配置。

使用签名服务器

你在登录账号的时候会看到这样一个界面。qsign-1.png点击下面的“签名服务”一栏的“简易配置”,然后会变成这样:

qsign-2.png

服务url:你要链接的qsign url

服务key:密码

服务鉴权:默认为空,如果有的服务器要求特定的鉴权,就填上吧

本地qsign:

你没有特殊设置的话qsign的url默认为http://localhost:13579,mac和win的key没有特殊设置则会在你启动qsign时随机生成一个并显示在qsign的命令行里,而linux的默认就是114514

第三方/远程的qsign:

分享服务器的人会附上url和key,照抄就行了:)

都输完了直接下一步,过验证,进登录流程,成功!

Linux 部署

一键方案

如果是首次部署,可以使用此命令一键安装,但注意以下几点:

  • 脚本会在当前目录创建sealdice子目录,如果目录已经存在将直接失败
  • 为了实现自启动,脚本会自动安装一个名为sealdice的systemd系统服务,请确保对systemd有基本的了解
  • 端口为3211,如果端口被占用,会直接启动失败
  • 部署第二只海豹时,仍需要手动部署
curl -L https://sealdice.com/s/linux.sh | bash

如果一切正常,执行结果如下:

image-20220512034041887

访问 http://ip地址:3211 即可进行使用。随系统自启动。

手动方案

下载解压海豹,在终端执行前,请先给文件加可执行权限:

chmod +x ./sealdice-core

随后运行

./sealdice-core

即可。余下的流程和Windows版本相同。

如果希望安装为系统服务,参考下面一节。

如果你使用SSH部署,不能访问http://localhost:3211,那么改为访问 http://ip地址:3211

Mac 部署

如果你是Mac系统,需要在终端运行SealDice。

注意:如果你是M1或更新的Apple Silicon芯片,那么需要安装Rosetta并使用x64版本

# P.S. 目前有一个测试中的新方案,macOS一键安装!但是注意,此方案是群友编写,开发者并无硬件进行测试。
# 打开终端(terminal),在其中粘贴,回车运行:
curl -L https://sealdice.com/s/darwin.sh | bash

首先,下载并解压海豹,并在有sealdice-core文件的目录下,右键“新建位于文件夹位置的终端窗口”,随后执行下面两条指令。

如果没有这个选项,参考 https://www.cnblogs.com/huangshiyu13/p/14101284.html 进行设置。

将以下两条指令,逐条复制到终端中,回车运行(空格不能省略):

chmod +x ./sealdice-core
./sealdice-core

看到 => http server started on [::]:3221 就是启动成功了。

接下来使用浏览器访问 http://localhost:3211打开用户界面,余下的流程和Windows版本相同。

注意!如果二次启动海豹,最后还是右键打开终端窗口,然后运行指令中第二句(./sealdice-core)。直接双击运行可能会有权限不足的问题。

安装为系统服务 / 自启动

如果你使用远程Linux服务器部署,那么随着SSH终端关闭,通常海豹也会一块关闭。

SealDice提供了一种自动安装为系统服务(systemd服务项)的功能,可以免去手动配置:

./sealdice-core -i
正在安装系统服务,安装完成后,SealDice将自动随系统启动
安装完成,正在启动……

卸载:

./sealdice-core --uninstall
正在卸载系统服务……
系统服务已删除

服务名称和启动服务使用的用户名可以自定义,输入./sealdice-core -h自行查看

安装完成后,可以使用systemctl来管理服务:

systemctl status sealdice
systemctl start sealdice
systemctl stop sealdice
journalctl -xe -u sealdice.service // 查看日志

更多用法参见 systemd文档

P.S. 如果你有自己的onebot服务端,请将其设为WS主动连接模式,手动修改 data/default/serve.yaml即可进行连接。

在手机上运行

海豹的手机版已经进入公测阶段!

你可以在这里下载

手机海豹启动核心前,请点击阅读主界面上的“配置教程”,进行保活配置。确保你的手机允许海豹后台运行并联网。

迁移数据

如果你需要把旧的电脑海豹的数据转移到手机海豹上,那么你需要转移旧海豹的data文件夹。步骤如下:

  1. 关闭你的电脑海豹,复制电脑海豹的data文件夹发送到手机上。
  2. 点击手机海豹的“导出数据”按钮,在设置中勾选“文件同步模式”(平时推荐关闭这个选项,这里只是为了替换数据库特别地需要开启)。
  3. 在你习惯的手机文件管理软件中找到 根目录/document/com.sealdice.dice/sealdice,用之前发送过来的你的电脑海豹的data文件夹替换这里面的data文件夹
  4. 回到手机海豹的界面中点击“导入数据”。导入完成后再点击启动核心,这时你的手机海豹里就应该是你的旧海豹的数据了。同时导入完成后也可以把“文件同步模式”关闭了。

海豹的data文件夹全平台通用,反过来把手机海豹的data文件夹导出后发到电脑上也是可以用的

建议手机海豹每次导入数据前都点一次导出数据,因为手机海豹不能直接操作里面的文件,必须导出修改后再导入。如果使用了一段时间后没有导出就再次点击导入,会导致海豹的数据被之前导出的数据覆盖,丢失这段时间以来录入的角色卡、log、修改的自定义文案等海豹内部的数据。

MOD和自定义

牌堆

仓库:https://github.com/sealdice/draw

牌堆是什么 怎么加在骰子里

牌堆的本质是json文件或者yaml文件,编写遵循两者的语法。

  • 一般的牌堆文件放置在.\data\decks 目录下以单独的文件存在,如果要添加 牌堆,你只需要把下载好的文件放在该目录下,然后对骰子发送 .draw reload 指令就可以了。
  • 对于带图的牌堆压缩包,如果是按照seal格式来的,解压后整个文件夹拖进 decks目录里。 其他情况下的带图的牌堆文件压缩包一般会给一个说明 这里只给出几个大概的名词间等价关系 请自行摸索
dicedata ≈ dice123456789 ≈ dice骰子QQ ≈ cocdata ≈ seal根目录
PublicDeck ≈ draw ≈ decks
mod ≈ helpdoc
pictures ≈ images
Q:.\data\decks 是什么?
A:在我的电脑上就是 D:\Uso\tmp\sealdice-core\data\decks 因为
D:\Uso\tmp\sealdice-core 和每个人解压seal程序的位置有关,是可以自定义的,
所以用 . 来表示

draw 怎么用

抽牌命令:
.draw help // 显示本帮助
.draw list // 查看载入的牌堆文件
.draw keys // 查看可抽取的牌组列表(容易很长,不建议用)
.draw keys <牌堆> // 查看特定牌堆可抽取的牌组列表
.draw search <牌组名称> // 搜索相关牌组
.draw reload // 从硬盘重新装载牌堆,仅Master可用
.draw <牌组名称> // 进行抽牌

特别提醒 牌堆文件名不等于抽取关键词

用什么打开&用什么写

可以用window自带的记事本打开和编写,但不建议,我用的是 vscode ,有人推荐 sublime text. 你真的连上网搜一下都懒得话,群文件里有一个可能过时的vscode安装包 一个合适的编辑器会给指出一些基本错误,比如多一个逗号这种 当然,json在线编辑及检查网站也有,比如自己去搜 更懒一点,你可以花钱找人写(划掉)

总结 与 概览

牌堆编写请使用 UTF-8 编码 我更推荐去写yaml牌堆 因为简单

如果你会json语法
  • draw key 指令中key是对象名,对象key是数组,数组key中的元素就是抽取项

  • key 与 _key 两种格式的区别是 _key 不会在 draw keys 指令中显示

  • 字符串中的 {otherkey} 有特殊意义 表示把 draw otherkey 指令的结果组 合到原字符串中

    {%key}放回抽取 {key}不放回 迭代上限是……

    暂不支持,因此不明

  • 字符串中的[exp] 里会先执行其中的投骰表达式 exp 再组合到原字符串里

    例如 [1d100] 在最终结果中是1

  • 字符串 ::数字:: 格式以开头时表示权重 也就是概率 支持 :::[1d100]:: 格 式 尽管意义不明

  • 字符串里可以插入CQ code 和 其他 Seal 在回复中支持的写法 比如[图:]

如果你会YAML语法
  • 还是要看编写方法 yaml 因为有格式要求
建议的格式
对于json
{
    "署名":[
        "seal尊重牌堆作者的权力,因此建议在你的牌堆里面加上如下的内容",
        "它们将在draw desc指令中被公示",
        "当然,你也可以选择匿名,这只是一个建议"
    ],
    "_title":["文件名"],
    "_author":["作者名"],
    "_date":["发布时间"],
    "_version":["牌堆版本"],
    "_brief":["牌堆的简介"],
    "其他":[
        "如果你有大量相似的词条,请尽量使用{}来进行调用",
        "请尊重别人的权力,仅仅修改了一点内容后就当作全新的牌堆来发布的行为是禁止的",
        "夹带私货的行为最好在简介中写出"
    ],
    "对于带图的牌堆":[
        "seal搜索decks文件夹下子目录时,跳过名为assets 或者 images的文件夹",
        "这意味这这两个目录下的文件不会被视为牌堆文件,你可以在里面放图片",
        "建议将整个牌堆做成目录,并将配套图片放入assets子目录中,方便压缩打包后发布"
        "具体压缩方法,选中牌堆文件和assets目录压缩,压缩包名为'牌堆的名字'。"
        "对于这样的牌堆,解压后直接拖进decks目录就能用"
    ],
    "_牌堆名图片路径":["[图:data/decks/牌堆的名字/assets/"],
    "key":["你的图是{_牌堆名图片路径}图片名及后缀]"],
    "对于QQ平台,把 [图: 换成 [CQ:image,file=data/decks/牌堆的名字/assets/ 也是可以的",
    "注意[]点完整性"
}
对于YAML
  • 按照编写方法即可

不在乎原理的教学

仍旧建议随手学一下两种文件格式

JSON牌堆基本
{
    "key 也就draw指令后面跟的东西":[
        "这是一个可能被抽到东西(后面叫他元素),如果是文字,必须被一对英文引号包裹住",
        "第二个可能被抽到元素,记得给上面那个兄弟加个英文逗号",
        "这是最后一个,这一个不能加逗号"
    ],
    "key 如果不止一个key,写第二个key时记得给上面的[]后面加一个,":[
        "如果没有再后面key了 ]后面就没有,号",
        123456789,
        "如上,纯数字可以不加英文引号",
        "每一个key以及能通过他抽取的在这里都叫做牌组,一群牌组叫牌堆",
        "最后记得用{}包裹住所有的牌组",
        "这里还是建议用点好用的编辑器,别的不说,标点主动补成一对就和自动缩进很舒服",
        "也能很直观的看到编码,牌堆编写请使用 UTF-8 编码",
        "换行和缩进其实没有什么意义,主要是看着好看,但是",
        "两个英文引号之间是不能换行的,这是完全不合语法的",
        "补充:在牌堆中插入图片可以使用CQ码,比如这样",
        "替身!!![CQ:image,file=data/images/JOJO替身集/倒吊男.jpg]",
        "对于带图的牌堆,可以看看下面的'建议的格式'一节。"
        "当然的,同样利用CQ码的语音、表情等等都可以这样",
        "至于CQ码的详细内容,见 https://docs.go-cqhttp.org/cqcode "
    ]
}
JSON牌堆技巧
{
    "牌组之间的调用":[
        "假如我想实现的抽取句子只有一点不同,比如下面",
        "今天吃饼干",
        "今天吃海豹",
        "那么,你可以分开来写,比如下面"
    ],
    "吃什么":[
        "今天吃{食物}"
    ],
    "食物":[
        "海豹",
        "",
        "扁面蛸"
    ],
    "放回和不放回":[
        "显而易见的,{key}表示从牌组key中抽出一个元素合并到自己的元素中",
        "上面的方法,我姑且称为'调用'。",
        "上面的例子里,'吃什么'牌组调用了'食物'牌组",
        "除了{key}之外,还有{%key}这种格式,含有%将采取放回抽取,否则为不放回抽取",
        "seal目前没有实现不放回抽取,预计会实现针对群的不放回",
        "自然的,要小心\"食物\":[\"{食物}\"]这种情况,无限的自我调用"
    ],
    "特殊的符号":[
        "你可能注意到我在上面用了一个怪怪的 \" 反斜杠+英文引号"
        "因为引号在json里面已经用来表示一个文本了,所以不得不用 \" 来表示原本的意思",
        "反斜杠表示转义,有了特别的表示,于是和引号一样,两个反斜杠才是真的反斜杠 \\ ",
        "在上面说过两个英文引号之间不能换行要输出里面换行就要用到换行的转义符 \n",
        "你能看到上面的灰掉了,就是因为在引号之间换行了"
    ],
    "多吃点海豹":[
        "我想用 draw 多抽一点海豹来吃,怎么做",
        "在古老的时代,那些人是这样做的",
        "ctrl c+v",
        "海豹","海豹","海豹","海豹","海豹","海豹",
        "海豹","海豹","海豹","海豹","海豹","海豹",
        "海豹","海豹","海豹","海豹","海豹","海豹",
        "海豹","海豹","海豹","海豹","海豹","海豹",
        "海豹","海豹","海豹","海豹","海豹","海豹",
        "通过重复来调整概率,简直是可怕,幸好后来有了一个新东西",
        "::1:: 两个英文冒号+数字+两个英文冒号写在文本前面表示权重,下面是例子",
        "::23::海豹",
        "可以简单理解为中间的数字表示重复几次",
        "除此之外,在一些远古的牌堆,你还能看见"
    ],
    "数字":[1,2,3,4,5,6,7,8,9,10],
    "幸好我入坑晚点":[
        "现在你不用专门写一个数字牌堆来生成随机数了",
        "你可以[1d100]来表示1-100的随机数",
        "[]里面可以填写任意合法(合法是符合语法)的投骰表达式",
        "一些很实用的牌堆,比如调查员生成就是这样写,幸运:[3d6*5]",
        "你甚至可以::[1d100]::来写一些随机的权重"
    ],
    "_我不想给别人draw我的牌堆":[
        "在key前面加一个_,变成_key",
        "这样的牌组不可以使用draw指令抽取,但仍然可以通过{_key}调用",
        "初衷大概就是修饰一下你的draw key指令",
        "即前面有下划线的牌组不会在draw key中出现"
    ],
    "其他的":[
        "一些编辑器提供了格式化文档的功能,比如手机上用的MT管理器,你可以用他检查(格式化失败就是写错了)",
        "甚至格式化的时候可以把那些不合法的换行变成\n,这样就不用想象输出的排版了"
    ]
}
YAML牌堆
name: 塔骰格式要求比Dice!格式严格 也更加方便 在这里填牌堆名字
author: 在这里填写作者名字
version: 这里版本号
command: 指令名字 也就是keys里的内容
desc: 牌堆的简介
includes:
#好事是seal暂不支持 因此下面的可以无视
#用#号开头的是注释 不会影响牌堆 怎样写都可以
#includes是json牌堆没有的 意思包含的可抽取key 也就说你没必要通过_key来修饰keys命令了
#塔的抽牌格式是 deck command <可选的子命令>
    - default # 这里等价与command项 抽取时是 deck command
    - "key" # 子命令 抽取时是 deck command key
default:
    - "{%key0}" # 与Dice恰好相反 这里是不放回 暂不支持
    - "{%key1}" # 这是放回抽取 暂不支持
    - "正常的元素" # 也就是被抽取的内容
key:
    - "如果你会yaml语法,那么你可以去看看json的写法,除了上面说过的基本一致"
    - "如果不会 那你就学 或者 套格式"
key1:
    - "贴着一行的开头写key 然后打个英文冒号"
    - "换行 打一个空格 打一个减号 然后是引号包裹的内容"
    - "空格数量无所谓 但你要保证他们对齐 也就是同一层级"
key2:
- "" # 没了 其他编写牌堆的技巧看json的那

自定义回复

仓库:https://github.com/sealdice/reply

其实写这篇教程的原因是,群文件里大佬的教程对一些人(比如笔者)来说太过高冷,后来跌跌撞撞地才学会了一点,我不想因为这些吓退他们,或者让他们觉得这很难…… 所以如果看到了熟悉的地方,请不要怀疑,没错,是我从大佬的教程上摘抄的…… 请不要忽视注意,注意中都是新手(笔者)常犯且犯过的错误,避免踩坑! 总之,先感谢<于言诺>大佬的教程,让我们开始!

0.如何优雅地偷懒

.text将是你的一大助力。他会输出他后面的执行结果,这和自定义回复中的“回复”长得一模一样,因此你不必新建回复,设置触发,最后不厌其烦地输入触发词。你可以将他看成不需要触发词的自定义回复。

当然,在采用 .text 指令进行debug时,可能出现因为测试的内容对变量造成影响,而不得不频繁复制黏贴清空指令的情况。因此,在debug到烦的时候,相当建议专门开一个一两个字即可触发的自定义回复,用来节省你的剪贴板。

1.创建一个自定义回复

首先我们打开“自定义回复”一项,可以看到默认的reply.yaml应该是一片空白。让我们新建一项自定义回复。

2.触发条件

你应该可以看到,新建的自定义回复中分“条件”和“结果”两个部分。我们先说说条件。 条件的最上方有一个“文本匹配”,点击他,展开下拉菜单,你可以发现还有“文本长度”和“表达式为真”两项,一共三项。我们接下来就按顺序,逐个讲解。 条件可以有多个,但是必须同时满足,即“and”。 海豹接收到以后就会返回你给定的内容。注意,在这里,是可以嵌入CQ码的,任何特殊消息类型都会被解析为CQ码。 CQ码列表 这里有些复杂,本人完全照搬(不是)大量借鉴了群友<于言诺>的描述:

当前版本的海豹提供如下几种匹配方式:

  • 文本匹配
    • 精确匹配
    • 包含文本
    • 不包含文本
    • 模糊匹配
    • 正则匹配
    • 前缀匹配
    • 后缀匹配
    • 任意相符
  • 文本长度匹配
    • 大于等于
    • 小于等于
  • 表达式为真

在这里,需要着重指出几处可能导致匹配结果不符合预期的情况:

  1. 模糊匹配中,是按照特殊算法进行匹配的,因此可能出现感觉能匹配上,实际上没办法匹配上的情况。如果想要某段文本出现就被回复,请使用包含文本匹配或正则匹配。
  2. 使用正则匹配时,直接在要匹配的文本一栏中写入正则表达式即可。正则表达式可以参照https://www.runoob.com/regexp/regexp-tutorial.html 在这里给出一个示例: ^测试(.+)吗$ 这一正则表达式将会匹配所有开头是 测试 而结尾是 的回复,但中间如果存在换行则不匹配。如果没有 ^ $ 两个符号,则会是同时包含 测试 的任何发言。此乃贪婪匹配是也,所以请注意做好限定。
  3. 在文本长度匹配中,需要注意的是,一个汉字算作两个字符。如 你好 两字可以触发大于等于4的文本长度匹配。
  4. 在表达式为真匹配中,只需要直接在匹配文本中写出形如 变量名==需要的值 的形式即可,不需要使用任何 {}

在这里说一下新增的“任意相符”条件: 任意相符,顾名思义,只要符合了几个中的任意一个,就能触发回复。 例子:

设置:任意相符,文本a|b,回复c;  
输入:a  
回复:c  
输入:b  
回复:c  
输入:ab  
回复:不回复  
输入:a|b  
回复:不回复
输入:其他  
回复:不回复

正则匹配和精确匹配可能是自定义回复中使用最多的匹配。对于正则匹配而言,通过恰当的分组,可以做到将回复中的一部分存入变量中以备调用。

正则匹配:^购买(.+)
输出文本:{$t玩家}购买了{$t1}

购买猫粮
Dice: <于言诺>购买了猫粮

在一些官方教程中也有提到过,对于匹配到的自定义回复整条消息,会将其存入 $t0 ,而正则分组会存入 $t1 中。在如上例子中,就是将 购买 后用括号括起来的部分存入了 $t1 中,从而可以进行调用。如果有多组正则分组,则会按顺序存入 $t1 $t2 $t3 中。如果额外存在组名,如 (?P<A>cc) ,将会额外存入 $tA

在使用正则回复时请注意,由于 . 。 / ! 等符号会作为海豹中指令的前缀,因此作为前缀时可能导致将其识别为指令而非自定义回复的情况,建议换成别的前缀。

*注意:海豹支持用正则匹配CQ码,但是如果你这么做了,请在[]前面放反斜杠(\)转义。 示例:^\[CQ:xxx,xx=xxx\]

3.返回结果

(1)回复方式

你应该可以看到,结果一栏的左上角有一个默认为“回复”的下拉框。点击它,会展开三个回复方式。其中”回复“就是回复,而”私聊回复“可以做到类似暗骰的效果,“群内回复”则是在群里回复。

(2)高阶玩法

看到这里,你肯定想问: 就这?没有更复杂一些的?没有能挑战自己的? 别急,这就到了。 在回复文本中,可以调用一些变量,也可以嵌入内置脚本语言

调用变量

对海豹输入 .text {$t玩家}进行测试 返回则是 <流溪>进行测试(流溪是我的qq昵称/我在海豹那里。nn的名字)

嵌入脚本语言

.text {d100} 输出的是掷一个d100的结果(只有数字)。详情见脚本语言

变量赋值

所有变量在未被赋值时被调用的值都为0,而不是什么“空白”或者报错。 一般来说,直接使用"变量=值"是最常用的。当然,变量也可以等于骰子算符。 详细内容参见内置脚本语言

*注意:如果用作判断(多用于if),则是"变量==值"。

执行块

{% %}被称为执行块。 由{% %}括起来的部分会被作为代码执行,然后输出最后一个语句的结果。

如果想要输出字符串,则应该用反引号/双引号/单引号括上。(例:形如{% $t1="114514" %}的式子会输出114514)。 另:若想在结果中调用变量,请用反引号。目前海豹只支持在反引号中调用变量。

执行块中的两个语句之间要使用分号(;)隔开。 只要有一点点编程基础应该都能理解这个地方的语法。下面让我们看一个简单的示例: .text {% $t测试=1 %} 这是一个最简单的示例。这里的第一句,也是最后一句,所以他的结果会作为“最后一句的结果”被输出。

if语法

if的使用很简单: if 条件 {结果}; if 条件 {结果} else {结果} 这里举一个简单且有趣的例子:

{%  
$t测试=1;  
if $t测试==1 {$t输出="赞美木落"};  
if $t测试==2 {$t输出="快点更新"};  
if $t测试!=2&&$t测试!=1 {$t输出="群主女装"}  
%}  
{$t输出}  

(当然我为了看着方便进行了适当格式化) 如果你可以看懂这段代码,你就可以出师了(笑)。 看不懂也没关系,我们逐行解析:

首先,给$t测试赋值为1。 然后进入if判断: 1.如果$t测试等于1 则变量$t输出等于"赞美木落"; 2.如果$t测试等于2 则变量$t输出等于"快点更新"; 3.如果$t测试既不等于2也不等于1 则变量$t输出等于"群主女装"; 3.最后,输出变量$t输出。 (关于我为什么不用else,请看这里) 在这个例子里,我们一开始就对$t测试进行了赋值为1,而且直到if前都没有变过,所以,$t输出就是"赞美木落",而最终输出的也是这个。

接下来是一个简单的变量综合运算示例:

{%  
$t0=1;  
$tRand=d6;  
if $t0==1 {$t0=$t0+$tRand}  
%}  
{$t0}

在这个例子里,我们先给t0赋值为1,然后判断t0是否等于1,若通过则t0的值增加1d6,最后输出结果。 这就像一种简单的编程语言,不是吗?

注意:

当形如:

if xxx {xxx};  
if xxx {xxx}  
else {xxx}  

的式子出现以后,第二个if实质上是和else配对的,而不是 if elseif elseif else的关系!即使满足了第一个if,其结果也是执行else中的内容! 所以,如果将上文的 if $t测试!=2&&$t测试!=1 {$t输出="群主女装"}更换为 else {$t输出="群主女装"}的话,逻辑如下: 1.判断第一个if,判断通过,$t测试被赋值"赞美木落" 2.判断一对if……else,if不成立,所以运行else的结果,最终$t测试被赋值为"群主女装"。 如果想实现 if elseif elseif else的逻辑,有一种笨方法,像前文示例一样,用 if xxx&&xxx {xxx}来代替。

(3)我们还有不足之处……
目前已知的bug有:

1.形如 xxx{{$t1=xx};{$t2=xx}}的回复会输出格式错误,但实际上已经执行了; 注:该问题已确认,会在未来版本进行修复,如果你发现了其他bug,欢迎在 https://github.com/sealdice/sealdice 中提交issue

(4)紧张刺激的实战例子!

1.养猫,撸它,随机反应(牌堆与自定义回复的结合)

{% 
$mCatFavor<=100 ? `#{DRAW-第一档猫好感}`,
$mCatFavor<=250 ? `#{DRAW-第二档猫好感}` ,
$mCatFavor<=600 ? `#{DRAW-第三档猫好感}` ,
$mCatFavor<=1500 ? `#{DRAW-第四档猫好感}` ,
$mCatFavor<=2500 ? `#{DRAW-第五档猫好感}` ,
1 ? `#{DRAW-第六档猫好感}`
 %}

以上是一段来自于我自己写的养猫玩玩.yaml的脚本。在养猫玩玩这一脚本中,我采用的是根据猫好感度 $mCatFavor 不同,需要输出不同回复的机制。因此,为了实现这一想法, 可以采用形如:

{% 
判断1 ? `#{DRAW-牌组1}`,
判断2 ? `#{DRAW-牌组2}`,
判断3 ? `#{DRAW-牌组3}`,
1 ? `#{DRAW-牌组4}`,
 %}

的写法。这样的写法可以极大降低自定义回复中代码过于冗长的情况,或者你想要写这种?

{% 
if 判断1 {
  $tRand=d6;
  $t输出=$tRand==1?`内容1`,
  $t输出=$tRand==2?`内容2`,
  $t输出=$tRand==3?`内容3`,
  $t输出=$tRand==4?`内容4`,
  $t输出=$tRand==5?`内容5`,
  $t输出=$tRand==6?`内容6`
  };
if 判断2 {$tRand=d10;$t输出=$tRand==1?`内容1`,……};
if 判断3 {$tRand=d10;$t输出=$tRand==1?`内容1`,……};
 %}
//顺带一提,这还是通过在赋值中使用条件算符,降低了代码行数的情况。

就,对自己好一点吧。

2.海豹,重命名,踹!(注意自定义回复输出格式;多个条件时如何处理)

可能有人思考过,为什么我的踢海豹.yaml中, 踢海豹 这一自定义回复的输出并不是:

{$t输出0}
{$t输出1}
{$t输出2}
{$t输出3}

的形式,而是在为四个 $t输出 变量赋值时在内部写上\n,并采用 {$t输出0}{$t输出1}{$t输出2}{$t输出3} 的形式。

实际上,因为 $t输出2 这一变量仅在触发上行的梯子事件或下行的滑道事件时不为空,因此在一般情况下,如果采用第一种分行的写法,会出现这样的效果:

踢海豹
Dice: <于言诺>一脚踢向海豹,踢了3格。
海豹现在离终点还有37格。

海豹的逃走骰:1d100=60,海豹没能逃脱凶猛恶汉们的掌控!

中间会出现突兀的空行。这是因为虽然并没有在变量内部赋值 \n ,但是由于海豹读取时会按照写的格式读取,因此在应当是 $t输出2 的一行中,会照样调用 {$t输出2} ,并且照常空行。

所以,如果想要某个变量为空时,看不出来这里应该存在什么,就最好多做几次实验,好好规划一下换行符 \n 这些应该搁哪。

还有一个同样也是 $t输出2 部分的问题。实际上,现在发布版本中的判断并不是最简写法,不过这个打算等木落发布数组、函数和循环的写法之后一并优化。

对于类似踢海豹中判断是否触发事件这类在多个前提分别有对应的回复之后,需要为剩余情况统一输出一个结果的写法,实际上可以采用多重条件算符或条件语句实现。多重条件算符的问题是无法在结果中对多个变量进行赋值,只能直接输出;而在条件语句中,由于海豹当前不支持 if……else if……else 的写法,所以可以采用如下写法:

{% 
if 1 {
  剩余情况输出的回复;
  剩余情况需要的赋值
  };
if 判断1 {
  回复1;
  回复1需要的赋值
  };
if 判断2 {
  回复2;
  回复2需要的赋值
  }
 %}

通过在最开始令所有情况下的输出值变为其他情况下需要的回复,而在之后拣出需要特殊回复的情况的方式,可以达成类似于 if……else if……else 的效果。当然,如果不嫌麻烦,也可以参考踢海豹里面的使用标记变量的方式,但还是那句话,没必要折磨自己。

3.打卡,攒钱并购买(如何限定每人/每群每天一次)

其实写这个主要是因为有人问了怎么限定每人每天一次,刚好我写过类似的东西,所以顺便写一下。

海豹提供了一系列时间变量来调用,虽然对于V1.0.2 v20220820之前的版本而言,这一系列变量存在在条件中不会刷新从而导致错误判断的bug,但是对于这及之后的版本而言,这一问题不存在。因此,在新版本中,可以采用如下两种写法中的一种(示例为每人每天一次,如要每群自行将 $m 换成 $g ):

//写法1:
文本匹配:你需要的文本
表达式为真:$m变量!=$tDate
回复:{if 1 {$m变量=$tDate}}你需要的回复文本//使 $m变量 作为标记变量,用if套娃是防止它出现在回复文本中。

文本匹配:你需要的文本
表达式为真:$m变量==$tDate //也可以不写然后放到相较于上一条的后面,利用海豹从上往下逐个匹配的机制达成相同效果
回复:在一天触发多次时的回复

//写法2:
文本匹配:你需要的文本
回复:
{% 
if $m变量!=$tDate {
  $t输出=`你需要的回复文本`;
  $m变量=$tDate//对其赋值,作为标记
} else {
  $t输出=`在一天触发多次时的回复`
  }
 %}
{$t输出}

以上两种写法实现效果没有差别,具体使用哪种请自行决断。

4.石头剪刀布,判断?(条件语句嵌套条件算符;多条件条件语句)

其实直接复制黏贴了我自己写的石头剪刀布编写中的思路和解释中的一部分*樂

为了实现骰子随机出招的效果,令 $tRand=d3 ,然后根据 $tRand的情况赋值 $tDicePlay ;通过骰子出招和玩家出招两个变量判断,输出游戏结果,并记录场次。

为了防止直接使用 {%%} 进行赋值导致的变量显示,需要在最外面写 if 1 ,则在生成 $tRand之后再次判断的时候,可以使用 $tDicePlay=条件算符 ,或是再新开一行用条件算符或者条件语句实现。以下给出在同一个if内直接赋值的写法和新开一行使用条件语句的写法:

{% //在同一个if内直接赋值。可以在赋值时使用条件算符。
if 1 {
    $tRand=d3;
    $tDicePlay=$tRand==1?"石头",
                $tRand==2?"剪刀",
                1?"布"
    } 
%}

{% //新开一行赋值
if 1 {
    $tRand=d3;
    } ;
if $tRand==1 {$tDicePlay="石头"};
if $tRand==2 {$tDicePlay="剪刀"};
if $tRand==3 {$tDicePlay="布"}
%}

两种写法实现效果相同,石头剪刀布内在这里采用了第一种,实际上没有差别。

生成骰子出招并获取玩家出招之后,就开始判断。这里除了平局可以使用 $tDicePlay==$t0 省事之外,其他的都需要在条件中用多个进行嵌套。提醒,豹骰语法中,判断时条件中的 ||&& 是从左往右计算的,如果后面有需要优先计算与或的东西,请加好括号!!!

if $t0==$tDicePlay {
    $t输出=`那我出{$tDicePlay}!{$t玩家}出的是{$t0}啊,我们平局了。`;
    $mPlayerTime=$mPlayerTime+1
    };
if $t0=="剪刀"&&$tDicePlay=="石头"||($t0=="石头"&&$tDicePlay=="布")||($t0=="布"&&$tDicePlay=="剪刀") {
     //后两个与需要单独计算,加上括号
    $t输出=`那我出{$tDicePlay}!{$t玩家}出的是{$t0}啊,我赢了。`;
    $mPlayerTime=$mPlayerTime+1;
    $mPlayerLose=$mPlayerLose+1
    };
if $t0=="石头"&&$tDicePlay=="剪刀"||($t0=="布"&&$tDicePlay=="石头")||($t0=="剪刀"&&$tDicePlay=="布") {
    $t输出=`那我出{$tDicePlay}!{$t玩家}出的是{$t0}啊,你赢了。`;
    $mPlayerTime=$mPlayerTime+1;
    $mPlayerWin=$mPlayerWin+1
    }

感谢大佬的努力,令吾等咸鱼有了用武之地! 另,海豹将在未来实现dicescript,介时文档会有较大变动

帮助文档 helpdoc

仓库:https://github.com/sealdice/helpdoc

海豹目前可以健康食用的helpdoc格式为.json以及.xslx,其中后者更多用于快捷编写查询文档(.find)。

0.如何优雅地偷懒

编写帮助文档非常容易,或者说——你基本上只用依照模板填入你需要的内容即可。若非万不得已,不要直接使用window自带的记事本打开和编写helpdoc.json,使用Sublime等软件是更好的选择,这能极大程度减轻你的工作量。

Sublime:https://www.sublimetext.com/

或者在线JSON编辑网站:在线JSON (仅做示例,你可以通过搜索引擎寻找你更喜爱的编辑网站)

Helpdoc编写请使用 UTF-8 编码。

1.什么是帮助文档

帮助文档用于编写骰子使用帮助或骰子的查询资料。当你在帮助文档中写下词条A后,通过.help 词条A 或 .find 词条A 获得的结果是一样的。

如果你希望你的用户在对骰子发送 .help 使用须知 后,骰子能回复对应内容,你可以这样写:

"使用须知": "请勿在未经允许的情况下将骰子用于非TRPG相关活动,骰主保留删除好友、退群、拉黑等操作权利。",

或者你希望你的骰子能对 .find 海豹骰 做出回应,你可以这样写:

"海豹骰": "一个简单易用的跑团骰子系统。/n形象是海豹,可以被叫做海豹骰、豹骰,豹子骰之类。",

2.创建一个Helpdoc

JSON

你可以直接按照以下格式书写helpdoc.json

{
    "mod": "名字",
    "author": "作者",
    "brief": "概述",
    "comment": "备注",
    "helpdoc": {
        "词条A": "词条A的具体内容。",
        "词条B": "词条B的具体内容。",
        "词条C": "[图:data/images/sealdice.png]词条C的图片与内容。"
        }
}

你需要注意的地方在于:

  • 除包含在""之间的文本外,你应当在全文使用半角符。使用全角符会导致格式出现问题。
  • 你应该在每一条的最后加上半角符逗号作为结尾!
  • 特别地,不要在最后一条后加逗号!
  • 若你的文本需要换行,你可以使用\n作为换行符,而不是在编写时直接换行,这会导致格式错误。你还可以使用\f或{FormFeed}作为分页符。
- 进阶

你甚至可以在JSON格式的帮助文档中套娃。

{
    "helpdoc": {
        "词条A": "词条A的具体内容。",
        "词条B": "{词条A}词条B的具体内容。",
        "词条C": "你还可以{词条A}\n{词条B}"
        }
}

如此一来当你发送.help 词条B 时,骰子将回复:“词条A的具体内容。词条B的具体内容。”

EXCEL

你还可以按照以下格式书写helpdoc.xlsx

Key Synonym Content Description Catalogue Tag
词条A 词条同义词 词条内容 对词条的简述 所属目录 内容TAG
海豹骰 豹骰/海豹/sealdice 一个简单易用的跑团骰子系统。``形象是海豹,可以被叫做海豹骰、豹骰,豹子骰之类。

helpdoc.xlsx原本是梨骰用于DnD词条查询的格式,但豹骰同样可以读取它。

  • 不难看出,Excel格式的helpdoc分为Key、Synonym、Content、Description、Catalogue、Tag六块,其中第二列、后三项为选填,一般情况下你都可以留空。
  • Synonym列可以填写多个同义词,使用/分隔即可。
  • 注意!不要删去第一行的Key、Synonym等词。请从第二行开始编写词条。
  • 不难看出,Excel中的词条内容可以直接换行(也可以使用\n来换行,但不支持其它换行符与分页符),因此它常用来快速编写有大段文字的帮助文档。
  • 帮助文档所在工作表的名称会作为前缀加入词条。如将工作表命名为测试,则对应的词条会显示为 测试:词条A。由于海豹的.find指令会对词条进行模糊检索,所以你不必担心前缀对查询带来的影响。

3.使用helpdoc

将helpdoc移动至海豹根目录\data\helpdoc下,再对骰子发送.help reload,等到骰子回复“帮助文档已经重新装载”后即可正常使用。

你会发现helpdoc文件夹下还有coc、dnd两个文件夹,它们储存了coc与dnd的查询资料。在helpdoc文件夹下创建新的文件夹不会影响骰子对helpdoc的读取,因此出于功能考虑,推荐你在helpdoc文件夹下新建对应的文件夹来存放你编写的帮助文档。

示例文档:塔罗牌(节选)
{
    "mod": "KIYTarot",
    "author": "浣熊旅記",
    "brief": "KIY塔罗牌",
    "comment": "释义来源网络。",
    "helpdoc": {
        "愚者逆位": "漂泊,冒险,鲁莽,冒失,疯狂,无视物质损失,灵魂堕落,内心空虚,感情轻浮。",
        "魔术师逆位": "方向错误,被骗或失败,局面失控,二流角色,缺乏热忱和创造力,爱情难有进展。",
        "女祭司逆位": "挑剔,贪心,目光短浅,洁癖,不适宜的激情,自尊心太强,锋芒外露,单相思。",
        "女皇逆位": "冷淡,缺乏上进心,困难,享乐,环境险恶,贴近自然,自负,纠纷,感情挫折。",
        "皇帝逆位": "幼稚,挫折,武断,滥用权利,冷酷,占有欲和控制力强烈,感情勉强。"
        }
}

JS脚本

憧憬少:木落佬要烤的饼还挺多的,于是帮他打个下手,完善一下JS脚本部分的文档吧。由于我也才刚接触海豹的JS扩展,对其了解不深,可能会有错误之处。

已经支持使用JavaScript编写扩展,示例与已有扩展见:https://github.com/sealdice/javascript

如何安装扩展?

  1. 在扩展仓库或者群文件找到你要安装的JS扩展(扩展名为 .js的文件)并下载
  2. 打开你骰子的Web界面,在侧边栏找到【扩展功能】->【JS扩展】
  3. 有两个选项卡,“控制台”和“插件列表”,在“插件列表”中点击“上传插件”按钮,上传刚刚下载的扩展文件,并点击“重载JS”按钮即可。

如何编写扩展?

编写扩展需要了解JavaScript,它非常好学,你如果已经学了自定义回复里使用的内置脚本语言,你会发现很多内容只是换了个写法而已。

由于网上优秀的JavaScript教程非常多(见下方),在这里就不赘述,而是重点讲一讲海豹提供的接口。

0.如何优雅地偷懒

放弃系统自带的记事本软件,拥抱vscode吧,正如@于言诺 大佬在《海豹骰点核心SealDice 个人自定义回复编写经验》中所言:

请所有阅读本文档的海豹自定义回复编写者注意,只要你的设备支持,你都不应该使用windows自带的txt或任何非代码编写器的软件编写自定义回复或牌堆。

有那么多好用的代码编写器,如windows的vs,vscode,sublime text,安卓的MT管理器,请不要折磨自己用txt,至少这些软件还会告诉你世界上有个很好用的东西叫报错

vscode相比于记事本的优势:

  • 写错的地方会给你标出来,不用等到安装完才发现
  • 强大的编辑器功能,例如语法高亮、显示行号、灵活的多行光标、自动补全、悬停提示、批量重命名、正则查找替换、自动保存、版本控制等
  • 丰富的扩展
  • 更多这里写不下的功能

如果嫌官网下载得太慢,还可以在群文件的“工具&内置文件”中找到vscode的安装包。

如果觉得英文界面用起来太困难,可以在vscode扩展商店中安装中文扩展。

(顺便一提,和海豹合作的跑团replay视频制作软件回声工坊也主要使用vscode作为log编辑工具,拥有vscode扩展支持。使用vscode说不定以后也会有相应的辅助扩展来帮忙在写JS扩展时偷懒)

(已经有了,名为Sealdice Snippets,在vscode的扩展商店就能找到并直接安装,提供了一些常见代码片段,可以快速生成模板代码)

1.创建一个JS扩展

你可以选择使用JavaScript或者Typescript来作为你的编写语言。

Typescript是JavaScript的超集,如果你不太懂这是啥,可以理解为是强化版的JavaScript(虽然这么说可能不太准确)。

Typescript文件可以编译为JavaScript文件,安装的时候也是安装编译好的JS文件,但Typescript更优秀的特性可以帮助你在编写扩展的阶段更方便。

更推荐使用Typescript,不过你可以先从JavaScript开始。

如果你打算使用JavaScript,那么新建一个文本文件,将文件扩展名改为 .js即可。

如果你打算使用Typescript,可以使用github上的扩展模板,注册扩展和指令的代码已经写好,可以直接编译出一个可直接装载的JS扩展文件。

JS扩展的示例:

// ==UserScript==
// @name         示例:如何开始
// @author       木落
// @version      1.0.0
// @description  这是一个演示脚本,并没有任何实际作用。
// @timestamp    1671368035
// 2022-12-18
// @license      Apache-2
// @homepageURL  https://github.com/sealdice/javascript
// ==/UserScript==

/*
这里是海豹支持的js脚本范例
海豹使用的js脚本引擎为goja.
在几次更新后,goja支持了ES6的基本上全部特性,包括async/await,promise和generator.

特别注意一点是js引擎的整型为32位,请小心溢出问题。

推荐使用的语法风格为airbnb风格,内容较多这里不赘述,其有代表性的一些特征为:
使用两空格缩进,{不换行,必须写分号,只用let不写var等。

if (true) {
  let a = 123;
  console.log(a);
}

推荐有经验的用户使用typescript,但注意要编译打包后才能使用,target选es6应当可以工作。

还有一个小提示:
console打印出来的东西不光会在控制台中出现,在日志中也会显示。
涉及网络请求或延迟执行的内容,有时候不会在控制台调试面板上显示出来,而在日志中能看到。

以及重要提醒:
不要灌铅!不要灌铅!不要灌铅!
*/

console.log('这是测试控制台');
console.log('可以这样来查看变量详情:');
console.log(Object.keys(seal));
console.log('更多内容正在制作中...');
console.log('注意: 测试版!API仍然可能发生重大变化!');

2.留下作者信息

每个JS扩展需要在开头以注释的形式留下如下信息以便大家使用:

// ==UserScript==
// @name         脚本的名字
// @author       木落
// @version      1.0.0
// @description  这是一个演示脚本,并没有任何实际作用。
// @timestamp    1672066028
// @license      Apache-2
// @homepageURL  https://github.com/sealdice/javascript
// ==/UserScript==
属性 含义
@name JS扩展的名称,会展示在插件列表页面
@author 作者名
@version 版本号,可以自己定义,但建议遵循语义版本控制(Semantic Versioning)
@description 对扩展的功能的描述
@timestamp 最后更新时间,以秒为单位的unix时间戳,可以搜索一些时间戳在线转换工具来获取当前时间戳
@license 开源协议,示例中的Apache-2是一个比较自由的协议,允许任意使用和分发(包括商用)
@homepageURL 你的扩展的主页链接,有github仓库可以填仓库链接,没有则可以填海豹官方插件仓库链接

3.自定义扩展

扩展机制可以看做是海豹的mod管理器,可以模块化开关海豹的任意一部分,如常用的开启dnd扩展,关闭coc扩展,关闭自动回复等等。

可以通过[.ext命令](#.ext 扩展管理)来进行具体操作,所有指令必须归属于某个扩展,而一个扩展可以包含多条指令。

例如内置扩展 coc7包含了如下指令:coc、en、li、rc/ra、rcv/rav、sc、setcoc、st、ti

可以通过Web图形界面中【综合设置】下的【基本设置】的最底下,设置各个扩展及其指令的默认开启状态。

出于对公平性的考虑,js脚本不能替换内置指令和内置扩展。

创建扩展之后,要注意还需要注册扩展,才能让扩展起效,不要漏掉哦!

// 如何建立一个扩展

// 首先检查是否已经存在
if (!seal.ext.find('test')) {
  // 不存在,那么建立扩展,名为test,作者“木落”,版本1.0.0
  const ext = seal.ext.new('test', '木落', '1.0.0');
  // 注册扩展
  seal.ext.register(ext);
}

4.自定义指令

想要创建一条自定义指令,首先需要创建一个扩展(seal.ExtInfo),写好自定义指令的实现逻辑之后,再注册到扩展中。

接上一个标题下的代码,假设目前已经注册好了一个名为 test的扩展,现在要写一个名为 seal的指令。

  • 这个命令的功能为,显示“抓到一只海豹的文案”;
  • 如果命令写“.seal ABC”,那么文案中将海豹命名为“ABC”;
  • 如果命令中没写名字,那么命名为默认值“氪豹”。

第一步,创建新的自定义指令,设置好名字和帮助信息。

const cmdSeal = seal.ext.newCmdItemInfo();
cmdSeal.name = 'seal'; // 指令名字,可用中文
cmdSeal.help = '召唤一只海豹,可用.seal <名字> 命名';

第二步,编写指令的具体处理代码。

你需要编写指令对象的 solve函数,而在使用该指令的时候,海豹核心会调用你写的这个函数。

cmdSeal.solve = (ctx, msg, cmdArgs) => {
    //这里是你需要编写的内容
};
参数 说明
ctx 主要是和当前环境以及用户相关的内容,如当前发指令用户,当前群组信息等
msg 原始指令内容,如指令文本,发送平台,发送时间等
cmdArgs 指令信息,会将用户发的信息进行分段,方便快速取用

这里仅说明需要用到的接口,详细可见插件仓库examp_ts目录下的 seal.d.ts文件,里面包含了目前开放的接口的定义及其注释说明。

参数与返回值

假设用户发送过来的消息是 .seal A B C,那么可以用 cmdArgs.getArgN(1)获取到 AcmdArgs.getArgN(2)获取到 BcmdArgs.getArgN(3)获取到 C

通常会对参数值进行判断,随后作出响应。

以下代码处理的是 .seal help的情形:

cmdSeal.solve = (ctx, msg, cmdArgs) => {
  // 获取第一个参数,例如 .seal A B C
  // 这里 cmdArgs.getArgN(1) 的结果即是A,传参为2的话结果是B
  let val = cmdArgs.getArgN(1);
  switch (val) {
    case 'help': {
      // 命令为 .seal help
      // 创建一个结果对象,并将showHelp标记为true,这会自动给用户发送帮助
      const ret = seal.ext.newCmdExecuteResult(true);
      ret.showHelp = true;
      return ret;
    }
    default: {
      //没有传入参数时的代码
      return seal.ext.newCmdExecuteResult(true);
    }
  }
};

注意,在执行完自己的代码之后,需要返回指令结果对象,其参数是是否执行成功。

核心代码

给消息发送者回应,需要使用 seal.replyToSender()函数,前两个参数和 solve()函数接收的参数一致,第三个参数是你要发送的文本。

发送的文本中,可以包含自定义回复中的变量(例如 {$t玩家}),也可以包含CQ码,用来实现回复发送者、@发送者、发送图片、发送分享卡片等功能。

在这个例子中,我们需要获取作为海豹名字的参数,获取不到就使用默认值,随后向消息发送者发送回应。

在刚刚的位置填入核心代码,就可以完成了。

cmdSeal.solve = (ctx, msg, cmdArgs) => {
  // 获取第一个参数,例如 .seal A B C
  // 这里 cmdArgs.getArgN(1) 的结果即是A,传参为2的话结果是B
  let val = cmdArgs.getArgN(1);
  switch (val) {
    case 'help': {
      // 命令为 .seal help
      // 创建一个结果对象,并将showHelp标记为true,这会自动给用户发送帮助
      const ret = seal.ext.newCmdExecuteResult(true);
      ret.showHelp = true;
      return ret;
    }
    default: {
      // 命令为 .seal XXXX,取第一个参数为名字
      if (!val) val = '氪豹';
      // 进行回复,如果是群聊发送那么在群里回复,私聊发送则在私聊回复(听起来是废话文学,但详细区别见暗骰例子)
      seal.replyToSender(ctx, msg, `你抓到一只海豹!取名为${val}\n它的逃跑意愿为${Math.ceil(Math.random() * 100)}`);
      return seal.ext.newCmdExecuteResult(true);
    }
  }
};

第三步,将命令注册到扩展中。

ext.cmdMap['seal'] = cmdSeal;

如果你想要给这个命令起一个别称,也就是增加一个触发词,可以这样写:

ext.cmdMap['seal'] = cmdSeal;//注册.seal指令
ext.cmdMap['海豹'] = cmdSeal;//注册.海豹指令,等效于.seal

完整的代码如下:

// ==UserScript==
// @name         示例:编写一条自定义指令
// @author       木落
// @version      1.0.0
// @description  召唤一只海豹,可用.seal <名字> 命名
// @timestamp    1671368035
// 2022-12-18
// @license      Apache-2
// @homepageURL  https://github.com/sealdice/javascript
// ==/UserScript==

// 编写一条自定义指令
// 先将扩展模块创建出来,如果已创建就直接使用
let ext = seal.ext.find('test');
if (!ext) {
  ext = seal.ext.new('test', '木落', '1.0.0');
  seal.ext.register(ext);
}

// 创建指令 .seal
// 这个命令的功能为,显示“抓到一只海豹的文案”
// 如果命令写“.seal ABC”,那么文案中将海豹命名为“ABC”
// 如果命令中没写名字,那么命名为默认值“氪豹”
const cmdSeal = seal.ext.newCmdItemInfo();
cmdSeal.name = 'seal'; // 指令名字,可用中文
cmdSeal.help = '召唤一只海豹,可用.seal <名字> 命名';

// 主函数,指令解析器会将指令信息解析,并储存在几个参数中
// ctx 主要是和当前环境以及用户相关的内容,如当前发指令用户,当前群组信息等
// msg 为原生态的指令内容,如指令文本,发送平台,发送时间等
// cmdArgs 为指令信息,会将用户发的信息进行分段,方便快速取用
cmdSeal.solve = (ctx, msg, cmdArgs) => {
  // 获取第一个参数,例如 .seal A B C
  // 这里 cmdArgs.getArgN(1) 的结果即是A,传参为2的话结果是B
  let val = cmdArgs.getArgN(1);
  switch (val) {
    case 'help': {
      // 命令为 .seal help
      // 创建一个结果对象,并将showHelp标记为true,这会自动给用户发送帮助
      const ret = seal.ext.newCmdExecuteResult(true);
      ret.showHelp = true;
      return ret;
    }
    default: {
      // 命令为 .seal XXXX,取第一个参数为名字
      if (!val) val = '氪豹';
      // 进行回复,如果是群聊发送那么在群里回复,私聊发送则在私聊回复(听起来是废话文学,但详细区别见暗骰例子)
      seal.replyToSender(ctx, msg, `你抓到一只海豹!取名为${val}\n它的逃跑意愿为${Math.ceil(Math.random() * 100)}`);
      return seal.ext.newCmdExecuteResult(true);
    }
  }
}

// 将命令注册到扩展中
ext.cmdMap['seal'] = cmdSeal;

// 无实际意义,用于消除编译报错
export { }

这就是最基本的模板了。

5.进行随机

由于木落没有在示例中写这部分,我也不太清楚是不是这里有什么接口可以调用,如果后面有的话那就再补上。

这里就先摆一个随机整数的生成函数吧:

/**
 * 生成随机整数
 * @param min 最小值
 * @param max 最大值
 * @returns 位于[min,max]区间的随机整数
 */
function randomInt(min, max) {
  return Math.floor(Math.random() * (max - min + 1)) + min;
}

6.抽取牌堆

原来还可以抽取牌堆吗?但目前开放的API里面找不到这个API,等待木落烤饼吧。

7.存取数据

相关的API是两个函数,ExtInfo.storageSet(key,value)函数和 ExtInfo.storageGet(key),一个存,一个取。

关于key:

存储时需要指定key,你可以设定为你的扩展的名字,也可以设定为其他的,注意不要和别的扩展的key重名就可以了。

就好比你在商场门口想要把随身物品存进暂存柜中,需要先找到个和别人不重复的柜子,避免放错地方或者取错东西。

关于value:

存放的数据是字符串类型,且只能存一个,但如果想要存放更多的数据以及非字符串类型的数据怎么办?

答案是使用 JSON.stringify()函数将存储了数据的JS对象转换为JSON字符串,存储起来,需要取出的时候,再使用 JSON.parse()函数将数据再转换为JS对象。

示例代码:投喂插件
// ==UserScript==
// @name         示例:存储数据
// @author       木落
// @version      1.0.0
// @description  投喂,格式 .投喂 <物品>
// @timestamp    1672423909
// 2022-12-31
// @license      Apache-2
// @homepageURL  https://github.com/sealdice/javascript
// ==/UserScript==

// 先将扩展模块创建出来,如果已创建就直接使用
let ext = seal.ext.find('test');
if (!ext) {
  ext = seal.ext.new('test', '木落', '1.0.0');
  seal.ext.register(ext);
}
const cmdFeed = seal.ext.newCmdItemInfo();
cmdFeed.name = '投喂';
cmdFeed.help = '投喂,格式: .投喂 <物品>\n.投喂 记录 // 查看记录';
cmdFeed.solve = (ctx, msg, cmdArgs) => {
  let val = cmdArgs.getArgN(1);
  switch (val) {
    case 'help':
    case '': {
      // .投喂 help
      const ret = seal.ext.newCmdExecuteResult(true);
      ret.showHelp = true;
      return ret;
    }
    case '列表':
    case '记录':
    case 'list': {
      const data = JSON.parse(ext.storageGet('feedInfo') || '{}');
      const lst = [];
      for (let [k, v] of Object.entries(data)) {
        lst.push(`- ${k}: 数量 ${v}`);
      }
      seal.replyToSender(ctx, msg, `投喂记录:\n${lst.join('\n')}`);
      return seal.ext.newCmdExecuteResult(true);
    }
    default: {
      const data = JSON.parse(ext.storageGet('feedInfo') || '{}');
      const name = val || '空气';
      if (data[name] === undefined) {
        data[name] = 0;
      }
      data[name] += 1;
      ext.storageSet('feedInfo', JSON.stringify(data));
      seal.replyToSender(ctx, msg, `你给海豹投喂了${name},要爱护动物!`);
      return seal.ext.newCmdExecuteResult(true);
    }
  }
};
// 将命令注册到扩展中
ext.cmdMap['投喂'] = cmdFeed;
ext.cmdMap['feed'] = cmdFeed;
示例代码:群内安价收集

这是关于数据的增加、删除、查询等操作的实现示例(修改的话就是删除之后增加)

// ==UserScript==
// @name         群内安价收集
// @author       憧憬少
// @version      1.0.0
// @description  在群内收集群友给出的安价选项,并掷骰得出结果
// @timestamp    1676386517
// 2023-02-14 22:55:17
// @license      MIT
// @homepageURL  https://github.com/ChangingSelf/sealdice-js-ext-anchor
// ==/UserScript==

(() => {
  // src/index.ts
  const HELP = `群内安价收集(ak是アンカー罗马字缩写)
注意ak后面有空格,“.ak”也可以换成“.安价”

.ak help //查看帮助
.ak # 标题 //新建一轮分歧并设标题
.ak + 选项 //需要添加的选项的内容
.ak - 序号 //需要移除的选项的序号
.ak ? //列出目前所有选项
.ak = //随机抽取1个选项并继续
.ak = n //随机抽取n个选项并继续
`;
  const STORAGE_KEY = "anchor";
  const OPTION_NUM_PER_PAGE = 15;//列出所有选项时,每页放多少个选项
  function akNew(ctx, msg, ext, title) {
    const data = {
      "title": title,
      "options": []
    };
    ext.storageSet(STORAGE_KEY, JSON.stringify(data));
    seal.replyToSender(ctx, msg, `已新建分歧:${title}`);
  }
  function akAdd(ctx, msg, ext, option) {
    const data = JSON.parse(ext.storageGet(STORAGE_KEY) || '{"title":"","options":[]}');
    data.options.push(option);
    seal.replyToSender(ctx, msg, `当前分歧:${data.title}
已添加第${data.options.length}个选项:${option}`);
    ext.storageSet(STORAGE_KEY, JSON.stringify(data));
  }
  function akDel(ctx, msg, ext, index) {
    const data = JSON.parse(ext.storageGet(STORAGE_KEY) || '{"title":"","options":[]}');
    const removed = data.options.splice(index - 1, 1)[0];
    seal.replyToSender(ctx, msg, `当前分歧:${data.title}
已移除第${index}个选项:${removed}`);
    ext.storageSet(STORAGE_KEY, JSON.stringify(data));
  }
  function akList(ctx, msg, ext) {
    const data = JSON.parse(ext.storageGet(STORAGE_KEY) || '{"title":"","options":[]}');
    if (data.options.length === 0) {
      seal.replyToSender(ctx, msg, `当前分歧:${data.title}
还没有任何选项呢`);
      return;
    }
    let optStr = "";
    let curPageRows = 0;
    data.options.forEach((value, index) => {
      optStr += `${index + 1}.${value}
`;
      ++curPageRows;
      if (curPageRows >= OPTION_NUM_PER_PAGE) {
        seal.replyToSender(ctx, msg, `当前分歧:${data.title}
${optStr}`);
        optStr = "";
        curPageRows = 0;
      }
    });
    if (curPageRows > 0) {
      seal.replyToSender(ctx, msg, `当前分歧:${data.title}
${optStr}`);
    }
  }
  function randomInt(min, max) {
    return Math.floor(Math.random() * (max - min + 1)) + min;
  }
  function akGet(ctx, msg, ext, num = 1) {
    const data = JSON.parse(ext.storageGet(STORAGE_KEY) || '{"title":"","options":[]}');
    if (data.options.length === 0) {
      seal.replyToSender(ctx, msg, `当前分歧:${data.title}
还没有任何选项呢`);
      return;
    }
    akList(ctx, msg, ext);
    let optStr = "";
    for (let i = 0; i < num; ++i) {
      const r = randomInt(1, data.options.length);
      const result = data.options.splice(r - 1, 1);
      optStr += `${i + 1}.${result}
`;
    }
    seal.replyToSender(ctx, msg, `结果是:
${optStr}`);
    ext.storageSet(STORAGE_KEY, JSON.stringify(data));
  }
  function main() {
    let ext = seal.ext.find("anchor");
    if (!ext) {
      ext = seal.ext.new("anchor", "憧憬少", "1.0.0");
      seal.ext.register(ext);
    }
    const cmdSeal = seal.ext.newCmdItemInfo();
    cmdSeal.name = "安价";
    cmdSeal.help = HELP;
    cmdSeal.solve = (ctx, msg, cmdArgs) => {
      try {
        let val = cmdArgs.getArgN(1);
        switch (val) {
          case "#": {
            const title = cmdArgs.getArgN(2);
            akNew(ctx, msg, ext, title);
            return seal.ext.newCmdExecuteResult(true);
          }
          case "+": {
            const option = cmdArgs.getArgN(2);
            akAdd(ctx, msg, ext, option);
            return seal.ext.newCmdExecuteResult(true);
          }
          case "-": {
            const index = Number(cmdArgs.getArgN(2));
            akDel(ctx, msg, ext, index);
            return seal.ext.newCmdExecuteResult(true);
          }
          case "?":
          case "?": {
            akList(ctx, msg, ext);
            return seal.ext.newCmdExecuteResult(true);
          }
          case "=": {
            let num = 1;
            if (cmdArgs.args.length >= 2) {
              num = Number(cmdArgs.getArgN(2));
            }
            akGet(ctx, msg, ext, num);
            return seal.ext.newCmdExecuteResult(true);
          }
          case "help":
          default: {
            const ret = seal.ext.newCmdExecuteResult(true);
            ret.showHelp = true;
            return ret;
          }
        }
      } catch (error) {
        seal.replyToSender(ctx, msg, error.Message);
        return seal.ext.newCmdExecuteResult(true);
      }
    };
    ext.cmdMap["安价"] = cmdSeal;
    ext.cmdMap["ak"] = cmdSeal;
  }
  main();
})();
模板

关于取出数据来修改的函数,可以参考如下代码:

const STORAGE_KEY = "anchor";//将你的key抽出来单独作为一个常量,方便开发阶段修改(使用了之后就不要修改了)
//函数:添加选项
function akAdd(ctx, msg, ext, option) {
    //取出数据
    const data = JSON.parse(ext.storageGet(STORAGE_KEY) || '{"title":"","options":[]}');

    //处理数据
    data.options.push(option);

    //响应发送者
    seal.replyToSender(ctx, msg, `当前分歧:${data.title}\n已添加第${data.options.length}个选项:${option}`);

    //将处理完的数据写回去
    ext.storageSet(STORAGE_KEY, JSON.stringify(data));
}

8.读取玩家或群组数据

相关API比较多,还是等木落佬写示例代码吧。或者查看下文的API

9.编写暗骰指令

如下:

// ==UserScript==
// @name         示例:编写暗骰指令
// @author       流溪
// @version      1.0.0
// @description  暗骰,格式.hide 原因
// @timestamp    1671540835
// 2022-12-20
// @license      Apache-2
// @homepageURL  https://github.com/sealdice/javascript
// ==/UserScript==
ext = seal.ext.find('hide');
if (!ext){
    ext = seal.ext.new('hide','流溪','0.0.1');
    seal.ext.register(ext);
}
const cmdHide = seal.ext.newCmdItemInfo;
cmdHide.name = 'hide';
cmdHide.help = '暗骰,使用 .hide 面数 暗骰';
cmdHide.solve = (ctx, msg, cmdArgs) => {
    if (msg.messageType !== 'group'){
        seal.replyToSender(ctx, msg, '暗骰只能在群内触发');
        return seal.ext.newCmdExecuteResult(true);
    }
    function rd(x){
        //这里写的时候有点不清醒了,感觉是对的,如果不对请拷打我
        return Math.round(Math.random() * (x - 1) + 1);
    }
    let x = cmdArgs.getArgN(1);
    if (x === 'help'){
        return seal.ext.newCmdExecuteResult(true).showhelp = true;
    } else if (isNaN(Number(x))){
        //我知道这里有更好的判断是否为数字的方法但是我不会.jpg
        seal.replyToSender(ctx, msg, `骰子面数应是数字`);
        return seal.ext.newCmdExecuteResult(true);
    } else {
        //这就是暗骰api哒!
        seal.replyPerson(ctx, msg, `你在群${msg.groupId}的掷骰结果为:${rd(x)}`);
        return seal.ext.newCmdExecuteResult(true);
    }
}
ext.cmdMap['hide'] = cmdHide;

可以看到使用 seal.replyPerson做到暗骰的效果。

10.编写代骰指令

// ==UserScript==
// @name         示例:编写代骰指令
// @author       木落
// @version      1.0.0
// @description  捕捉某人,格式.catch <@名字>
// @timestamp    1671540835
// 2022-12-20
// @license      Apache-2
// @homepageURL  https://github.com/sealdice/javascript
// ==/UserScript==

// 编写代骰指令
// 先将扩展模块创建出来,如果已创建就直接使用
let ext = seal.ext.find('test');
if (!ext) {
  ext = seal.ext.new('test', '木落', '1.0.0');
  seal.ext.register(ext);
}
// 创建指令 .catch
// 这个命令的功能为,显示“试图捕捉某人”,并给出成功率
// 如果命令写“.catch @张三”,那么就会试着捕捉张三
const cmdCatch = seal.ext.newCmdItemInfo();
cmdCatch.name = 'catch';
cmdCatch.help = '捕捉某人,格式.catch <@名字>';
// 对这个指令启用使用代骰功能,即@某人时,可获取对方的数据,以对方身份进行骰点
cmdCatch.allowDelegate = true;
cmdCatch.solve = (ctx, msg, cmdArgs) => {
  // 获取对方数据,之后用mctx替代ctx,mctx下读出的数据即被代骰者的个人数据
  const mctx = seal.getCtxProxyFirst(ctx, msg);
  let val = cmdArgs.getArgN(1);
  switch (val) {
    case 'help': {
      // 命令为 .catch help
      const ret = seal.ext.newCmdExecuteResult(true);
      ret.showHelp = true;
      return ret;
    }
    default: {
      const text = `正在试图捕捉${mctx.player.name},成功率为${Math.ceil(Math.random() * 100)}%`;
      seal.replyToSender(mctx, msg, text);
      return seal.ext.newCmdExecuteResult(true);
    }
  }
};
// 将命令注册到扩展中
ext.cmdMap['catch'] = cmdCatch;

11.网络请求

主要使用Fetch API进行网络请求,详细文档见链接。

// 你可以使用async/await和generator来重写这段代码,欢迎pr
// 访问网址
fetch('https://api-music.imsyy.top/cloudsearch?keywords=稻香').then((resp) => {
  // 在返回对象的基础上,将文本流作为json解析
  resp.json().then((data) => {
    // 打印解析出的数据
    console.log(JSON.stringify(data));
  });
});

套用这个模板,你可以写出很多调用API的简单扩展。

比如核心代码只有一行的“随机猫猫图片”扩展:

seal.replyToSender(ctx, msg, `[CQ:image,file=https://thiscatdoesnotexist.com/,cache=0]`);

核心代码同样只有一行的“随机二次元图片”扩展:

seal.replyToSender(ctx, msg, `[CQ:image,file=https://api.oick.cn/random/api.php?type=${val},cache=0]`);

当然,也有稍微复杂的,比如“AI骰娘”扩展,但也没有太复杂,只是处理了一下发送者传过来的消息,再发送给网络API,收到响应之后再回应发送者。

它的核心代码如下:

const BID = ''; //填入你的骰娘的大脑的id
const KEY = ''; //填入你的key
/**
 * 给AI主脑发送消息并接收回复
 * @param ctx 主要是和当前环境以及用户相关的内容,如当前发指令用户,当前群组信息等
 * @param msg 为原生态的指令内容,如指令文本,发送平台,发送时间等
 * @param message 要发送给骰娘的具体消息
 */
function chatWithBot(ctx,msg,message) {
    fetch(`http://api.brainshop.ai/get?bid=${BID}&key=${KEY}&uid=${msg.sender.userId}&msg=${message}`).then(response => {
      if (!response.ok) {
        seal.replyToSender(ctx, msg, `抱歉,我连接不上主脑了。它传递过来的信息是:${response.status}`);
        return seal.ext.newCmdExecuteResult(false);
      } else {
        response.json().then(data => {
          seal.replyToSender(ctx, msg, data["cnt"]);
          return seal.ext.newCmdExecuteResult(true);
        });
        return seal.ext.newCmdExecuteResult(true);
      }
    });
}

12.自定义COC规则

// ==UserScript==
// @name         示例:自定义COC规则
// @author       木落
// @version      1.0.0
// @description  自设规则,出1大成功,出100大失败。困难极难等保持原样
// @timestamp    1671886435
// 2022-12-24
// @license      Apache-2
// @homepageURL  https://github.com/sealdice/javascript
// ==/UserScript==

const rule = seal.coc.newRule();
rule.index = 20; // 自定义序号必须大于等于20,代表可用.setcoc 20切换
rule.key = '测试'; // 代表可用 .setcoc 测试 切换
rule.name = '自设规则'; // 已切换至规则文本 name: desc
rule.desc = '出1大成功\n出100大失败';
// d100 为出目,checkValue 为技能点数
rule.check = (ctx, d100, checkValue) => {
  let successRank = 0;
  const criticalSuccessValue = 1;
  const fumbleValue = 100;
  if (d100 <= checkValue) {
    successRank = 1;
  } else {
    successRank = -1;
  }
  // 成功判定
  if (successRank == 1) {
    // 区分大成功、困难成功、极难成功等
    if (d100 <= checkValue / 2) {
      //suffix = "成功(困难)"
      successRank = 2;
    }
    if (d100 <= checkValue / 5) {
      //suffix = "成功(极难)"
      successRank = 3;
    }
    if (d100 <= criticalSuccessValue) {
      //suffix = "大成功!"
      successRank = 4;
    }
  } else {
    if (d100 >= fumbleValue) {
      //suffix = "大失败!"
      successRank = -2;
    }
  }
  let ret = seal.coc.newRuleCheckResult();
  ret.successRank = successRank;
  ret.criticalSuccessValue = criticalSuccessValue;
  return ret;
};
// 注册规则
seal.coc.registerRule(rule);

使用TS模板

clone或下载项目

推荐的流程:

  1. 模板项目仓库点击“Use this template”按钮,使用该模板在自己的github上创建一个扩展的仓库,并设置为自己的扩展的名字
  2. git clone到本地,进行开发

如果你没有github账号,也不会用git:

  1. 模板项目仓库点击“Code”按钮,在出现的浮窗中选择“Download ZIP”,这样就会下载一个压缩包
  2. 解压后进行开发
编译项目

NPM是一种javascript的包管理工具,可以管理项目使用的依赖包。

一开始,需要先将所需依赖包安装好,在命令行使用如下命令:

npm install

当你写好了代码,需要将ts文件转换为js文件以便上传到海豹骰时,在命令行使用如下命令:

npm run build

编译成功的js文件在dist目录下,默认的名字是 sealdce-js-ext.js

填写个人信息

当插件开发完成后(或者开始开发时),你需要修改几处地方:

  • header.txt 这个文件是你插件的描述信息
  • tools/build-config.js 最开头一行"var filename = 'sealdce-js-ext.js';",改成你中意的名字,注意不要与现有的重名
目录结构

只列出其中主要的一些文件

  • src
    • index.ts:你的扩展的代码就写在这个文件里
  • tools
    • build-config:一些编译的配置,影响 index.ts编译成js文件的方式
    • build.js:在命令 npm run build执行时所运行的脚本,用于读取 build-config并按照配置进行编译
  • types
    • seal.d.ts:类型文件,海豹核心提供的扩展API
  • header.txt:扩展头信息,会在编译时自动加到目标文件头部
  • package.json:命令 npm install时就在安装这个文件里面所指示的依赖包
  • tsconfig.json:typescript的配置

补充:使用非指令关键词

你是否因为自定义回复能实现的功能有限而烦恼?你是否因为自定义回复的匹配方式不全而愤怒?你是否因为自定义回复只能调用图片api而感到焦头烂额?不要紧张,我的朋友,试试非指令关键词,这会非常有用。

通常情况下,我们使用 ext.onNotCommandReceived作为非指令关键词的标志;这限定了只有在未收到命令且未达成自定义回复时,海豹才会触发此流程。

一个完整的非指令关键词模板如下:

//必要流程,注册扩展,注意即使是非指令关键词也是依附于扩展的  
if (!seal.ext.find('xxx')){  
    ext = seal.ext.new('xxx','xxx','x.x.x');  
    seal.ext.register(ext); 
    //这里其实是编写处理函数   
    ext.onNotCommandReceived = (ctx, msg) => {  
        let message = msg.message;  
        //这里请自己处理要如何达成message的匹配条件,js那么多的匹配方法,足够你玩出花来。  
        if(xxx){
          //匹配到关键词了,要干什么?  
          xxx;
        }
    }
}

JS扩展API

这里只是粗略的整理,具体请看jsvm源码

按类别整理。

其中ctx为信息的MsgContext,msg为信息的Message,一般会在定义指令函数时就声明,如:

cmd.solve = (ctx, msg, cmdArgs) => {
    someFunction;
} 

下面是api的说明(完全了吧......应该?):

//被注释掉的api是可以提供的,但是在源码中被注释。  
//seal.setVarInt(ctx, `$XXX`, valueToSet) //`$XXX`即rollvm(初阶豹语)中的变量,其会将$XXX的值设定为int类型的valueToSet。  
//seal.setVarStr(ctx, `$XXX`, valueToSet) //同上,区别是设定的为str类型的valueToSet。  
seal.replyGroup(ctx, msg, something) //向收到指令的群中发送something。  
seal.replyPerson(ctx, msg, something) //顾名思义,类似暗骰,向指令触发者(若为好友)私信something。  
seal.replyToSender(ctx, msg, something) //同上,区别是群内收到就群内发送,私聊收到就私聊发送。  
seal.memberBan(ctx, groupID, userID, dur) //将指定群的指定用户封禁指定时间(似乎只实现了walleq协议?)
seal.memberKick(ctx, groupID, userID)  //将指定群的指定用户踢出(似乎也只实现了walleq协议?)
seal.format(ctx, something) //将something经过一层rollvm转译并返回,注意需要配合replyToSender才能发送给触发者!  
seal.formatTmpl(ctx, something) //调用自定义文案something  
seal.getCtxProxyFirst(ctx, cmdArgs)  //获取被at的第一个人, 等价于getCtxProxyAtPos(ctx, 0)  
seal.vars.intGet(ctx, `$XXX`) //返回一个数组,其为[int类型的触发者的该变量的值,bool]当strGet一个int或intGet一个str时bool为false,若一切正常则为true。(之所以会有这么奇怪的说法是因为rollvm的“个人变量”机制)。  
seal.vars.intSet(ctx, `$XXX`, valueToSet) //`$XXX`即rollvm(初阶豹语)中的变量,其会将$XXX的值设定为int类型的valueToSet。  
seal.vars.strGet(ctx, `$XXX`) //返回一个数组,其为[str类型的触发者的该变量的值,bool](之所以会有这么奇怪的说法是因为rollvm的“个人变量”机制),当strGet一个int或intGet一个str时bool为false,如果一切正常则为true。  
seal.vars.strSet(ctx, `$XXX`, valueToSet) //`$XXX`即rollvm(初阶豹语)中的变量,其会将$XXX的值设定为str类型的valueToSet。  
//seal.vars.varSet(ctx, `$XXX`, valueToSet) //可能是根据数据类型自动推断int或str?
//seal.vars.varGet(ctx, `$XXX`) //同上
seal.ext.newCmdItemInfo() //用来定义新的指令;没有参数,个人觉得可以视其为类(class)。  
seal.ext.newCmdExecuteResult(bool) //用于判断指令执行结果,true为成功,false为失败。  
seal.ext.new(extName, extAuthor, Version) //用于建立一个名为extName,作者为extAuthor,版本为Version的扩展。注意,extName, extAuthor和Version均为字符串。  
seal.ext.find(extName) //用于查找名为extname的扩展,若存在则返回true,否则返回false。  
seal.ext.register(newExt) //将扩展newExt注册到系统中。注意newExt是seal.ext.new的返回值,将register视为seal.ext.new()是错误的。  
seal.coc.newRule() //用来创建自定义coc规则,github.com/sealdice/javascript/examples中已有详细例子,不多赘述。  
seal.coc.newRuleCheckResult() //同上,不多赘述。  
seal.coc.registerRule(rule) //同上,不多赘述。  
seal.deck.draw(ctx, deckname, isShuffle) //他会返回一个抽取牌堆的结果。这里有些复杂:deckname为需要抽取的牌堆名,而isShuffle则是一个布尔值,它决定是否放回抽取;false为放回,true为不放回。  
seal.deck.reload() //重新加载牌堆。  
//下面是1.2新增api  
seal.newMessage() //返回一个空白的Message对象, 结构与收到消息的msg相同
seal.createTempCtx(endpoint, msg) // 制作一个ctx, 需要msg.MessageType和msg.Sender.UserId
seal.applyPlayerGroupCardByTemplate(ctx, tmpl) // 设定当前ctx玩家的自动名片格式
seal.gameSystem.newTemplate(string) //从json解析新的游戏规则。  
seal.gameSystem.newTemplateByYaml(string) //从yaml解析新的游戏规则。 
seal.getCtxProxyAtPos(ctx, pos) //获取第pos个被at的人, pos从0开始计数 
seal.atob(base64String) //返回被解码的base64编码  
seal.btoa(string) //将string编码为base64并返回
部分api使用示例

声明和注册扩展的代码部分已省略。

1: replyGroup, replyPerson, replyToSender:
//在私聊触发replyGroup不会回复
seal.replyGroup(ctx, msg, 'something'); //触发者会收到"something"的回复
seal.replyPerson(ctx, msg, 'something'); //触发者会收到"something"的私聊回复
seal.replyToSender(ctx, msg, 'something'); //触发者会收到"something"的回复
2: memberBan, memberKick

是否保留待议

//注意这些似乎只能在WQ协议上实现;
seal.memberBan(ctx, groupID, userID, dur) //将群为groupID,userid为userID的人封禁dur(单位未知)
seal.memberKick(ctx, groupID, userID) ////将群为groupID,userid为userID的人踢出那个群
3: format, formatTmpl
//注意format不会自动reply,而是return,所以请套一层reply
seal.replyToSender(ctx, msg, seal.format(`{$t玩家}的人品为:{$t人品}`))
//{$t人品}是一个rollvm变量,其值等于.jrrp出的数值
//回复:
//群主的人品为:87
seal.replyToSender(ctx, msg, seal.formatTmpl(unknown))
//这里等大佬来了再研究
4: getCtxProxyFirst, getCtxProxyAtPos
cmd.solve = (ctx, msg, cmdArgs) => {
    let ctxFirst = seal.getCtxProxyFirst(ctx, cmdArgs)
    seal.replyToSender(ctx, msg, ctxFirst.player,name)
}
ext.cmdMap['test'] = cmd
//输入:.test @A @B
//返回:A的名称。这里其实获取的是A玩家的ctx,具体见文末的ctx数据结构。
cmd.solve = (ctx, msg, cmdArgs) => {
    let ctx3 = seal.getCtxProxyAtPos(ctx, 3)
    seal.replyToSender(ctx, msg, ctx3.player,name)
}
ext.cmdMap['test'] = cmd
//输入:.test @A @B @C
//返回:C(第三个被@的人)的名称。这里其实获取的是C玩家的ctx,具体见文末的ctx数据结构。
5: vars
//要看懂这里你可能需要学习一下初阶豹语
seal.vars.intSet(ctx, `$m今日打胶次数` 8) //将触发者的该个人变量设置为8
seal.vars.intGet(ctx, `$m今日打胶次数`) //返回 [8,true]
seal.vars.strSet(ctx, `$g群友发癫语录`, `一条也没有,快来发癫吧`) //将群内的该群组变量设置为“一条也没有,快来发癫吧!”
seal.vars.strGet(ctx, `$g群友发癫语录`) //返回 ["一条也没有,快来发癫吧",true]
6: ext
//用于注册扩展和定义指令的api,已有详细示例,不多赘述
7: coc
//用于创建coc村规的api,已有详细示例,不多赘述
8: deck
seal.deck.draw(ctx, `煤气灯`, false) //返回 放回抽取牌堆“煤气灯”的结果
seal.deck.draw(ctx, `煤气灯`, true) //返回 不放回抽取牌堆“煤气灯”的结果
seal.deck.reload() //重新加载牌堆
9: 自定义trpg规则相关
//这里实在不知道如何举例了
seal.gameSystem.newTemplate(string) //从json解析新的游戏规则。  
seal.gameSystem.newTemplateByYaml(string) //从yaml解析新的游戏规则。
seal.applyPlayerGroupCardByTemplate(ctx, tmpl) // 设定当前ctx玩家的自动名片格式
10: 其他
seal.newMessage() //返回一个空白的Message对象, 结构与收到消息的msg相同
seal.createTempCtx(endpoint, msg) // 制作一个ctx, 需要msg.MessageType和msg.Sender.UserId
seal.atob(base64String) //返回被解码的base64编码  
seal.btoa(string) //将string编码为base64并返回
seal.getEndPoints() //返回骰子(应该?)的EndPoints
seal.getVersion() //返回一个map,键值为version和versionCode
ctx 的内容
//在github.com/sealdice/javascript/examples_ts/seal.d.ts中有完整内容
// 成员
ctx.group // 当前群信息(对象)
ctx.player // 当前玩家数据(对象)
ctx.endPoint // 接入点数据(对象)
// 以上三个对象内容暂略
ctx.isCurGroupBotOn // bool 
ctx.isPrivate // bool 是否私聊
ctx.privilegeLevel // int 权限等级 40邀请者 50管理 60群主 70信任 100master
ctx.delegateText // string 代骰附加文本
// 方法 (太长,懒.)
chBindCur
chBindCurGet
chBindGet
chBindGetList
chExists
chGet
chLoad
chNew
chUnbind
chUnbindCur
chVarsClear
chVarsGet
chVarsNumGet
chVarsUpdateTime
loadGroupVars
loadPlayerGlobalVars
loadPlayerGroupVars,notice
ctx.group 的内容
// 成员
active
groupId
guildId
groupName
cocRuleIndex
logCurName
logOn
recentDiceSendTime
showGroupWelcome
groupWelcomeMessage
enteredTime
inviteUserId
// 方法
extActive
extClear
extGetActive
extInactive
extInactiveByName
getCharTemplate
isActive
playerGet
ctx.player 的内容
// 成员
name
userId
lastCommandTime
autoSetNameTemplate
// 方法
getValueNameByAlias
ctx.endPoint 的内容
// 成员
baseInfo
id
nickname
state
userId
groupNum
cmdExecutedNum
cmdExecutedLastTime
onlineTotalTime
platform
enable
// 方法
adapterSetup
refreshGroupNum
setEnable
unmarshalYAML
msg 的内容
// 成员
msg.time // int64 发送时间
msg.messageType // string group群聊 private私聊
msg.groupId // string 如果是群聊, 群号
msg.guildId // string 服务器群组号,会在discord,kook,dodo等平台见到
msg.sender // 发送者信息(对象)
    sender.nickname
    sender.userId
msg.message
msg.rawId // 原始信息ID, 用于撤回等
msg.platform // 平台
// 方法
// (似乎目前没有?)
cmdArgs 的内容
// 成员
.command // string
.args // []string
.kwargs // []Kwarg
.at // []AtInfo
.rawArgs // string
.amIBeMentioned // bool (为何要加一个Be?)
.cleanArgs // string 一种格式化后的参数,也就是中间所有分隔符都用一个空格替代
.specialExecuteTimes // 特殊的执行次数,对应 3# 这种
// 方法
.isArgEqual(n, ss...) // 返回bool, 检查第n个参数是否在ss中
.eatPrefixWith(ss...) // 似乎是从cleanArgs中去除ss中第一个匹配的前缀
.chopPrefixToArgsWith(ss...) // 不懂
.getArgN(n) // -> string
.getKwarg(str) // -> Kwarg 如果有名为str的flag,返回对象,否则返回null/undefined(不确定)
.getRestArgsFrom(n) // -> string 获取从第n个参数之后的所有参数, 用空格拼接成一个字符串
ChangeLog
版本 日期 作者 内容
v1.2 2023-04-08 JohNSoN 补充seal对象的两个方法
v1.1 2023-04-08 JohNSoN 补充部分内容
v1.0 2023-04-08 不学会go不改名(划掉)流溪 为社区贡献者欢呼的初始发布!

服务器管理

Windows间迁移

来到海豹目录,右键添加为压缩包,带走。

Windows往Linux迁移

来到海豹目录,右键data文件夹,添加为zip压缩包。

到Linux服务器上事先准备好的海豹程序文件夹,执行:

unzip ./data.zip

将其解压,然后运行海豹核心

./sealdice-core

Linux服务器间跨平台迁移(arm64->amd64)

首先在你的旧服务器上,关闭海豹进程,随后来到海豹的工作目录,执行

tar -zcvf ./data.tar ./data

将data目录打包。

image-20220319011719014

现在你多拥有了一个data.tar,复制到新的服务器,预先准备好的海豹程序目录,执行这条命令解压:

tar --no-same-owner -zxvf data.tar

然后启动海豹即可。

./sealdice-core

如果文件权限不正确,可以在目录下执行这两条:

find . -type f | xargs chmod 644
find . -type d | xargs chmod 755

特色功能

指令兼容性

兼容了溯洄骰、赵骰和塔骰的绝大部分主流指令,无论之前用过哪一个都可以轻松上手。

对格式不敏感,你可以 .r1d20 +1.r d20+1.stshow力量 敏捷。st hp - 1等等各种写法。

易于部署和使用

海豹的设计初衷就是“能被任何TRPG玩家喜爱和使用”,同时秉持”没有提供UI的功能等于不存在“的理念。

只需一个15MB的安装包、一个空闲的QQ号,和5分钟时间,任何人都可以搭建一个QQ骰子。

极少的资源占用

由于Golang的高效,海豹核心只占用大概40MB内存,以及极少的CPU资源。

自带的《COC7怪物之锤》和《DND系列资料》会另外占用50-70MB内存,但能提供极为强大的资料查询功能。

因此总计的初始内存大概在90MB左右。其中有一部分其实是空闲内存,因为go运行时的特性并不立即还给操作系统。

强自定义

提供了友好的GUI界面,可以自定义大部分指令的回复语句。

同时对于自定义回复(即reply)功能进行了强化,可以支持一些逻辑分支。

人工智能鸽子

让您想咕就咕

.gugu
🕊️: <木落>在来开团的路上被巨龙叼走了!

查看鸽子背后的故事

.gugu 来源
🕊️: 前往了一个以前捕鲸的小岛度假~这里人很亲切!但是吃了这里的鱼肉料理之后有点晕晕的诶...想到前几天<木落>的短信,还是别追究他为什么不在了。
    ——鹊鹊结合实际经历创作

强大的查询功能

海豹搭载了全文搜索技术(仅限PC版),可以看作是一个微型搜索引擎。

可以找一只海豹试下这个指令:

.查询 30尺 法术

细节可参见 [.find/.查询 查询指令](#.find/.查询 查询指令) 一节

COC特化支持

属性同义词

各种旧版翻译、俗称、简繁汉字的属性被视为同一属性。

默认技能等级

内置部分人物卡上没有的默认技能等级。

包括不限于速记、园艺、攀爬、各种武器等等。

支持 .scb 指令

用于在sc时额外摇一个奖励骰。

适用于规则书中,用关键连接自救的部分

见《COC7th守密人核心规则书》第八章 理智 - 增加当前理智值 - 4.自救

同时遇到多个目标的理智检定

指令未完成

DND特化支持

属性同义词

功能同COC,但是使用不同的关键字

人物卡录入

参见[.st DND属性设置](#.st DND属性设置)

优势骰劣势骰

.set 20
.r d20优势 - d劣势

d20优势 = 2d20k1,即2个d20取结果高的一个

d20劣势 = 2d20q1,即2个d20取结果低的一个

属性调整值和熟练自动计算

临时属性

参见 [.buff 临时属性](#.buff 临时属性)

法术位支持

参见 [.ss 法术位(spell slots)](#.ss 法术位(spell slots))

死亡豁免

参见死亡豁免

休息

参见 [.longrest / .长休](#.longrest / .长休)

通用特化支持

对他人做检定 / 属性操作

功能描述见 对他人做检定,同理,可以修改他人的属性。

支持COC的.ra / .rc 指令,DND的除了先攻之外的全部指令

小队 / 批量操作

未完成 但是有热心的社区开发者们制作了类似的插件,你可以在js仓库或用户群中找到更多

可以实时编辑的跑团日志

https://log.sealdice.com/

除了海豹log自用之外,支持QQ格式和塔骰格式。

全选删除现有文本后,全文粘贴到文本框里即可。

是纯净骰,也是娱乐骰

海豹除了核心指令之外,都被设计为可插拔的,目前有7个内置扩展。

关闭对应扩展会连同扩展带来的指令一块关闭。

因此在跑团时,如果你不想要任何娱乐功能,你可以(以COC团为例):

.ext fun off // 关闭.jrrp和.gugu
.ext reply off // 关闭关键字回复
.ext deck off // 关闭抽牌 .draw
.ext dnd5e off // 关闭dnd5e系列指令
.ext story off // 关闭.name[随机指令]/.modu[魔都查询]/.who[排序]等

也可以写成一行:
.ext fun reply deck dnd5e story off

这样一来,就只剩下coc系列指令和log指令了。

这个改动在当前群持续生效,如果想回去可以这样再打开

.ext fun on

请注意!默认情况下是所有扩展全部开启的,且coc7优先级高于dnd5e。

但这取决于骰主的个人喜好,骰主可以设置刚进群的时候默认开启的扩展。

因此遇到不认识指令你的骰,可以先用.ext看一眼情况。

更强的指令支持

海豹核心将表达式翻译成字节码,丢进指令虚拟机里执行,同时也做了很强的兼容性处理。

可以在不同骰系下测试一下这些指令:

.r 30 + (-1d20) + 49
.r d20 + (3 + 1d4)
.r 1 + (3+2)d(4+2d10)

.sc 0/-1
.sc (1+2)/0
.sc 0/1d4+(3*2)
.sc 1 / 2
.sc 1/2 + 3

.ra b 测试50
.ra b 50 测试

彩蛋:

.r 1+d10-3*4 --asm
<木落>掷出了 1+d10-3*4=1 + 3[1d10=3] - 12=-8
=== VM Code ===
push 1
mark.left
push 1
mark.left
push 10
dice
add
mark.left
push 3
mark.left
push 4
mul
sub
halt
=== VM Code End===

跨平台

虽然海豹核心目前主要服务于QQ用户,并做了很多对应优化。

但其实QQ本身只是默认被实现的第一个交互端。

海豹核心的底层架构,支持单进程的多骰多号

多骰即可以执行多个骰子实例,分别拥有不同的自定义设置。好比是你的学校有3个不同的食堂。

多号即每个骰子上,可以绑定N个帐号,就好比是同一个食堂开了多个窗口,卖的东西是一样的。

这里的帐号概念并不局限于QQ,至少可以确定的是,未来会提供骰点HTTP API

2023/2/4 更新: 目前海豹已经支持 Kook(开黑啦) Discord Telegram 甚至是 Minecraft 服务器中使用! 你可以在添加账号时的账号类型里看到他们。感谢Szzrain提交的PR。以后也会支持更多的平台

TRPG规则支持列表

  • COC7
  • DND5e
  • COC - DeltaGreen (.setcoc dg)
  • 暗影狂奔 (.rsr)
  • WOD/无限团 (.ww)
  • 双十字DoubleCross (.dx)
  • 共鸣性怪异Emoklore (.ek .ekgen)
  • 剑世界2.5 (插件)
  • 明日方舟跑团规则 (插件)
  • 其它插件由海豹社区提供,欢迎前去插件仓库查看

如果你不跑这些规则,或者觉得并无必要,可以在后台关闭对应的指令。

如果你喜欢的规则这里没有,那么可以去插件仓库看一看,或者自己编写规则模板插件!

许可协议

兴趣使然之作。

使用造成的任何后果与本人无关。

进行使用即代表同意此声明:

尊敬的用户,欢迎您选择由木落等研发的海豹骰点核心(SealDice),在您使用自定义功能前,请务必仔细阅读使用须知,当您使用我们提供的服务时,即代表您已同意使用须知的内容。

您需了解,海豹核心官方版只支持TRPG功能,娱乐功能定制化请自便,和海豹无关。

您清楚并明白您对通过骰子提供的全部内容负责,包括自定义回复、非自带的插件、牌堆。海豹骰不对非自身提供以外的内容合法性负责。您不得在使用海豹骰服务时,导入包括但不限于以下情形的内容:

(1) 反对中华人民共和国宪法所确定的基本原则的;

(2) 危害国家安全,泄露国家秘密,颠覆国家政权,破坏国家统一的;

(3) 损害国家荣誉和利益的;

(4) 煽动民族仇恨、民族歧视、破坏民族团结的;

(5) 破坏国家宗教政策,宣扬邪教和封建迷信的;

(6) 散布谣言,扰乱社会秩序,破坏社会稳定的;

(7) 散布淫秽、色情、赌博、暴力、凶杀、恐怖或者教唆犯罪的;

(8) 侮辱或者诽谤他人,侵害他人合法权益的;

(9) 宣扬、教唆使用外挂、私服、病毒、恶意代码、木马及其相关内容的;

(10)侵犯他人知识产权或涉及第三方商业秘密及其他专有权利的;

(11)散布任何贬损、诋毁、恶意攻击海豹骰及开发人员、海洋馆工作人员、mod编写人员、关联合作者的;

(12)含有中华人民共和国法律、行政法规、政策、上级主管部门下发通知中所禁止的其他内容的。

一旦您有以上禁止行为,请立即停用海豹骰。

关于

木落

鹊鹊

蜜瓜包

于言诺

流溪

憧憬少

浣熊旅記

海洋馆工作人员

木落 (fy0748#gmail.com)

Yumeno(184023393)

鹊鹊(1251343516)

作者Github

https://github.com/fy0

https://github.com/sealdice // 海豹开源组织

项目源码

海豹已经在github开源,欢迎有实力或有毅力或感兴趣的用户参与贡献。 链接:github - sealdice/sealdice-core

现在加入有机会获得木落的神❤秘❤大❤礼(bushi

官方站点

http://sealdice.com/

建设中,预计提供log染色、人物卡制作、新版本发布、云人物卡、自动更新等功能。

log染色: https://log.sealdice.com/

投喂

请参见官网——投喂部分,目前尚未更新蓝绿修改器(暂时不顾上)。

https://sealdice.com/feed

你的投喂将用于支付服务器费用,

以及帮助社畜制作者们稍微抵抗来自现实的引力。

非常感谢。

用户群

主群: 524364253

分群: 562897832

分群: 715664650