17. 拓展规范文档#

17.1. 简介#

本文档仍处于草稿状态。 本文档针对未来的某个重构了拓展写法的新版本,目前(v0.5.3)应该是尚未实装, 仍在讨论可行性之中。

新月杀本身仅含有最基本的标准包,拓展是用以拓展游戏玩法的,其由一个或多个包 (package) 组成。为了使拓展的开发与维护更加便捷、统一,特制定此规范。

17.2. 拓展与包的基本结构#

拓展存在于游戏目录的 packages 目录之下,其本身是一个目录。文件结构如下:

extension
├── audio
│   ├── card
│   ├── death
│   ├── skill
│   └── win
│
├── image
│   ├── anim
│   ├── card
│   ├── generals
│   ├── mark
│   └── role
│
├── init.lua
│
└── pkg
    ├── package1
    │   ├── init.lua
    │   └── skills
    │       ├── jianxiong.lua
    │       └── ...
    │
    ├── package2
    └── ...

拓展根目录中的 init.lua 需要加载其包含的所有子扩展包。

audioimage 分别为声音和图像资源文件夹。

pkg 文件夹内为你的子扩展包文件夹。

这些文件夹拥有相对固定的名称,请不要随意更改

子扩展包可以用来拓展更多的技能、武将、卡牌、游戏模式。

如果你看过新人入门教程,这里就是你的DIY武将在开发时,所需要了解的各种文件与代码的规范。

17.2.1. audio#

audio 是你的语音文件夹,其内部包括了 death 文件夹, skill 文件夹和 card 文件夹。

警告

audio 中的语音文件,应 全部是mp3格式。

如果单纯的从其他格式以重命名的方式修改为mp3,可能会导致无法播放,请注意使用转化软件或者在线网站。

一个扩展包只能有一个 audio 文件夹与 image 文件夹,其内部可能存放多个其他文件夹。

death 文件夹内部应放置武将的【阵亡语音】,该语音的命名应与武将的代码名称相同。

skill 文件夹内部应放置武将的【技能语音】,该语音的命名应与武将技能的代码名称相同。

win 文件夹内部应放置武将的【胜利语音】,该语音的命名应与武将的代码名称相同

card 文件夹内部应放置【卡牌语音】, card 内部又分为 femalemale 文件夹。 femalemale 分别对应卡牌在使用时所播放的女性语音与男性语音。一张卡牌对应一个语音,语音的命名与卡牌的代码名称相同。

备注

如果你的武将技能有多个语音,语音的命名格式应为武将技能的代码名称+语音编号。

语音编号从1开始计算,例如testGeneral_skill1.mp3和testGeneral_skill2.mp3。

17.2.2. image#

image 是你的武将立绘与势力图片的文件夹,其内部包括了 generals 文件夹和 kingdomcard 文件夹。

警告

image 中的【武将立绘】文件,应 全部是jpg格式。

image 中的【势力图片】文件,应 全部是png格式。

image 中的【卡牌图片】文件,应 全部是png格式。

generals 文件夹内部应放置武将的【立绘】, 该立绘的命名应与武将的代码名称相同。

对于武将的立绘同时拥有其文件大小的要求,请记住武将的立绘格式应为 250x292 大小。

kingdom 文件夹内部应放置【DIY势力图片】,势力图片拥有三个部分,均为png图片。

备注

如果你仅仅需要原版三国杀的势力,则不用创建本文件夹。

../../_images/shu.png

势力的图标(格式最好在30x30-35x35之间)#

../../_images/shu-magatama.png

势力的阴阳玉(格式为10x12)#

../../_images/shu-back.png

势力的将框(格式为175x233)#

势力图标的名称应为该势力的【英文代码名称】

势力阴阳玉的名称应为图标名称+(-magatama)。例如shu-magatama.png。

势力将框的名称应为图标名称+(-back)。例如shu-back.png。

card 文件夹内部应放置【DIY卡牌图片】,其内部存放了三部分,均为png图片。

  • delayedTrick 文件夹

  • equipIcon 文件夹

  • 卡牌立绘文件.png等

delayedTrick 文件夹里面存放了DIY延时锦囊在使用后的图标。格式为 47x55 (若不需要则可以不用创建本文件夹)

equipIcon 文件夹内部存放了DIY装备的小图标(即装备栏所见的小图标)。格式为 28x22 (若不需要则可以不用创建本文件夹)

警告

delayedTrick与equipIcon图标的命名格式需要与对应的延时锦囊牌或装备牌代码名称相同。

卡牌立绘则是主要的部分,在card文件夹中放置你的diy卡牌立绘。格式为**93x130 png**文件。

