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 需要加载其包含的所有子扩展包。
audio , image 分别为声音和图像资源文件夹。
pkg 文件夹内为你的子扩展包文件夹。
这些文件夹拥有相对固定的名称,请不要随意更改
子扩展包可以用来拓展更多的技能、武将、卡牌、游戏模式。
如果你看过新人入门教程,这里就是你的DIY武将在开发时,所需要了解的各种文件与代码的规范。
17.2.1. audio#
audio 是你的语音文件夹,其内部包括了 death 文件夹, skill 文件夹和 card 文件夹。
警告
audio 中的语音文件,应 全部是mp3格式。
如果单纯的从其他格式以重命名的方式修改为mp3,可能会导致无法播放,请注意使用转化软件或者在线网站。
一个扩展包只能有一个 audio 文件夹与 image 文件夹,其内部可能存放多个其他文件夹。
death 文件夹内部应放置武将的【阵亡语音】,该语音的命名应与武将的代码名称相同。
skill 文件夹内部应放置武将的【技能语音】,该语音的命名应与武将技能的代码名称相同。
win 文件夹内部应放置武将的【胜利语音】,该语音的命名应与武将的代码名称相同
card 文件夹内部应放置【卡牌语音】, card 内部又分为 female 与 male 文件夹。 female 与 male
分别对应卡牌在使用时所播放的女性语音与男性语音。一张卡牌对应一个语音,语音的命名与卡牌的代码名称相同。
备注
如果你的武将技能有多个语音,语音的命名格式应为武将技能的代码名称+语音编号。
语音编号从1开始计算,例如testGeneral_skill1.mp3和testGeneral_skill2.mp3。
17.2.2. image#
image 是你的武将立绘与势力图片的文件夹,其内部包括了 generals 文件夹和 kingdom 和 card 文件夹。
警告
image 中的【武将立绘】文件,应 全部是jpg格式。
image 中的【势力图片】文件,应 全部是png格式。
image 中的【卡牌图片】文件,应 全部是png格式。
generals 文件夹内部应放置武将的【立绘】, 该立绘的命名应与武将的代码名称相同。
对于武将的立绘同时拥有其文件大小的要求,请记住武将的立绘格式应为 250x292 大小。
kingdom 文件夹内部应放置【DIY势力图片】,势力图片拥有三个部分,均为png图片。
备注
如果你仅仅需要原版三国杀的势力,则不用创建本文件夹。
势力的图标(格式最好在30x30-35x35之间)#
势力的阴阳玉(格式为10x12)#
势力的将框(格式为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模式等)的模块逻辑。