2023-04-09 05:35:35 +00:00
|
|
|
|
-- SPDX-License-Identifier: GPL-3.0-or-later
|
|
|
|
|
|
2023-03-26 09:32:45 +00:00
|
|
|
|
--- Engine是整个FreeKill赖以运行的核心。
|
|
|
|
|
---
|
|
|
|
|
--- 它包含了FreeKill涉及的所有武将、卡牌、游戏模式等等
|
|
|
|
|
---
|
|
|
|
|
--- 同时也提供了许多常用的函数。
|
|
|
|
|
---
|
2022-03-30 08:33:56 +00:00
|
|
|
|
---@class Engine : Object
|
2024-01-24 19:23:29 +00:00
|
|
|
|
---@field public extensions table<string, string[]> @ 所有mod列表及其包含的拓展包
|
2024-01-26 06:02:55 +00:00
|
|
|
|
---@field public extension_names string[] @ Mod名字的数组,为了方便排序
|
2023-03-26 09:32:45 +00:00
|
|
|
|
---@field public packages table<string, Package> @ 所有拓展包的列表
|
|
|
|
|
---@field public package_names string[] @ 含所有拓展包名字的数组,为了方便排序
|
|
|
|
|
---@field public skills table<string, Skill> @ 所有的技能
|
|
|
|
|
---@field public related_skills table<string, Skill[]> @ 所有技能的关联技能
|
|
|
|
|
---@field public global_trigger TriggerSkill[] @ 所有的全局触发技
|
|
|
|
|
---@field public global_status_skill table<class, Skill[]> @ 所有的全局状态技
|
|
|
|
|
---@field public generals table<string, General> @ 所有武将
|
|
|
|
|
---@field public same_generals table<string, string[]> @ 所有同名武将组合
|
|
|
|
|
---@field public lords string[] @ 所有主公武将,用于常备主公
|
2023-10-18 13:46:11 +00:00
|
|
|
|
---@field public all_card_types table<string, Card> @ 所有的卡牌类型以及一张样板牌
|
2023-03-26 09:32:45 +00:00
|
|
|
|
---@field public cards Card[] @ 所有卡牌
|
|
|
|
|
---@field public translations table<string, table<string, string>> @ 翻译表
|
|
|
|
|
---@field public game_modes table<string, GameMode> @ 所有游戏模式
|
2023-07-14 14:17:54 +00:00
|
|
|
|
---@field public game_mode_disabled table<string, string[]> @ 游戏模式禁用的包
|
2023-06-03 15:59:04 +00:00
|
|
|
|
---@field public currentResponsePattern string @ 要求用牌的种类(如要求用特定花色的桃···)
|
|
|
|
|
---@field public currentResponseReason string @ 要求用牌的原因(如濒死,被特定牌指定,使用特定技能···)
|
2023-06-23 14:18:11 +00:00
|
|
|
|
---@field public filtered_cards table<integer, Card> @ 被锁视技影响的卡牌
|
2023-08-10 19:19:59 +00:00
|
|
|
|
---@field public printed_cards table<integer, Card> @ 被某些房间现场打印的卡牌,id都是负数且从-2开始
|
2023-08-24 13:37:24 +00:00
|
|
|
|
---@field private _custom_events any[] @ 自定义事件列表
|
2023-09-19 06:27:54 +00:00
|
|
|
|
---@field public poxi_methods table<string, PoxiSpec> @ “魄袭”框操作方法表
|
2023-12-06 13:07:35 +00:00
|
|
|
|
---@field public qml_marks table<string, QmlMarkSpec> @ 自定义Qml标记的表
|
2024-01-11 10:36:05 +00:00
|
|
|
|
---@field public mini_games table<string, MiniGameSpec> @ 自定义多人交互表
|
2022-03-28 14:24:30 +00:00
|
|
|
|
local Engine = class("Engine")
|
2022-03-25 04:28:07 +00:00
|
|
|
|
|
2023-03-26 09:32:45 +00:00
|
|
|
|
--- Engine的构造函数。
|
|
|
|
|
---
|
|
|
|
|
--- 这个函数只应该被执行一次。执行了之后,会创建一个Engine实例,并放入全局变量Fk中。
|
|
|
|
|
---@return nil
|
2022-03-28 14:24:30 +00:00
|
|
|
|
function Engine:initialize()
|
2022-04-30 07:27:56 +00:00
|
|
|
|
-- Engine should be singleton
|
|
|
|
|
if Fk ~= nil then
|
|
|
|
|
error("Engine has been initialized")
|
|
|
|
|
return
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
Fk = self
|
|
|
|
|
|
2024-01-24 19:23:29 +00:00
|
|
|
|
self.extensions = {
|
|
|
|
|
["standard"] = { "standard" },
|
|
|
|
|
["standard_cards"] = { "standard_cards" },
|
|
|
|
|
["maneuvering"] = { "maneuvering" },
|
|
|
|
|
}
|
2024-01-26 06:02:55 +00:00
|
|
|
|
self.extension_names = { "standard", "standard_cards", "maneuvering" }
|
2022-04-30 07:27:56 +00:00
|
|
|
|
self.packages = {} -- name --> Package
|
|
|
|
|
self.package_names = {}
|
|
|
|
|
self.skills = {} -- name --> Skill
|
|
|
|
|
self.related_skills = {} -- skillName --> relatedSkill[]
|
|
|
|
|
self.global_trigger = {}
|
2023-01-04 06:21:29 +00:00
|
|
|
|
self.global_status_skill = {}
|
2022-04-30 07:27:56 +00:00
|
|
|
|
self.generals = {} -- name --> General
|
2023-03-18 18:21:45 +00:00
|
|
|
|
self.same_generals = {}
|
2022-04-30 07:27:56 +00:00
|
|
|
|
self.lords = {} -- lordName[]
|
2023-10-18 13:46:11 +00:00
|
|
|
|
self.all_card_types = {}
|
2022-04-30 07:27:56 +00:00
|
|
|
|
self.cards = {} -- Card[]
|
|
|
|
|
self.translations = {} -- srcText --> translated
|
2023-03-13 16:12:02 +00:00
|
|
|
|
self.game_modes = {}
|
2023-07-14 14:17:54 +00:00
|
|
|
|
self.game_mode_disabled = {}
|
2023-04-30 10:55:59 +00:00
|
|
|
|
self.kingdoms = {}
|
2023-08-24 13:37:24 +00:00
|
|
|
|
self._custom_events = {}
|
2023-09-19 06:27:54 +00:00
|
|
|
|
self.poxi_methods = {}
|
2023-12-06 13:07:35 +00:00
|
|
|
|
self.qml_marks = {}
|
2024-01-11 10:36:05 +00:00
|
|
|
|
self.mini_games = {}
|
2022-04-30 07:27:56 +00:00
|
|
|
|
|
|
|
|
|
self:loadPackages()
|
2023-07-14 14:17:54 +00:00
|
|
|
|
self:loadDisabled()
|
2022-09-15 03:17:13 +00:00
|
|
|
|
self:addSkills(AuxSkills)
|
2022-03-28 14:24:30 +00:00
|
|
|
|
end
|
|
|
|
|
|
2023-06-16 05:26:02 +00:00
|
|
|
|
local _foreign_keys = {
|
|
|
|
|
"currentResponsePattern",
|
|
|
|
|
"currentResponseReason",
|
2023-06-23 14:18:11 +00:00
|
|
|
|
"filtered_cards",
|
2023-08-10 19:19:59 +00:00
|
|
|
|
"printed_cards",
|
2023-06-16 05:26:02 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function Engine:__index(k)
|
|
|
|
|
if table.contains(_foreign_keys, k) then
|
|
|
|
|
return self:currentRoom()[k]
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
function Engine:__newindex(k, v)
|
|
|
|
|
if table.contains(_foreign_keys, k) then
|
|
|
|
|
self:currentRoom()[k] = v
|
|
|
|
|
else
|
|
|
|
|
rawset(self, k, v)
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
2023-03-26 09:32:45 +00:00
|
|
|
|
--- 向Engine中加载一个拓展包。
|
|
|
|
|
---
|
|
|
|
|
--- 会加载这个拓展包含有的所有武将、卡牌以及游戏模式。
|
|
|
|
|
---@param pack Package @ 要加载的拓展包
|
2022-03-28 14:24:30 +00:00
|
|
|
|
function Engine:loadPackage(pack)
|
2022-04-30 07:27:56 +00:00
|
|
|
|
assert(pack:isInstanceOf(Package))
|
2023-02-26 08:51:29 +00:00
|
|
|
|
if self.packages[pack.name] ~= nil then
|
2022-04-30 07:27:56 +00:00
|
|
|
|
error(string.format("Duplicate package %s detected", pack.name))
|
|
|
|
|
end
|
|
|
|
|
self.packages[pack.name] = pack
|
|
|
|
|
table.insert(self.package_names, pack.name)
|
|
|
|
|
|
|
|
|
|
-- add cards, generals and skills to Engine
|
|
|
|
|
if pack.type == Package.CardPack then
|
|
|
|
|
self:addCards(pack.cards)
|
|
|
|
|
elseif pack.type == Package.GeneralPack then
|
|
|
|
|
self:addGenerals(pack.generals)
|
|
|
|
|
end
|
|
|
|
|
self:addSkills(pack:getSkills())
|
2023-03-13 16:12:02 +00:00
|
|
|
|
self:addGameModes(pack.game_modes)
|
2022-03-25 04:28:07 +00:00
|
|
|
|
end
|
|
|
|
|
|
2023-03-26 09:32:45 +00:00
|
|
|
|
--- 加载所有拓展包。
|
|
|
|
|
---
|
|
|
|
|
--- Engine会在packages/下搜索所有含有init.lua的文件夹,并把它们作为拓展包加载进来。
|
|
|
|
|
---
|
|
|
|
|
--- 这样的init.lua可以返回单个拓展包,也可以返回拓展包数组,或者什么都不返回。
|
|
|
|
|
---
|
|
|
|
|
--- 标包和标准卡牌包比较特殊,它们永远会在第一个加载。
|
|
|
|
|
---@return nil
|
2022-03-28 14:24:30 +00:00
|
|
|
|
function Engine:loadPackages()
|
2022-04-30 07:27:56 +00:00
|
|
|
|
local directories = FileIO.ls("packages")
|
|
|
|
|
|
|
|
|
|
-- load standard & standard_cards first
|
|
|
|
|
self:loadPackage(require("packages.standard"))
|
|
|
|
|
self:loadPackage(require("packages.standard_cards"))
|
2023-08-02 15:01:28 +00:00
|
|
|
|
self:loadPackage(require("packages.maneuvering"))
|
2022-04-30 07:27:56 +00:00
|
|
|
|
table.removeOne(directories, "standard")
|
|
|
|
|
table.removeOne(directories, "standard_cards")
|
2023-08-02 15:01:28 +00:00
|
|
|
|
table.removeOne(directories, "maneuvering")
|
2022-04-30 07:27:56 +00:00
|
|
|
|
|
2023-08-27 12:21:37 +00:00
|
|
|
|
local _disable_packs = json.decode(fk.GetDisabledPacks())
|
|
|
|
|
|
2022-04-30 07:27:56 +00:00
|
|
|
|
for _, dir in ipairs(directories) do
|
2023-08-27 12:21:37 +00:00
|
|
|
|
if (not string.find(dir, ".disabled")) and not table.contains(_disable_packs, dir)
|
|
|
|
|
and FileIO.isDir("packages/" .. dir)
|
2023-02-15 11:54:35 +00:00
|
|
|
|
and FileIO.exists("packages/" .. dir .. "/init.lua") then
|
2022-04-30 07:27:56 +00:00
|
|
|
|
local pack = require(string.format("packages.%s", dir))
|
|
|
|
|
-- Note that instance of Package is a table too
|
|
|
|
|
-- so dont use type(pack) == "table" here
|
2023-02-21 05:44:24 +00:00
|
|
|
|
if type(pack) == "table" then
|
2024-01-26 06:02:55 +00:00
|
|
|
|
table.insert(self.extension_names, dir)
|
2023-02-21 05:44:24 +00:00
|
|
|
|
if pack[1] ~= nil then
|
2024-01-24 19:23:29 +00:00
|
|
|
|
self.extensions[dir] = {}
|
2023-02-21 05:44:24 +00:00
|
|
|
|
for _, p in ipairs(pack) do
|
2024-01-24 19:23:29 +00:00
|
|
|
|
table.insert(self.extensions[dir], p.name)
|
2023-02-21 05:44:24 +00:00
|
|
|
|
self:loadPackage(p)
|
|
|
|
|
end
|
|
|
|
|
else
|
2024-01-24 19:23:29 +00:00
|
|
|
|
self.extensions[dir] = { pack.name }
|
2023-02-21 05:44:24 +00:00
|
|
|
|
self:loadPackage(pack)
|
2022-03-28 14:24:30 +00:00
|
|
|
|
end
|
2022-04-30 07:27:56 +00:00
|
|
|
|
end
|
2022-03-28 14:24:30 +00:00
|
|
|
|
end
|
2022-04-30 07:27:56 +00:00
|
|
|
|
end
|
2022-03-25 04:28:07 +00:00
|
|
|
|
end
|
|
|
|
|
|
2023-07-14 14:17:54 +00:00
|
|
|
|
---@return nil
|
|
|
|
|
function Engine:loadDisabled()
|
|
|
|
|
for mode_name, game_mode in pairs(self.game_modes) do
|
|
|
|
|
local disabled_packages = {}
|
|
|
|
|
for name, pkg in pairs(self.packages) do
|
|
|
|
|
if table.contains(game_mode.blacklist or Util.DummyTable, name) or
|
|
|
|
|
(game_mode.whitelist and not table.contains(game_mode.whitelist, name)) or
|
|
|
|
|
table.contains(pkg.game_modes_blacklist or Util.DummyTable, mode_name) or
|
|
|
|
|
(pkg.game_modes_whitelist and not table.contains(pkg.game_modes_whitelist, mode_name)) then
|
|
|
|
|
table.insert(disabled_packages, name)
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
self.game_mode_disabled[game_mode.name] = disabled_packages
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
2023-03-26 09:32:45 +00:00
|
|
|
|
--- 向翻译表中加载新的翻译表。
|
|
|
|
|
---@param t table @ 要加载的翻译表,这是一个 原文 --> 译文 的键值对表
|
2023-12-03 11:35:14 +00:00
|
|
|
|
---@param lang? string @ 目标语言,默认为zh_CN
|
2023-02-27 02:23:48 +00:00
|
|
|
|
function Engine:loadTranslationTable(t, lang)
|
2022-04-30 07:27:56 +00:00
|
|
|
|
assert(type(t) == "table")
|
2023-02-27 02:23:48 +00:00
|
|
|
|
lang = lang or "zh_CN"
|
|
|
|
|
self.translations[lang] = self.translations[lang] or {}
|
2022-04-30 07:27:56 +00:00
|
|
|
|
for k, v in pairs(t) do
|
2023-02-27 02:23:48 +00:00
|
|
|
|
self.translations[lang][k] = v
|
2022-04-30 07:27:56 +00:00
|
|
|
|
end
|
2022-03-25 04:28:07 +00:00
|
|
|
|
end
|
|
|
|
|
|
2023-03-26 09:32:45 +00:00
|
|
|
|
--- 翻译一段文本。其实就是从翻译表中去找
|
|
|
|
|
---@param src string @ 要翻译的文本
|
2023-12-03 11:35:14 +00:00
|
|
|
|
---@param lang? string @ 要使用的语言,默认读取config
|
|
|
|
|
function Engine:translate(src, lang)
|
|
|
|
|
lang = lang or (Config.language or "zh_CN")
|
2023-02-27 02:23:48 +00:00
|
|
|
|
if not self.translations[lang] then lang = "zh_CN" end
|
|
|
|
|
local ret = self.translations[lang][src]
|
|
|
|
|
return ret or src
|
2022-05-01 10:37:13 +00:00
|
|
|
|
end
|
|
|
|
|
|
2023-03-26 09:32:45 +00:00
|
|
|
|
--- 向Engine中加载一个技能。
|
|
|
|
|
---
|
|
|
|
|
--- 如果技能是global的,那么同时会将其放到那些global技能表中。
|
|
|
|
|
---
|
|
|
|
|
--- 如果技能有关联技能,那么递归地加载那些关联技能。
|
|
|
|
|
---@param skill Skill @ 要加载的技能
|
2022-03-28 14:24:30 +00:00
|
|
|
|
function Engine:addSkill(skill)
|
2022-04-30 07:27:56 +00:00
|
|
|
|
assert(skill.class:isSubclassOf(Skill))
|
|
|
|
|
if self.skills[skill.name] ~= nil then
|
|
|
|
|
error(string.format("Duplicate skill %s detected", skill.name))
|
|
|
|
|
end
|
|
|
|
|
self.skills[skill.name] = skill
|
2023-01-04 06:21:29 +00:00
|
|
|
|
|
|
|
|
|
if skill.global then
|
|
|
|
|
if skill:isInstanceOf(TriggerSkill) then
|
|
|
|
|
table.insert(self.global_trigger, skill)
|
|
|
|
|
else
|
|
|
|
|
local t = self.global_status_skill
|
|
|
|
|
t[skill.class] = t[skill.class] or {}
|
|
|
|
|
table.insert(t[skill.class], skill)
|
|
|
|
|
end
|
|
|
|
|
end
|
2023-01-29 10:11:41 +00:00
|
|
|
|
|
|
|
|
|
for _, s in ipairs(skill.related_skills) do
|
|
|
|
|
self:addSkill(s)
|
|
|
|
|
end
|
2022-03-28 14:24:30 +00:00
|
|
|
|
end
|
|
|
|
|
|
2023-03-26 09:32:45 +00:00
|
|
|
|
--- 加载一系列技能。
|
|
|
|
|
---@param skills Skill[] @ 要加载的技能数组
|
2022-03-28 14:24:30 +00:00
|
|
|
|
function Engine:addSkills(skills)
|
2022-04-30 07:27:56 +00:00
|
|
|
|
assert(type(skills) == "table")
|
|
|
|
|
for _, skill in ipairs(skills) do
|
|
|
|
|
self:addSkill(skill)
|
|
|
|
|
end
|
2022-03-28 14:24:30 +00:00
|
|
|
|
end
|
|
|
|
|
|
2023-03-26 09:32:45 +00:00
|
|
|
|
--- 加载一个武将到Engine中。
|
|
|
|
|
---
|
|
|
|
|
--- 如果武将的trueName和name不同的话,那么也会将其加到同将清单中。
|
|
|
|
|
---@param general General @ 要添加的武将
|
2022-03-28 14:24:30 +00:00
|
|
|
|
function Engine:addGeneral(general)
|
2022-04-30 07:27:56 +00:00
|
|
|
|
assert(general:isInstanceOf(General))
|
|
|
|
|
if self.generals[general.name] ~= nil then
|
|
|
|
|
error(string.format("Duplicate general %s detected", general.name))
|
|
|
|
|
end
|
|
|
|
|
self.generals[general.name] = general
|
2023-03-18 18:21:45 +00:00
|
|
|
|
|
2023-04-30 10:55:59 +00:00
|
|
|
|
if general.kingdom ~= "unknown" then
|
|
|
|
|
table.insertIfNeed(self.kingdoms, general.kingdom)
|
|
|
|
|
end
|
|
|
|
|
|
2023-03-18 18:21:45 +00:00
|
|
|
|
if general.name ~= general.trueName then
|
|
|
|
|
local tName = general.trueName
|
|
|
|
|
self.same_generals[tName] = self.same_generals[tName] or { tName }
|
|
|
|
|
table.insert(self.same_generals[tName], general.name)
|
|
|
|
|
end
|
2022-03-28 14:24:30 +00:00
|
|
|
|
end
|
|
|
|
|
|
2023-03-26 09:32:45 +00:00
|
|
|
|
--- 加载一系列武将。
|
|
|
|
|
---@param generals General[] @ 要加载的武将列表
|
2022-03-28 14:24:30 +00:00
|
|
|
|
function Engine:addGenerals(generals)
|
2022-04-30 07:27:56 +00:00
|
|
|
|
assert(type(generals) == "table")
|
|
|
|
|
for _, general in ipairs(generals) do
|
|
|
|
|
self:addGeneral(general)
|
|
|
|
|
end
|
2022-03-28 14:24:30 +00:00
|
|
|
|
end
|
|
|
|
|
|
2023-09-27 13:02:22 +00:00
|
|
|
|
--- 判断一个武将是否在本房间可用。
|
|
|
|
|
---@param g string @ 武将名
|
|
|
|
|
function Engine:canUseGeneral(g)
|
|
|
|
|
local r = self:currentRoom()
|
|
|
|
|
local general = self.generals[g]
|
2023-08-24 13:37:24 +00:00
|
|
|
|
if not general then return false end
|
|
|
|
|
return not table.contains(r.disabled_packs, general.package.name) and
|
|
|
|
|
not table.contains(r.disabled_generals, g) and not general.hidden and not general.total_hidden
|
2023-06-16 05:26:02 +00:00
|
|
|
|
end
|
|
|
|
|
|
2023-03-26 09:32:45 +00:00
|
|
|
|
--- 根据武将名称,获取它的同名武将。
|
|
|
|
|
---
|
|
|
|
|
--- 注意以此法返回的同名武将列表不包含他自己。
|
|
|
|
|
---@param name string @ 要查询的武将名字
|
|
|
|
|
---@return string[] @ 这个武将对应的同名武将列表
|
2023-03-18 18:21:45 +00:00
|
|
|
|
function Engine:getSameGenerals(name)
|
2024-01-24 19:13:57 +00:00
|
|
|
|
if not self.generals[name] then return {} end
|
|
|
|
|
local tName = self.generals[name].trueName
|
2023-03-18 18:21:45 +00:00
|
|
|
|
local ret = self.same_generals[tName] or {}
|
|
|
|
|
return table.filter(ret, function(g)
|
2023-09-27 13:02:22 +00:00
|
|
|
|
return g ~= name and self.generals[g] ~= nil and self:canUseGeneral(g)
|
2023-03-18 18:21:45 +00:00
|
|
|
|
end)
|
|
|
|
|
end
|
|
|
|
|
|
2022-03-31 05:29:23 +00:00
|
|
|
|
local cardId = 1
|
2023-03-26 09:32:45 +00:00
|
|
|
|
|
|
|
|
|
--- 向Engine中加载一张卡牌。
|
|
|
|
|
---
|
|
|
|
|
--- 卡牌在加载的时候,会被赋予一个唯一的id。(从1开始)
|
|
|
|
|
---@param card Card @ 要加载的卡牌
|
2022-03-28 14:24:30 +00:00
|
|
|
|
function Engine:addCard(card)
|
2022-04-30 07:27:56 +00:00
|
|
|
|
assert(card.class:isSubclassOf(Card))
|
|
|
|
|
card.id = cardId
|
|
|
|
|
cardId = cardId + 1
|
|
|
|
|
table.insert(self.cards, card)
|
2023-10-18 13:46:11 +00:00
|
|
|
|
if self.all_card_types[card.name] == nil then
|
2023-12-09 13:57:47 +00:00
|
|
|
|
self.skills[card.skill.name] = card.skill
|
2023-10-18 13:46:11 +00:00
|
|
|
|
self.all_card_types[card.name] = card
|
2023-02-15 11:54:35 +00:00
|
|
|
|
end
|
2022-03-28 14:24:30 +00:00
|
|
|
|
end
|
|
|
|
|
|
2023-03-26 09:32:45 +00:00
|
|
|
|
--- 向Engine中加载一系列卡牌。
|
|
|
|
|
---@param cards Card[] @ 要加载的卡牌列表
|
2022-03-28 14:24:30 +00:00
|
|
|
|
function Engine:addCards(cards)
|
2022-04-30 07:27:56 +00:00
|
|
|
|
for _, card in ipairs(cards) do
|
|
|
|
|
self:addCard(card)
|
|
|
|
|
end
|
2022-03-25 04:28:07 +00:00
|
|
|
|
end
|
|
|
|
|
|
2023-03-26 09:32:45 +00:00
|
|
|
|
--- 根据牌名、花色、点数,复制一张牌。
|
|
|
|
|
---
|
|
|
|
|
--- 返回的牌是一张虚拟牌。
|
|
|
|
|
---@param name string @ 牌名
|
2023-12-03 11:35:14 +00:00
|
|
|
|
---@param suit? Suit @ 花色
|
|
|
|
|
---@param number? integer @ 点数
|
2023-01-16 11:13:07 +00:00
|
|
|
|
---@return Card
|
|
|
|
|
function Engine:cloneCard(name, suit, number)
|
2023-10-18 13:46:11 +00:00
|
|
|
|
local cd = self.all_card_types[name]
|
2023-01-16 11:13:07 +00:00
|
|
|
|
assert(cd, "Attempt to clone a card that not added to engine")
|
2023-02-15 11:54:35 +00:00
|
|
|
|
local ret = cd:clone(suit, number)
|
|
|
|
|
ret.package = cd.package
|
|
|
|
|
return ret
|
2023-01-16 11:13:07 +00:00
|
|
|
|
end
|
|
|
|
|
|
2023-03-26 09:32:45 +00:00
|
|
|
|
--- 向Engine中添加一系列游戏模式。
|
|
|
|
|
---@param game_modes GameMode[] @ 要添加的游戏模式列表
|
2023-03-13 16:12:02 +00:00
|
|
|
|
function Engine:addGameModes(game_modes)
|
|
|
|
|
for _, s in ipairs(game_modes) do
|
|
|
|
|
self:addGameMode(s)
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
2023-03-26 09:32:45 +00:00
|
|
|
|
--- 向Engine中添加一个游戏模式。
|
|
|
|
|
---@param game_mode GameMode @ 要添加的游戏模式
|
2023-03-13 16:12:02 +00:00
|
|
|
|
function Engine:addGameMode(game_mode)
|
|
|
|
|
assert(game_mode:isInstanceOf(GameMode))
|
|
|
|
|
if self.game_modes[game_mode.name] ~= nil then
|
|
|
|
|
error(string.format("Duplicate game_mode %s detected", game_mode.name))
|
|
|
|
|
end
|
|
|
|
|
self.game_modes[game_mode.name] = game_mode
|
|
|
|
|
end
|
|
|
|
|
|
2023-08-24 13:37:24 +00:00
|
|
|
|
function Engine:addGameEvent(name, pfunc, mfunc, cfunc, efunc)
|
|
|
|
|
table.insert(self._custom_events, { name = name, p = pfunc, m = mfunc, c = cfunc, e = efunc })
|
|
|
|
|
end
|
|
|
|
|
|
2023-09-19 06:27:54 +00:00
|
|
|
|
---@param spec PoxiSpec
|
|
|
|
|
function Engine:addPoxiMethod(spec)
|
|
|
|
|
assert(type(spec.name) == "string")
|
|
|
|
|
assert(type(spec.card_filter) == "function")
|
|
|
|
|
assert(type(spec.feasible) == "function")
|
2023-12-06 13:07:35 +00:00
|
|
|
|
if self.poxi_methods[spec.name] then
|
|
|
|
|
fk.qCritical("Warning: duplicated poxi_method " .. spec.name)
|
|
|
|
|
end
|
2023-09-19 06:27:54 +00:00
|
|
|
|
self.poxi_methods[spec.name] = spec
|
|
|
|
|
spec.default_choice = spec.default_choice or function() return {} end
|
|
|
|
|
spec.post_select = spec.post_select or function(s) return s end
|
|
|
|
|
end
|
|
|
|
|
|
2024-01-11 10:36:05 +00:00
|
|
|
|
---@param spec QmlMarkSpec
|
2023-12-06 13:07:35 +00:00
|
|
|
|
function Engine:addQmlMark(spec)
|
|
|
|
|
assert(type(spec.name) == "string")
|
|
|
|
|
if self.qml_marks[spec.name] then
|
|
|
|
|
fk.qCritical("Warning: duplicated qml mark type " .. spec.name)
|
|
|
|
|
end
|
|
|
|
|
self.qml_marks[spec.name] = spec
|
|
|
|
|
end
|
|
|
|
|
|
2024-01-11 10:36:05 +00:00
|
|
|
|
---@param spec MiniGameSpec
|
|
|
|
|
function Engine:addMiniGame(spec)
|
|
|
|
|
assert(type(spec.name) == "string")
|
|
|
|
|
if self.mini_games[spec.name] then
|
|
|
|
|
fk.qCritical("Warning: duplicated mini game type " .. spec.name)
|
|
|
|
|
end
|
|
|
|
|
self.mini_games[spec.name] = spec
|
|
|
|
|
end
|
|
|
|
|
|
2023-03-26 09:32:45 +00:00
|
|
|
|
--- 从已经开启的拓展包中,随机选出若干名武将。
|
|
|
|
|
---
|
|
|
|
|
--- 对于同名武将不会重复选取。
|
|
|
|
|
---
|
|
|
|
|
--- 如果符合条件的武将不够,那么就不能保证能选出那么多武将。
|
|
|
|
|
---@param num integer @ 要选出的武将数量
|
2023-12-03 11:35:14 +00:00
|
|
|
|
---@param generalPool? General[] @ 选择的范围,默认是已经启用的所有武将
|
|
|
|
|
---@param except? string[] @ 特别要排除掉的武将名列表,默认是空表
|
|
|
|
|
---@param filter? fun(g: General): boolean? @ 可选参数,若这个函数返回true的话这个武将被排除在外
|
2023-03-26 09:32:45 +00:00
|
|
|
|
---@return General[] @ 随机选出的武将列表
|
2022-03-28 14:24:30 +00:00
|
|
|
|
function Engine:getGeneralsRandomly(num, generalPool, except, filter)
|
2022-04-30 07:27:56 +00:00
|
|
|
|
if filter then
|
|
|
|
|
assert(type(filter) == "function")
|
|
|
|
|
end
|
2022-03-25 04:28:07 +00:00
|
|
|
|
|
2023-03-14 06:12:13 +00:00
|
|
|
|
generalPool = generalPool or self:getAllGenerals()
|
2022-04-30 07:27:56 +00:00
|
|
|
|
except = except or {}
|
2023-02-15 16:54:39 +00:00
|
|
|
|
for _, g in ipairs(self.packages["test_p_0"].generals) do
|
|
|
|
|
table.insert(except, g.name)
|
|
|
|
|
end
|
2022-04-01 12:51:01 +00:00
|
|
|
|
|
2022-04-30 07:27:56 +00:00
|
|
|
|
local availableGenerals = {}
|
|
|
|
|
for _, general in pairs(generalPool) do
|
|
|
|
|
if not table.contains(except, general.name) and not (filter and filter(general)) then
|
2023-04-23 13:10:07 +00:00
|
|
|
|
if (not general.hidden and not general.total_hidden) and
|
|
|
|
|
#table.filter(availableGenerals, function(g)
|
2023-03-18 18:21:45 +00:00
|
|
|
|
return g.trueName == general.trueName
|
|
|
|
|
end) == 0 then
|
|
|
|
|
table.insert(availableGenerals, general)
|
|
|
|
|
end
|
2022-03-25 04:28:07 +00:00
|
|
|
|
end
|
2022-04-30 07:27:56 +00:00
|
|
|
|
end
|
2022-03-25 04:28:07 +00:00
|
|
|
|
|
2023-07-16 11:17:03 +00:00
|
|
|
|
if #availableGenerals < num then
|
2022-04-30 07:27:56 +00:00
|
|
|
|
return {}
|
|
|
|
|
end
|
2022-03-25 04:28:07 +00:00
|
|
|
|
|
2023-07-16 11:17:03 +00:00
|
|
|
|
return table.random(availableGenerals, num)
|
2022-03-25 04:28:07 +00:00
|
|
|
|
end
|
|
|
|
|
|
2023-03-26 09:32:45 +00:00
|
|
|
|
--- 获取已经启用的所有武将的列表。
|
2023-12-03 11:35:14 +00:00
|
|
|
|
---@param except? General[] @ 特别指明要排除在外的武将
|
2023-03-26 09:32:45 +00:00
|
|
|
|
---@return General[] @ 所有武将的列表
|
2022-03-28 14:24:30 +00:00
|
|
|
|
function Engine:getAllGenerals(except)
|
2022-04-30 07:27:56 +00:00
|
|
|
|
local result = {}
|
2023-03-14 06:12:13 +00:00
|
|
|
|
for _, general in pairs(self.generals) do
|
2022-04-30 07:27:56 +00:00
|
|
|
|
if not (except and table.contains(except, general)) then
|
2023-09-27 13:02:22 +00:00
|
|
|
|
if self:canUseGeneral(general.name) then
|
2023-03-14 06:12:13 +00:00
|
|
|
|
table.insert(result, general)
|
|
|
|
|
end
|
2022-03-25 04:28:07 +00:00
|
|
|
|
end
|
2022-04-30 07:27:56 +00:00
|
|
|
|
end
|
2022-03-25 04:28:07 +00:00
|
|
|
|
|
2022-04-30 07:27:56 +00:00
|
|
|
|
return result
|
2022-03-25 04:28:07 +00:00
|
|
|
|
end
|
|
|
|
|
|
2023-03-26 09:32:45 +00:00
|
|
|
|
--- 获取当前已经启用的所有卡牌。
|
2023-12-03 11:35:14 +00:00
|
|
|
|
---@param except? integer[] @ 特别指定要排除在外的id列表
|
2023-03-26 09:32:45 +00:00
|
|
|
|
---@return integer[] @ 所有卡牌id的列表
|
2022-04-08 10:39:58 +00:00
|
|
|
|
function Engine:getAllCardIds(except)
|
2022-04-30 07:27:56 +00:00
|
|
|
|
local result = {}
|
|
|
|
|
for _, card in ipairs(self.cards) do
|
|
|
|
|
if not (except and table.contains(except, card.id)) then
|
2023-06-16 05:26:02 +00:00
|
|
|
|
if not table.contains(self:currentRoom().disabled_packs, card.package.name) then
|
2023-03-14 06:12:13 +00:00
|
|
|
|
table.insert(result, card.id)
|
|
|
|
|
end
|
2022-04-08 10:39:58 +00:00
|
|
|
|
end
|
2022-04-30 07:27:56 +00:00
|
|
|
|
end
|
2022-04-08 10:39:58 +00:00
|
|
|
|
|
2022-04-30 07:27:56 +00:00
|
|
|
|
return result
|
2022-04-08 10:39:58 +00:00
|
|
|
|
end
|
|
|
|
|
|
2023-03-26 09:32:45 +00:00
|
|
|
|
--- 根据id返回相应的卡牌。
|
|
|
|
|
---@param id integer @ 牌的id
|
2023-12-03 11:35:14 +00:00
|
|
|
|
---@param ignoreFilter? boolean @ 是否要无视掉锁定视为技,直接获得真牌
|
2023-03-26 09:32:45 +00:00
|
|
|
|
---@return Card @ 这个id对应的卡牌
|
2023-02-15 11:54:35 +00:00
|
|
|
|
function Engine:getCardById(id, ignoreFilter)
|
2023-08-10 19:19:59 +00:00
|
|
|
|
if id == nil then return nil end
|
|
|
|
|
local card_tab = (id >= -1) and self.cards or self.printed_cards
|
|
|
|
|
local ret = card_tab[id]
|
2023-02-15 11:54:35 +00:00
|
|
|
|
if not ignoreFilter then
|
2023-08-10 19:19:59 +00:00
|
|
|
|
ret = self.filtered_cards[id] or card_tab[id]
|
2023-02-15 11:54:35 +00:00
|
|
|
|
end
|
|
|
|
|
return ret
|
|
|
|
|
end
|
|
|
|
|
|
2023-03-26 09:32:45 +00:00
|
|
|
|
--- 对那个id应用锁定视为技,将它变成要被锁定视为的牌。
|
|
|
|
|
---@param id integer @ 要处理的id
|
|
|
|
|
---@param player Player @ 和这张牌扯上关系的那名玩家
|
|
|
|
|
---@param data any @ 随意,目前只用到JudgeStruct,为了影响判定牌
|
2023-02-15 11:54:35 +00:00
|
|
|
|
function Engine:filterCard(id, player, data)
|
|
|
|
|
if player == nil then
|
2023-06-23 14:18:11 +00:00
|
|
|
|
self.filtered_cards[id] = nil
|
2023-02-15 11:54:35 +00:00
|
|
|
|
return
|
|
|
|
|
end
|
2023-12-10 10:55:16 +00:00
|
|
|
|
|
|
|
|
|
local card = self:getCardById(id, true)
|
2023-06-11 08:22:11 +00:00
|
|
|
|
local filters = self:currentRoom().status_skills[FilterSkill] or Util.DummyTable
|
2023-04-10 07:55:06 +00:00
|
|
|
|
|
2023-02-15 11:54:35 +00:00
|
|
|
|
if #filters == 0 then
|
2023-06-23 14:18:11 +00:00
|
|
|
|
self.filtered_cards[id] = nil
|
2023-02-15 11:54:35 +00:00
|
|
|
|
return
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
local modify = false
|
|
|
|
|
if data and type(data) == "table" and data.card
|
|
|
|
|
and type(data.card) == "table" and data.card:isInstanceOf(Card) then
|
|
|
|
|
modify = true
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
for _, f in ipairs(filters) do
|
2023-12-10 10:55:16 +00:00
|
|
|
|
if f:cardFilter(card, player, type(data) == "table" and data.isJudgeEvent) then
|
2023-04-10 07:55:06 +00:00
|
|
|
|
local _card = f:viewAs(card, player)
|
2023-02-15 11:54:35 +00:00
|
|
|
|
_card.id = id
|
|
|
|
|
_card.skillName = f.name
|
|
|
|
|
if modify and RoomInstance then
|
|
|
|
|
if not f.mute then
|
2023-08-24 13:37:24 +00:00
|
|
|
|
player:broadcastSkillInvoke(f.name)
|
2023-02-15 11:54:35 +00:00
|
|
|
|
end
|
|
|
|
|
RoomInstance:doAnimate("InvokeSkill", {
|
|
|
|
|
name = f.name,
|
|
|
|
|
player = player.id,
|
|
|
|
|
skill_type = f.anim_type,
|
|
|
|
|
})
|
|
|
|
|
RoomInstance:sendLog{
|
|
|
|
|
type = "#FilterCard",
|
|
|
|
|
arg = f.name,
|
|
|
|
|
from = player.id,
|
|
|
|
|
arg2 = card:toLogString(),
|
|
|
|
|
arg3 = _card:toLogString(),
|
|
|
|
|
}
|
|
|
|
|
end
|
|
|
|
|
card = _card
|
|
|
|
|
end
|
|
|
|
|
if card == nil then
|
|
|
|
|
card = self:getCardById(id)
|
|
|
|
|
end
|
2023-06-23 14:18:11 +00:00
|
|
|
|
self.filtered_cards[id] = card
|
2023-02-15 11:54:35 +00:00
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
if modify then
|
2023-06-23 14:18:11 +00:00
|
|
|
|
self.filtered_cards[id] = nil
|
2023-02-15 11:54:35 +00:00
|
|
|
|
data.card = card
|
|
|
|
|
return
|
|
|
|
|
end
|
2022-04-08 10:39:58 +00:00
|
|
|
|
end
|
|
|
|
|
|
2023-08-10 19:19:59 +00:00
|
|
|
|
--- 添加一张现场打印的牌到游戏中。
|
|
|
|
|
---
|
|
|
|
|
--- 这张牌必须是clone出来的虚拟牌,不能有子卡;因为他接下来就要变成实体卡了
|
|
|
|
|
---@param card Card
|
|
|
|
|
function Engine:_addPrintedCard(card)
|
|
|
|
|
assert(card:isVirtual() and #card.subcards == 0)
|
|
|
|
|
table.insert(self.printed_cards, card)
|
|
|
|
|
local id = -#self.printed_cards - 1
|
|
|
|
|
card.id = id
|
|
|
|
|
self.printed_cards[id] = card
|
|
|
|
|
end
|
|
|
|
|
|
2023-03-26 09:32:45 +00:00
|
|
|
|
--- 获知当前的Engine是跑在服务端还是客户端,并返回相应的实例。
|
|
|
|
|
---@return Room | Client
|
2022-09-14 05:01:10 +00:00
|
|
|
|
function Engine:currentRoom()
|
2023-02-26 07:01:14 +00:00
|
|
|
|
if RoomInstance then
|
|
|
|
|
return RoomInstance
|
2022-09-14 05:01:10 +00:00
|
|
|
|
end
|
2023-02-26 07:01:14 +00:00
|
|
|
|
return ClientInstance
|
2022-09-14 05:01:10 +00:00
|
|
|
|
end
|
|
|
|
|
|
2023-03-26 09:32:45 +00:00
|
|
|
|
--- 根据字符串获得这个技能或者这张牌的描述
|
|
|
|
|
---
|
|
|
|
|
--- 其实就是翻译了 ":" .. name 罢了
|
|
|
|
|
---@param name string @ 要获得描述的名字
|
|
|
|
|
---@return string @ 描述
|
2023-02-15 11:54:35 +00:00
|
|
|
|
function Engine:getDescription(name)
|
|
|
|
|
return self:translate(":" .. name)
|
|
|
|
|
end
|
|
|
|
|
|
2022-03-28 14:24:30 +00:00
|
|
|
|
return Engine
|