17.2.3. init.lua#

init.lua 文件是你扩展包的核心文件,如果没有 init.lua ,新月杀就不会加载你的 extension/ 文件夹。

在init文件中,应包含以下的函数或语句。

  • 各个子扩展包的引用语句

  • 各个子扩展包的翻译表语句

  • 返回值一个表,其元素为各个子扩展包(Pakage对象)

完整的 init.lua 示例结构如下:

 1local prefix = "packages.extension.pkg."
 2-- 这一行定义了扩展的和子扩展包文件夹,extension 为你的扩展名,应与文件夹名相对应。
 3
 4Fk:loadTranslationTable { ["extension"] = "扩展名" }
 5-- 这一行是扩展的翻译表
 6
 7local package1 = require(prefix .. "package1")
 8local package2 = require(prefix .. "package2")
 9-- 这两行定义了子扩展包的引用,返回的为Package对象
10
11Fk:loadTranslationTable {
12  ["package1"] = "包一名",
13  ["package2"] = "包二名",
14}
15-- 这两行是子扩展包的翻译表
16
17return {
18  package1,
19  package2,
20}
21-- 这一部分是return语句,作用返回你的各个子扩展包,需要与前面相对应。
22-- 写在return里的文件会被新月杀发现然后执行

17.3. 子扩展包的结构#

首先,我们要明白一个概念,扩展包是你的个人扩展,在这个大分类中包含了你的武将子扩展包,卡牌子扩展包和模式子扩展包。

每个子扩展包目录中的 init.lua 需要加通过 extension:loadSkillSkelsByPath 定义技能搜索目录。然后其余部分定义该包含有的武将、卡牌和游戏模式等,其应返回值是extension(一个Pakage对象)。

init.lua 中通常需包含以下内容。

  • 创建一个Package对象取名为extention。(这个命名是历史遗留)

  • 定义技能搜索目录

  • 创建若干武将对象并为他们添加技能,或者添加卡牌与游戏模式

  • 为该子扩展包中武将前缀,势力,各个武将,卡牌等信息添加翻译表

每个技能应位于包的 skills/ 目录下,置于单独的Lua文件中, 其应返回一个技能骨架(SkillSkelton)对象。

这里是一个典型的子扩展包的文件目录示例。其具体结构随子扩展包类型不同而略有区别。

package1
├── init.lua
└── skills
    ├── testGeneral_skill1.lua
    └── testGeneral_skill2.lua

package1是你的扩展包【英文代码】名称,请注意,不要使用中文命名

17.4. 武将子扩展包#

一个典型的武将子扩展包的 init.lua 示例如下

 1local extension = Package:new("package1")
 2extension.extensionName = "extension"
 3-- 这一部分是武将扩展包的创建语句。
 4-- 第一行为创建子扩展包,名称为package1。这里一般与你的子扩展包文件夹名一致。
 5-- 第二行为你的扩展包名,extension。请注意,包名一般与你的扩展文件夹名一致。
 6
 7extension:loadSkillSkelsByPath("./packages/extension/pkg/package1/skills")
 8-- 这一部确定分子扩展包技能搜索路径
 9
10Fk:loadTranslationTable {
11  ["package1"] = "扩展武将包",
12  ["testKingdom"] = "扩展武将势力",
13  ["test"] = "武将前缀",
14}
15
16General:new(extension, "test__General1", "testKingdom", 3, 4, General.Male):addSkills { "test__General_skill1", "test__General_skill2" }
17-- 这一部分是武将的创建函数。具体参数详情请查看General类。
18-- 我们创建了一个名为test__General1的武将,他的势力是testKingdom,初始体力为3,体力上限为4,是一名男性。
19-- 并为他添加了两个技能
20
21Fk:loadTranslationTable {
22  ["test__General1"] = "武将的名称",
23  ["#test__General1"] = "武将的称号",
24  -- #+武将代码名称 (若不写此条目,默认为【官方】)
25  ["~test__General1"] = "武将阵亡语音台词",
26  -- ~+武将代码名称 (若不写此条目则无语音台词)
27  ["designer:test__General1"] = "武将的设计者",
28  -- designer:+武将代码名称 (若不写此条目,默认为【官方】)
29  ["illustrator:test__General1"] = "武将立绘的画师",
30  -- illustrator:+武将代码名称 (若不写此条目,默认为【官方】)
31  ["cv:test__General1"] = "武将语音的配音",
32  -- cv:+武将代码名称 (若不写此条目,默认为【官方】)
33}
34
35
36local General2 = General:new(extension, "test__General2", "testKingdom", 3, 4, General.Male)
37
38General2:addSkills { "test__General2_skill1", "test__General2_skill2" }
39General2:addRelatedSkills { "test__General2_skill3" }
40
41Fk:loadTranslationTable {
42  -- 这其中应包含武将二的相关翻译表,这里不再赘述
43}
44-- 这一部分展示了带衍生技的武将的创建方式
45
46return extension
47-- 千万不要忘记在文件的末尾加入返回语句
48-- 这里返回的是local extension = Package:new("package1") 之前创建子扩展包时的对象。

skills 文件夹包含每个技能的实现,以下是一个典型的武将技能的实现文件

 1local skill1 = fk.CreateSkill {
 2  name = "test_skill1",        --技能代码名
 3  tags = { Skill.Compulsory, } -- 技能标签,比如锁定技
 4}
 5-- 这一部分创建了技能骨架,具体参见SkillSkeletonSpec类
 6skill1:addEffect("active", {})
 7-- 添加一个主动效果
 8skill1:addEffect(fk.AfterCardsMove, {})
 9-- 添加一个触发技效果
10-- 还可以添加其他技能效果,关于技能效果类型请参见技能管理
11
12
13Fk:loadTranslationTable {
14  ["test_skill1"] = "技能名",
15  [":test_skill1"] = "技能描述",
16  ["$test_skill1"] = "技能语音1",
17  ["$test_skill2"] = "技能语音2",
18}
19-- 这一部分是该技能相关的翻译表
20
21return skill1
22-- 返回我们创建的技能骨架对象

17.5. 卡牌子扩展包#

一个典型的卡牌子扩展包的 init.lua 示例如下

 1    local extension = Package:new("package2", Package.CardPack)
 2extension.extensionName = "extension"
 3-- 这一部分与武将扩展一致
 4extension.game_modes_whitelist = { "test_Gamemode1" }
 5extension.game_modes_blacklist = { "test_Gamemode2", "test_Gamemode3" }
 6-- 这一部分定义了该扩展的黑白名单
 7Fk:loadTranslationTable {
 8  ["package2"] = "子扩展包名",
 9}
10
11extension:loadSkillSkelsByPath("./packages/extension/pkg/package2/skills")
12-- 该子扩展包技能查找路径
13local card1 = fk.CreateCard {
14  name = "test_card1",
15  type = Card.TypeBasic,
16  skill = "test_card1_skill",
17}
18-- 这里是创建卡牌的创建函数,具体参见CardSpec类
19
20Fk:loadTranslationTable {
21  ["test_Card1"] = "卡牌的名称",
22  [":test_Card1"] = "卡牌效果的描述",
23  -- <b>牌名:</b>卡牌名称<br/><b>类型:</b>装备牌·武器(装备牌拥有副类时遵照此格式)<br /><b>攻击范围</b>:1<br /><b>武器技能</b>:技能描述。
24  -- <b>牌名:</b>卡牌名称<br/><b>类型:</b>基本牌<br /><b>时机</b>:出牌阶段<br /><b>目标</b>:一名其他角色<br /><b>效果</b>:对目标造成一点伤害
25  -- 若类型为延时锦囊则直接写延时锦囊牌 即可。
26  -- 若某些卡牌拥有主动效果,例如丈八蛇矛,则需要对武器牌的skill进行翻译。翻译格式与武将技能格式一样。
27}
28
29extension:loadCardSkels { card1, }
30-- 为我们创建的卡牌添加技能
31
32extension:addCardSpec("test_card1", Card.Heart, 1)
33extension:addCardSpec("test_card1", Card.Club, 1)
34extension:addCardSpec("test_card1", Card.Diamond, 1)
35extension:addCardSpec("test_card1", Card.Spade, 1)
36-- 这里是往本卡牌扩展中添加卡牌
37
38
39return extension
40-- 千万不要忘记在文件的末尾加入返回语句
41-- 这里返回的是local extension = Package:new("package2") 之前创建子扩展包时的对象。

卡牌技能的效果以技能的形式放于 skills 文件夹内

17.6. 模式子扩展包#

对于模式子扩展包,我们的惯例是将创建模式子扩展包的语句加入整个扩展根目录的 init.lua 中。但也可以如武将或者卡牌子扩展包那样置于子扩展包文件夹内。

模式子扩展包的文件夹内应存在 rule_skills 文件夹用于存放游戏规则技能的定义。

一个典型的模式子扩展包的 init.lua 示例如下

 1local extension = Package:new("test_mode", Package.SpecialPack)
 2-- 创建扩展包对象
 3extension.extensionName = "test_mode"
 4-- 定义扩展包名称
 5
 6extension:loadSkillSkelsByPath("./packages/extension/pkg/package3/rule_skills")
 7-- 加载游戏规则技能
 8Fk:loadTranslationTable {
 9  ["test_mode"] = "模式包名称",
10}
11local test_mode = require "packages/extension/package3/test_mode"
12-- 引用游戏模式定义文件
13
14extension:addGameMode(test_mode)
15-- 这里是往本扩展包中添加游戏模式,参数为定义好的游戏模式对象。
16
17
18return extension
19-- 返回扩展包对象

模式的具体实现应该置于 testmode.lua 文件中,示例如下

 1local jieshao = [[
 2     这里是游戏模式的介绍,可以写一些游戏规则,游戏机制等。
 3   ]]
 4
 5-- 定义游戏模式的逻辑,这里放的是主要的部分。由于该部分太长,所以不放在主函数中。
 6local role_getlogic = function() end
 7
 8-- 定义游戏模式,这里从身份模式举例
 9local test_mode = fk.CreateGameMode {
10  name = "testMode",       -- 定义游戏模式的名称
11  minPlayer = 2,           -- 定义游戏模式的最少玩家数量
12  maxPlayer = 8,           -- 定义游戏模式的最多玩家数量
13  rule = "game_rule",      -- 定义游戏的规则技能
14  logic = role_getlogic,   -- 定义游戏模式的逻辑
15  main_mode = "role_mode", -- 定义游戏模式的主模式,这里是身份模式
16  ......                   -- 其他参数
17}
18
19Fk:loadTranslationTable {  -- 翻译表
20  ["testMode"] = "测试模式",
21  [":testMode"] = jieshao, -- 模式的介绍
22}
23
24return test_mode

模式子扩展包的基本代码格式已经介绍完毕,下面我们介绍一下游戏模式的主要定义。

17.6.1. 游戏模式的主要定义和逻辑定义#

原GameModeSpec类的定义地址:packages/freekill-core/lua/fk_ex.lua 这里说明的很详细!

rule参数相当于给本模式添加一个全局的技能,这个技能的就是游戏的基础规则,默认优先级是0。

警告

在定义游戏规则的技能时,不需要给技能的effect添加global参数。

接下来我们主要介绍role_getlogic,这一部分是游戏模式的主要逻辑。

逻辑的定义地址:packages/freekill-core/lua/server/gamelogic.lua

我们首先来看run()函数,这里定义了我们逻辑的主要模块的加载顺序:

这里面执行的顺序是非常重要的,需要修改效果的话,要注意修改模块的部分和模块的加载顺序!

接下来,我们对应来介绍每个模块的作用和注意事项。

17.6.2. assignRoles()#

身份分配模块,这里是分配身份的主要逻辑。

如果你对玩家的身份需要修改的话,可以更改该模块中的self.role_table表中的内容。

17.6.3. adjustSeats()#

安排座位模块,例如2v2,3v3模式就需要对玩家的座位进行调整。

17.6.4. chooseGenerals()#

选将模块,这里可以对玩家的选将池进行设定,当然将池的白名单也可以在这里去设置。

17.6.5. buildPlayerCircle()#

构建玩家圈模块,这是链接玩家执行顺序的地方,基本不用改动。

17.6.6. broadcastGeneral()#

公布选将结果模块,这里是会设定玩家的武将图像,设定武将的初始体力,体力上限和护甲等,玩家属性方面的设置。

17.6.7. prepareDrawPile()#

准备牌堆模块,这里是对牌堆的初始化,如果你需要自定义牌堆的话,可以放在这里进行修改。

17.6.8. attachSkillToPlayers()#

给玩家添加初始技能模块,这里是给玩家添加初始技能,包括斗地主的飞扬跋扈等。

如果你需要给玩家添加其他初始技能 ,可以放在这里进行修改。

17.6.9. prepareForStart()#

准备开始模块,这里游戏开始前的准备,会将全部的全局触发技放到本房间。当然这个模块大部分是用来关闭手杀特效的(

17.6.10. action()#

游戏正式准备开始的模块,在这里会触发相关的游戏时机,例如fk.GamePrepared,fk.DrawInitialCards等。

至此,我们介绍了游戏模式的主要逻辑,以及各个模块的作用和注意事项。

根据自己的需求修改对应的模块即可,没必要所有模块全部更改,所有在自定义游戏模式时,如果有不需要修改模块可以不用写。

在运行时,会根据main_mode参数判断是否是某种模式的衍生,如果是,则会默认执行对应游戏类型(身份模式,1v2模式等)的模块逻辑。