2023-04-09 05:35:35 +00:00
|
|
|
|
-- SPDX-License-Identifier: GPL-3.0-or-later
|
|
|
|
|
|
2023-04-13 12:17:39 +00:00
|
|
|
|
local Util = {}
|
2023-06-08 17:10:16 +00:00
|
|
|
|
Util.DummyFunc = function() end
|
2023-07-16 07:32:16 +00:00
|
|
|
|
Util.TrueFunc = function() return true end
|
|
|
|
|
Util.FalseFunc = function() return false end
|
2023-06-08 17:10:16 +00:00
|
|
|
|
Util.DummyTable = setmetatable({}, {
|
|
|
|
|
__newindex = function() error("Cannot assign to dummy table") end
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
local metamethods = {
|
|
|
|
|
"__add", "__sub", "__mul", "__div", "__mod", "__pow", "__unm", "__idiv",
|
|
|
|
|
"__band", "__bor", "__bxor", "__bnot", "__shl", "__shr",
|
|
|
|
|
"__concat", "__len", "__eq", "__lt", "__le", "__call",
|
|
|
|
|
-- "__index", "__newindex",
|
|
|
|
|
}
|
|
|
|
|
-- 别对类用 暂且会弄坏isSubclassOf 懒得研究先
|
|
|
|
|
Util.lockTable = function(t)
|
|
|
|
|
local mt = getmetatable(t) or Util.DummyTable
|
|
|
|
|
local new_mt = {
|
|
|
|
|
__index = t,
|
|
|
|
|
__newindex = function() error("Cannot assign to locked table") end,
|
|
|
|
|
__metatable = false,
|
|
|
|
|
}
|
|
|
|
|
for _, e in ipairs(metamethods) do
|
|
|
|
|
new_mt[e] = mt[e]
|
|
|
|
|
end
|
|
|
|
|
return setmetatable({}, new_mt)
|
|
|
|
|
end
|
|
|
|
|
|
2023-08-12 18:25:04 +00:00
|
|
|
|
Util.convertSubtypeAndEquipSlot = function(value)
|
|
|
|
|
if type(value) == "number" then
|
|
|
|
|
local mapper = {
|
|
|
|
|
[Card.SubtypeWeapon] = Player.WeaponSlot,
|
|
|
|
|
[Card.SubtypeArmor] = Player.ArmorSlot,
|
|
|
|
|
[Card.SubtypeOffensiveRide] = Player.OffensiveRideSlot,
|
|
|
|
|
[Card.SubtypeDefensiveRide] = Player.DefensiveRideSlot,
|
|
|
|
|
[Card.SubtypeTreasure] = Player.TreasureSlot,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return mapper[value]
|
|
|
|
|
else
|
|
|
|
|
local mapper = {
|
|
|
|
|
[Player.WeaponSlot] = Card.SubtypeWeapon,
|
|
|
|
|
[Player.ArmorSlot] = Card.SubtypeArmor,
|
|
|
|
|
[Player.OffensiveRideSlot] = Card.SubtypeOffensiveRide,
|
|
|
|
|
[Player.DefensiveRideSlot] = Card.SubtypeDefensiveRide,
|
|
|
|
|
[Player.TreasureSlot] = Card.SubtypeTreasure,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return mapper[value]
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
2023-11-07 04:57:00 +00:00
|
|
|
|
--- 根据花色文字描述(如 黑桃、红桃、梅花、方块)或者符号(如♠♥♣♦,带颜色)返回花色ID。
|
|
|
|
|
---@param symbol string @ 描述/符号(原文,确保没被翻译过)
|
|
|
|
|
---@return Suit @ 花色ID
|
|
|
|
|
Util.getSuitFromString = function(symbol)
|
|
|
|
|
assert(type(symbol) == "string")
|
|
|
|
|
if symbol:find("spade") then
|
|
|
|
|
return Card.Spade
|
|
|
|
|
elseif symbol:find("heart") then
|
|
|
|
|
return Card.Heart
|
|
|
|
|
elseif symbol:find("club") then
|
|
|
|
|
return Card.Club
|
|
|
|
|
elseif symbol:find("diamond") then
|
|
|
|
|
return Card.Diamond
|
|
|
|
|
else
|
|
|
|
|
return Card.NoSuit
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
2023-06-16 02:56:33 +00:00
|
|
|
|
function printf(fmt, ...)
|
|
|
|
|
print(string.format(fmt, ...))
|
|
|
|
|
end
|
2023-04-13 12:17:39 +00:00
|
|
|
|
|
2022-03-27 06:49:41 +00:00
|
|
|
|
-- the iterator of QList object
|
|
|
|
|
local qlist_iterator = function(list, n)
|
2022-04-30 07:27:56 +00:00
|
|
|
|
if n < list:length() - 1 then
|
|
|
|
|
return n + 1, list:at(n + 1) -- the next element of list
|
|
|
|
|
end
|
2022-03-27 06:49:41 +00:00
|
|
|
|
end
|
|
|
|
|
|
2022-03-31 05:29:23 +00:00
|
|
|
|
function fk.qlist(list)
|
2022-04-30 07:27:56 +00:00
|
|
|
|
return qlist_iterator, list, -1
|
2022-03-27 06:49:41 +00:00
|
|
|
|
end
|
|
|
|
|
|
2023-12-03 10:45:25 +00:00
|
|
|
|
--- 用于for循环的迭代函数。可以将表按照某种权值的顺序进行遍历,这样不用进行完整排序。
|
|
|
|
|
---@generic T
|
|
|
|
|
---@param t T[]
|
|
|
|
|
---@param val_func? fun(e: T): integer @ 计算权值的函数,对int[]可不写
|
|
|
|
|
---@param reverse? boolean @ 是否反排?反排的话优先返回权值小的元素
|
|
|
|
|
function fk.sorted_pairs(t, val_func, reverse)
|
|
|
|
|
val_func = val_func or function(e) return e end
|
|
|
|
|
local t2 = table.simpleClone(t) -- 克隆一次表,用作迭代器上值
|
|
|
|
|
local iter = function()
|
|
|
|
|
local max_idx, max, max_val = -1, nil, nil
|
|
|
|
|
for i, v in ipairs(t2) do
|
|
|
|
|
if not max then
|
|
|
|
|
max_idx, max, max_val = i, v, val_func(v)
|
|
|
|
|
else
|
|
|
|
|
local val = val_func(v)
|
|
|
|
|
local checked = val > max_val
|
|
|
|
|
if reverse then checked = not checked end
|
|
|
|
|
if checked then
|
|
|
|
|
max_idx, max, max_val = i, v, val
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
if max_idx == -1 then return nil, nil end
|
|
|
|
|
table.remove(t2, max_idx)
|
|
|
|
|
return -1, max, max_val
|
|
|
|
|
end
|
|
|
|
|
return iter, nil, 1
|
|
|
|
|
end
|
|
|
|
|
|
2023-02-21 05:44:24 +00:00
|
|
|
|
---@param func fun(element, index, array)
|
|
|
|
|
function table:forEach(func)
|
|
|
|
|
for i, v in ipairs(self) do
|
|
|
|
|
func(v, i, self)
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
2023-07-14 15:12:46 +00:00
|
|
|
|
---@param func fun(element, index, array): any
|
2023-02-21 05:44:24 +00:00
|
|
|
|
function table:every(func)
|
|
|
|
|
for i, v in ipairs(self) do
|
|
|
|
|
if not func(v, i, self) then
|
|
|
|
|
return false
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
return true
|
|
|
|
|
end
|
|
|
|
|
|
2023-07-14 15:12:46 +00:00
|
|
|
|
---@param func fun(element, index, array): any
|
2023-04-22 06:10:06 +00:00
|
|
|
|
function table:find(func)
|
|
|
|
|
for i, v in ipairs(self) do
|
|
|
|
|
if func(v, i, self) then
|
|
|
|
|
return v
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
return nil
|
|
|
|
|
end
|
|
|
|
|
|
2023-02-21 05:44:24 +00:00
|
|
|
|
---@generic T
|
|
|
|
|
---@param self T[]
|
2023-07-14 15:12:46 +00:00
|
|
|
|
---@param func fun(element, index, array): any
|
2023-02-21 05:44:24 +00:00
|
|
|
|
---@return T[]
|
|
|
|
|
function table.filter(self, func)
|
|
|
|
|
local ret = {}
|
|
|
|
|
for i, v in ipairs(self) do
|
|
|
|
|
if func(v, i, self) then
|
|
|
|
|
table.insert(ret, v)
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
return ret
|
|
|
|
|
end
|
|
|
|
|
|
2023-07-14 15:12:46 +00:00
|
|
|
|
---@param func fun(element, index, array): any
|
2023-03-26 09:32:45 +00:00
|
|
|
|
function table:map(func)
|
2023-02-21 05:44:24 +00:00
|
|
|
|
local ret = {}
|
|
|
|
|
for i, v in ipairs(self) do
|
|
|
|
|
table.insert(ret, func(v, i, self))
|
|
|
|
|
end
|
|
|
|
|
return ret
|
|
|
|
|
end
|
|
|
|
|
|
2023-03-14 06:12:13 +00:00
|
|
|
|
-- frequenly used filter & map functions
|
2023-11-07 04:57:00 +00:00
|
|
|
|
|
|
|
|
|
--- 返回ID
|
2023-04-13 12:17:39 +00:00
|
|
|
|
Util.IdMapper = function(e) return e.id end
|
2023-11-07 04:57:00 +00:00
|
|
|
|
--- 根据卡牌ID返回卡牌
|
2023-04-13 12:17:39 +00:00
|
|
|
|
Util.Id2CardMapper = function(id) return Fk:getCardById(id) end
|
2023-11-07 04:57:00 +00:00
|
|
|
|
--- 根据玩家ID返回玩家
|
2023-04-13 12:17:39 +00:00
|
|
|
|
Util.Id2PlayerMapper = function(id)
|
|
|
|
|
return Fk:currentRoom():getPlayerById(id)
|
|
|
|
|
end
|
2023-11-07 04:57:00 +00:00
|
|
|
|
--- 返回武将名
|
2023-04-23 13:10:07 +00:00
|
|
|
|
Util.NameMapper = function(e) return e.name end
|
2023-11-07 04:57:00 +00:00
|
|
|
|
--- 根据武将名返回武将
|
2023-04-23 13:10:07 +00:00
|
|
|
|
Util.Name2GeneralMapper = function(e) return Fk.generals[e] end
|
2023-11-07 04:57:00 +00:00
|
|
|
|
--- 根据技能名返回技能
|
2023-04-23 13:10:07 +00:00
|
|
|
|
Util.Name2SkillMapper = function(e) return Fk.skills[e] end
|
2023-11-07 04:57:00 +00:00
|
|
|
|
--- 返回译文
|
2023-10-27 14:19:30 +00:00
|
|
|
|
Util.TranslateMapper = function(str) return Fk:translate(str) end
|
2023-03-14 06:12:13 +00:00
|
|
|
|
|
2023-08-01 18:19:51 +00:00
|
|
|
|
-- for card preset
|
|
|
|
|
Util.GlobalCanUse = function(self, player, card)
|
|
|
|
|
local room = Fk:currentRoom()
|
|
|
|
|
for _, p in ipairs(room.alive_players) do
|
|
|
|
|
if not (card and player:isProhibited(p, card)) then
|
|
|
|
|
return true
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
Util.AoeCanUse = function(self, player, card)
|
|
|
|
|
local room = Fk:currentRoom()
|
|
|
|
|
for _, p in ipairs(room.alive_players) do
|
|
|
|
|
if p ~= player and not (card and player:isProhibited(p, card)) then
|
|
|
|
|
return true
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
Util.GlobalOnUse = function(self, room, cardUseEvent)
|
|
|
|
|
if not cardUseEvent.tos or #TargetGroup:getRealTargets(cardUseEvent.tos) == 0 then
|
|
|
|
|
cardUseEvent.tos = {}
|
|
|
|
|
for _, player in ipairs(room:getAlivePlayers()) do
|
|
|
|
|
if not room:getPlayerById(cardUseEvent.from):isProhibited(player, cardUseEvent.card) then
|
|
|
|
|
TargetGroup:pushTargets(cardUseEvent.tos, player.id)
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
Util.AoeOnUse = function(self, room, cardUseEvent)
|
|
|
|
|
if not cardUseEvent.tos or #TargetGroup:getRealTargets(cardUseEvent.tos) == 0 then
|
|
|
|
|
cardUseEvent.tos = {}
|
|
|
|
|
for _, player in ipairs(room:getOtherPlayers(room:getPlayerById(cardUseEvent.from))) do
|
|
|
|
|
if not room:getPlayerById(cardUseEvent.from):isProhibited(player, cardUseEvent.card) then
|
|
|
|
|
TargetGroup:pushTargets(cardUseEvent.tos, player.id)
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
2023-02-21 05:44:24 +00:00
|
|
|
|
---@generic T
|
|
|
|
|
---@param self T[]
|
|
|
|
|
---@return T[]
|
|
|
|
|
function table.reverse(self)
|
|
|
|
|
local ret = {}
|
|
|
|
|
for _, e in ipairs(self) do
|
|
|
|
|
table.insert(ret, 1, e)
|
|
|
|
|
end
|
|
|
|
|
return ret
|
|
|
|
|
end
|
|
|
|
|
|
2022-03-27 06:49:41 +00:00
|
|
|
|
function table:contains(element)
|
2023-01-04 06:21:29 +00:00
|
|
|
|
if #self == 0 then return false end
|
2022-04-30 07:27:56 +00:00
|
|
|
|
for _, e in ipairs(self) do
|
|
|
|
|
if e == element then return true end
|
|
|
|
|
end
|
2022-03-27 06:49:41 +00:00
|
|
|
|
end
|
|
|
|
|
|
2022-03-28 14:24:30 +00:00
|
|
|
|
function table:shuffle()
|
2023-08-13 07:05:45 +00:00
|
|
|
|
if #self == 2 then
|
|
|
|
|
if math.random() < 0.5 then
|
|
|
|
|
self[1], self[2] = self[2], self[1]
|
|
|
|
|
end
|
|
|
|
|
else
|
|
|
|
|
for i = #self, 2, -1 do
|
|
|
|
|
local j = math.random(i)
|
|
|
|
|
self[i], self[j] = self[j], self[i]
|
|
|
|
|
end
|
2022-04-30 07:27:56 +00:00
|
|
|
|
end
|
2022-03-28 14:24:30 +00:00
|
|
|
|
end
|
|
|
|
|
|
2022-03-27 06:49:41 +00:00
|
|
|
|
function table:insertTable(list)
|
2022-04-30 07:27:56 +00:00
|
|
|
|
for _, e in ipairs(list) do
|
|
|
|
|
table.insert(self, e)
|
|
|
|
|
end
|
2022-03-27 06:49:41 +00:00
|
|
|
|
end
|
|
|
|
|
|
2022-04-01 12:51:01 +00:00
|
|
|
|
function table:indexOf(value, from)
|
2022-04-30 07:27:56 +00:00
|
|
|
|
from = from or 1
|
|
|
|
|
for i = from, #self do
|
|
|
|
|
if self[i] == value then return i end
|
|
|
|
|
end
|
|
|
|
|
return -1
|
2022-04-01 12:51:01 +00:00
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
function table:removeOne(element)
|
2022-04-30 07:27:56 +00:00
|
|
|
|
if #self == 0 or type(self[1]) ~= type(element) then return false end
|
2022-04-01 12:51:01 +00:00
|
|
|
|
|
2022-04-30 07:27:56 +00:00
|
|
|
|
for i = 1, #self do
|
|
|
|
|
if self[i] == element then
|
|
|
|
|
table.remove(self, i)
|
|
|
|
|
return true
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
return false
|
2022-04-01 12:51:01 +00:00
|
|
|
|
end
|
|
|
|
|
|
2022-04-14 10:22:00 +00:00
|
|
|
|
-- Note: only clone key and value, no metatable
|
|
|
|
|
-- so dont use for class or instance
|
|
|
|
|
---@generic T
|
|
|
|
|
---@param self T
|
|
|
|
|
---@return T
|
|
|
|
|
function table.clone(self)
|
2022-04-30 07:27:56 +00:00
|
|
|
|
local ret = {}
|
|
|
|
|
for k, v in pairs(self) do
|
|
|
|
|
if type(v) == "table" then
|
|
|
|
|
ret[k] = table.clone(v)
|
|
|
|
|
else
|
|
|
|
|
ret[k] = v
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
return ret
|
2023-02-15 16:54:39 +00:00
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
-- similar to table.clone but not recursively
|
|
|
|
|
function table.simpleClone(self)
|
|
|
|
|
local ret = {}
|
|
|
|
|
for k, v in pairs(self) do
|
|
|
|
|
ret[k] = v
|
|
|
|
|
end
|
|
|
|
|
return ret
|
2022-04-14 10:22:00 +00:00
|
|
|
|
end
|
|
|
|
|
|
2023-06-08 17:10:16 +00:00
|
|
|
|
-- similar to table.clone but not clone class/instances
|
2023-04-09 03:44:19 +00:00
|
|
|
|
function table.cloneWithoutClass(self)
|
|
|
|
|
local ret = {}
|
|
|
|
|
for k, v in pairs(self) do
|
|
|
|
|
if type(v) == "table" then
|
|
|
|
|
if v.class or v.super then
|
2023-06-08 17:10:16 +00:00
|
|
|
|
ret[k] = v
|
2023-04-09 03:44:19 +00:00
|
|
|
|
else
|
|
|
|
|
ret[k] = table.cloneWithoutClass(v)
|
|
|
|
|
end
|
|
|
|
|
else
|
|
|
|
|
ret[k] = v
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
return ret
|
|
|
|
|
end
|
|
|
|
|
|
2022-09-14 05:01:10 +00:00
|
|
|
|
-- if table does not contain the element, we insert it
|
|
|
|
|
function table:insertIfNeed(element)
|
|
|
|
|
if not table.contains(self, element) then
|
|
|
|
|
table.insert(self, element)
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
2023-08-03 13:08:01 +00:00
|
|
|
|
-- similar to table.insertTable but insertIfNeed inside
|
|
|
|
|
function table:insertTableIfNeed(list)
|
|
|
|
|
for _, e in ipairs(list) do
|
|
|
|
|
table.insertIfNeed(self, e)
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
---@generic T
|
|
|
|
|
---@return T[]
|
|
|
|
|
function table.connect(...)
|
|
|
|
|
local ret = {}
|
|
|
|
|
for _, v in ipairs({...}) do
|
|
|
|
|
table.insertTable(ret, v)
|
|
|
|
|
end
|
|
|
|
|
return ret
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
---@generic T
|
|
|
|
|
---@return T[]
|
|
|
|
|
function table.connectIfNeed(...)
|
|
|
|
|
local ret = {}
|
|
|
|
|
for _, v in ipairs({...}) do
|
|
|
|
|
table.insertTableIfNeed(ret, v)
|
|
|
|
|
end
|
|
|
|
|
return ret
|
|
|
|
|
end
|
|
|
|
|
|
2023-02-26 07:01:14 +00:00
|
|
|
|
---@generic T
|
|
|
|
|
---@param self T[]
|
2023-12-03 11:35:14 +00:00
|
|
|
|
---@param n? integer
|
2023-02-26 07:01:14 +00:00
|
|
|
|
---@return T|T[]
|
2023-03-26 09:32:45 +00:00
|
|
|
|
function table:random(n)
|
2023-03-04 17:28:59 +00:00
|
|
|
|
local n0 = n
|
2023-03-07 02:21:56 +00:00
|
|
|
|
n = n or 1
|
2023-08-03 13:08:01 +00:00
|
|
|
|
if #self == 0 then return n0 ~= nil and {} or nil end
|
2023-03-26 09:32:45 +00:00
|
|
|
|
local tmp = {table.unpack(self)}
|
2023-02-26 07:01:14 +00:00
|
|
|
|
local ret = {}
|
2023-03-01 13:41:16 +00:00
|
|
|
|
while n > 0 and #tmp > 0 do
|
2023-02-26 07:01:14 +00:00
|
|
|
|
local i = math.random(1, #tmp)
|
|
|
|
|
table.insert(ret, table.remove(tmp, i))
|
|
|
|
|
n = n - 1
|
|
|
|
|
end
|
2023-03-07 02:21:56 +00:00
|
|
|
|
return n0 == nil and ret[1] or ret
|
2023-02-26 07:01:14 +00:00
|
|
|
|
end
|
|
|
|
|
|
2023-04-22 06:10:06 +00:00
|
|
|
|
function table:slice(begin, _end)
|
|
|
|
|
local len = #self
|
|
|
|
|
begin = begin or 1
|
|
|
|
|
_end = _end or len + 1
|
|
|
|
|
|
|
|
|
|
if begin <= 0 then begin = len + 1 + begin end
|
|
|
|
|
if _end <= 0 then _end = len + 1 + _end end
|
|
|
|
|
if begin >= _end then return {} end
|
|
|
|
|
|
|
|
|
|
local ret = {}
|
2023-08-10 19:24:22 +00:00
|
|
|
|
for i = math.max(begin, 1), math.min(_end - 1, len), 1 do
|
2023-04-22 06:10:06 +00:00
|
|
|
|
table.insert(ret, self[i])
|
|
|
|
|
end
|
|
|
|
|
return ret
|
|
|
|
|
end
|
|
|
|
|
|
2023-05-20 08:00:03 +00:00
|
|
|
|
function table:assign(targetTbl)
|
|
|
|
|
for key, value in pairs(targetTbl) do
|
|
|
|
|
if self[key] then
|
|
|
|
|
if type(value) == "table" then
|
|
|
|
|
table.insertTable(self[key], value)
|
|
|
|
|
else
|
|
|
|
|
table.insert(self[key], value)
|
|
|
|
|
end
|
|
|
|
|
else
|
|
|
|
|
self[key] = value
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
2023-06-24 04:29:20 +00:00
|
|
|
|
function table.empty(t)
|
|
|
|
|
return next(t) == nil
|
|
|
|
|
end
|
|
|
|
|
|
2023-03-14 06:12:13 +00:00
|
|
|
|
-- allow a = "Hello"; a[1] == "H"
|
|
|
|
|
local str_mt = getmetatable("")
|
|
|
|
|
str_mt.__index = function(str, k)
|
|
|
|
|
if type(k) == "number" then
|
|
|
|
|
if math.abs(k) > str:len() then
|
|
|
|
|
error("string index out of range")
|
|
|
|
|
end
|
|
|
|
|
local start, _end
|
|
|
|
|
if k > 0 then
|
|
|
|
|
start, _end = utf8.offset(str, k), utf8.offset(str, k + 1)
|
|
|
|
|
elseif k < 0 then
|
|
|
|
|
local len = str:len()
|
|
|
|
|
start, _end = utf8.offset(str, len + k + 1), utf8.offset(str, len + k + 2)
|
|
|
|
|
else
|
|
|
|
|
error("str[0] is undefined behavior")
|
|
|
|
|
end
|
|
|
|
|
return str:sub(start, _end - 1)
|
|
|
|
|
end
|
|
|
|
|
return string[k]
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
str_mt.__add = function(a, b)
|
|
|
|
|
return a .. b
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
str_mt.__mul = function(a, b)
|
|
|
|
|
return a:rep(b)
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
-- override default string.len
|
|
|
|
|
string.rawlen = string.len
|
2023-08-10 19:24:22 +00:00
|
|
|
|
---@diagnostic disable-next-line: duplicate-set-field
|
2023-03-14 06:12:13 +00:00
|
|
|
|
function string:len()
|
|
|
|
|
return utf8.len(self)
|
|
|
|
|
end
|
|
|
|
|
|
2022-09-14 05:01:10 +00:00
|
|
|
|
---@param delimiter string
|
|
|
|
|
---@return string[]
|
|
|
|
|
function string:split(delimiter)
|
|
|
|
|
if #self == 0 then return {} end
|
|
|
|
|
local result = {}
|
|
|
|
|
local from = 1
|
|
|
|
|
local delim_from, delim_to = string.find(self, delimiter, from)
|
|
|
|
|
while delim_from do
|
|
|
|
|
table.insert(result, string.sub(self, from, delim_from - 1))
|
|
|
|
|
from = delim_to + 1
|
|
|
|
|
delim_from, delim_to = string.find(self, delimiter, from)
|
|
|
|
|
end
|
|
|
|
|
table.insert(result, string.sub(self, from))
|
|
|
|
|
return result
|
|
|
|
|
end
|
|
|
|
|
|
2023-04-08 12:45:55 +00:00
|
|
|
|
function string:startsWith(start)
|
|
|
|
|
return self:sub(1, #start) == start
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
function string:endsWith(e)
|
|
|
|
|
return e == "" or self:sub(-#e) == e
|
|
|
|
|
end
|
|
|
|
|
|
2022-03-28 14:24:30 +00:00
|
|
|
|
FileIO = {
|
2022-04-30 07:27:56 +00:00
|
|
|
|
pwd = fk.QmlBackend_pwd,
|
|
|
|
|
ls = function(filename)
|
|
|
|
|
if filename == nil then
|
|
|
|
|
return fk.QmlBackend_ls(".")
|
|
|
|
|
else
|
|
|
|
|
return fk.QmlBackend_ls(filename)
|
|
|
|
|
end
|
|
|
|
|
end,
|
|
|
|
|
cd = fk.QmlBackend_cd,
|
|
|
|
|
exists = fk.QmlBackend_exists,
|
|
|
|
|
isDir = fk.QmlBackend_isDir
|
2022-03-28 14:24:30 +00:00
|
|
|
|
}
|
|
|
|
|
|
2023-07-14 14:17:54 +00:00
|
|
|
|
os.getms = function() return fk.GetMicroSecond(fk) end
|
2022-03-30 06:14:40 +00:00
|
|
|
|
|
2022-03-31 05:29:23 +00:00
|
|
|
|
---@class Stack : Object
|
2022-03-28 14:24:30 +00:00
|
|
|
|
Stack = class("Stack")
|
|
|
|
|
function Stack:initialize()
|
2022-04-30 07:27:56 +00:00
|
|
|
|
self.t = {}
|
|
|
|
|
self.p = 0
|
2022-03-28 14:24:30 +00:00
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
function Stack:push(e)
|
2022-04-30 07:27:56 +00:00
|
|
|
|
self.p = self.p + 1
|
|
|
|
|
self.t[self.p] = e
|
2022-03-28 14:24:30 +00:00
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
function Stack:isEmpty()
|
2022-04-30 07:27:56 +00:00
|
|
|
|
return self.p == 0
|
2022-03-28 14:24:30 +00:00
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
function Stack:pop()
|
2022-04-30 07:27:56 +00:00
|
|
|
|
if self.p == 0 then return nil end
|
|
|
|
|
self.p = self.p - 1
|
2024-01-10 14:51:29 +00:00
|
|
|
|
local ret = self.t[self.p + 1]
|
|
|
|
|
self.t[self.p + 1] = nil
|
|
|
|
|
return ret
|
2022-03-28 14:24:30 +00:00
|
|
|
|
end
|
|
|
|
|
|
2022-03-27 06:49:41 +00:00
|
|
|
|
|
2022-04-01 12:51:01 +00:00
|
|
|
|
--- useful function to create enums
|
|
|
|
|
---
|
|
|
|
|
--- only use it in a terminal
|
|
|
|
|
---@param table string
|
2022-03-31 05:29:23 +00:00
|
|
|
|
---@param enum string[]
|
2022-04-01 12:51:01 +00:00
|
|
|
|
function CreateEnum(table, enum)
|
2022-04-30 07:27:56 +00:00
|
|
|
|
local enum_format = "%s.%s = %d"
|
|
|
|
|
for i, v in ipairs(enum) do
|
|
|
|
|
print(string.format(enum_format, table, v, i))
|
|
|
|
|
end
|
2022-03-27 06:49:41 +00:00
|
|
|
|
end
|
2022-04-02 13:39:44 +00:00
|
|
|
|
|
|
|
|
|
function switch(param, case_table)
|
2022-04-30 07:27:56 +00:00
|
|
|
|
local case = case_table[param]
|
|
|
|
|
if case then return case() end
|
|
|
|
|
local def = case_table["default"]
|
|
|
|
|
return def and def() or nil
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
---@class TargetGroup : Object
|
2023-02-26 07:01:14 +00:00
|
|
|
|
local TargetGroup = {}
|
2022-04-30 07:27:56 +00:00
|
|
|
|
|
2023-02-26 07:01:14 +00:00
|
|
|
|
function TargetGroup:getRealTargets(targetGroup)
|
2022-04-30 07:27:56 +00:00
|
|
|
|
if not targetGroup then
|
|
|
|
|
return {}
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
local realTargets = {}
|
|
|
|
|
for _, targets in ipairs(targetGroup) do
|
|
|
|
|
table.insert(realTargets, targets[1])
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
return realTargets
|
|
|
|
|
end
|
|
|
|
|
|
2023-02-26 07:01:14 +00:00
|
|
|
|
function TargetGroup:includeRealTargets(targetGroup, playerId)
|
2022-04-30 07:27:56 +00:00
|
|
|
|
if not targetGroup then
|
|
|
|
|
return false
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
for _, targets in ipairs(targetGroup) do
|
|
|
|
|
if targets[1] == playerId then
|
|
|
|
|
return true
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
return false
|
|
|
|
|
end
|
|
|
|
|
|
2023-02-26 07:01:14 +00:00
|
|
|
|
function TargetGroup:removeTarget(targetGroup, playerId)
|
2022-04-30 07:27:56 +00:00
|
|
|
|
if not targetGroup then
|
|
|
|
|
return
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
for index, targets in ipairs(targetGroup) do
|
|
|
|
|
if (targets[1] == playerId) then
|
|
|
|
|
table.remove(targetGroup, index)
|
|
|
|
|
return
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
2023-02-26 07:01:14 +00:00
|
|
|
|
function TargetGroup:pushTargets(targetGroup, playerIds)
|
2022-04-30 07:27:56 +00:00
|
|
|
|
if not targetGroup then
|
|
|
|
|
return
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
if type(playerIds) == "table" then
|
|
|
|
|
table.insert(targetGroup, playerIds)
|
|
|
|
|
elseif type(playerIds) == "number" then
|
|
|
|
|
table.insert(targetGroup, { playerIds })
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
---@class AimGroup : Object
|
2023-02-26 07:01:14 +00:00
|
|
|
|
local AimGroup = {}
|
2022-04-30 07:27:56 +00:00
|
|
|
|
|
|
|
|
|
AimGroup.Undone = 1
|
|
|
|
|
AimGroup.Done = 2
|
|
|
|
|
AimGroup.Cancelled = 3
|
|
|
|
|
|
2023-02-26 07:01:14 +00:00
|
|
|
|
function AimGroup:initAimGroup(playerIds)
|
2022-04-30 07:27:56 +00:00
|
|
|
|
return { [AimGroup.Undone] = playerIds, [AimGroup.Done] = {}, [AimGroup.Cancelled] = {} }
|
|
|
|
|
end
|
|
|
|
|
|
2023-02-26 07:01:14 +00:00
|
|
|
|
function AimGroup:getAllTargets(aimGroup)
|
2022-04-30 07:27:56 +00:00
|
|
|
|
local targets = {}
|
|
|
|
|
table.insertTable(targets, aimGroup[AimGroup.Undone])
|
|
|
|
|
table.insertTable(targets, aimGroup[AimGroup.Done])
|
|
|
|
|
return targets
|
|
|
|
|
end
|
|
|
|
|
|
2023-02-26 07:01:14 +00:00
|
|
|
|
function AimGroup:getUndoneOrDoneTargets(aimGroup, done)
|
2022-04-30 07:27:56 +00:00
|
|
|
|
return done and aimGroup[AimGroup.Done] or aimGroup[AimGroup.Undone]
|
|
|
|
|
end
|
|
|
|
|
|
2023-02-26 07:01:14 +00:00
|
|
|
|
function AimGroup:setTargetDone(aimGroup, playerId)
|
2022-04-30 07:27:56 +00:00
|
|
|
|
local index = table.indexOf(aimGroup[AimGroup.Undone], playerId)
|
|
|
|
|
if index ~= -1 then
|
|
|
|
|
table.remove(aimGroup[AimGroup.Undone], index)
|
|
|
|
|
table.insert(aimGroup[AimGroup.Done], playerId)
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
2023-02-26 07:01:14 +00:00
|
|
|
|
function AimGroup:addTargets(room, aimEvent, playerIds)
|
2022-04-30 07:27:56 +00:00
|
|
|
|
local playerId = type(playerIds) == "table" and playerIds[1] or playerIds
|
|
|
|
|
table.insert(aimEvent.tos[AimGroup.Undone], playerId)
|
2022-12-20 13:15:49 +00:00
|
|
|
|
|
|
|
|
|
if type(playerIds) == "table" then
|
|
|
|
|
for i = 2, #playerIds do
|
|
|
|
|
aimEvent.subTargets = aimEvent.subTargets or {}
|
|
|
|
|
table.insert(aimEvent.subTargets, playerIds[i])
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
2022-04-30 07:27:56 +00:00
|
|
|
|
room:sortPlayersByAction(aimEvent.tos[AimGroup.Undone])
|
|
|
|
|
if aimEvent.targetGroup then
|
|
|
|
|
TargetGroup:pushTargets(aimEvent.targetGroup, playerIds)
|
|
|
|
|
end
|
2022-04-02 13:39:44 +00:00
|
|
|
|
end
|
2022-04-30 07:27:56 +00:00
|
|
|
|
|
2023-02-26 07:01:14 +00:00
|
|
|
|
function AimGroup:cancelTarget(aimEvent, playerId)
|
2022-04-30 07:27:56 +00:00
|
|
|
|
local cancelled = false
|
|
|
|
|
for status = AimGroup.Undone, AimGroup.Done do
|
|
|
|
|
local indexList = {}
|
|
|
|
|
for index, pId in ipairs(aimEvent.tos[status]) do
|
|
|
|
|
if pId == playerId then
|
|
|
|
|
table.insert(indexList, index)
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
if #indexList > 0 then
|
|
|
|
|
cancelled = true
|
|
|
|
|
for i = 1, #indexList do
|
|
|
|
|
table.remove(aimEvent.tos[status], indexList[i])
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
if cancelled then
|
|
|
|
|
table.insert(aimEvent.tos[AimGroup.Cancelled], playerId)
|
|
|
|
|
if aimEvent.targetGroup then
|
|
|
|
|
TargetGroup:removeTarget(aimEvent.targetGroup, playerId)
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
2023-02-26 07:01:14 +00:00
|
|
|
|
function AimGroup:removeDeadTargets(room, aimEvent)
|
2022-04-30 07:27:56 +00:00
|
|
|
|
for index = AimGroup.Undone, AimGroup.Done do
|
|
|
|
|
aimEvent.tos[index] = room:deadPlayerFilter(aimEvent.tos[index])
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
if aimEvent.targetGroup then
|
|
|
|
|
local targets = TargetGroup:getRealTargets(aimEvent.targetGroup)
|
|
|
|
|
for _, target in ipairs(targets) do
|
|
|
|
|
if not room:getPlayerById(target):isAlive() then
|
|
|
|
|
TargetGroup:removeTarget(aimEvent.targetGroup, target)
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
2023-02-26 07:01:14 +00:00
|
|
|
|
function AimGroup:getCancelledTargets(aimGroup)
|
2022-04-30 07:27:56 +00:00
|
|
|
|
return aimGroup[AimGroup.Cancelled]
|
|
|
|
|
end
|
|
|
|
|
|
2023-04-13 12:17:39 +00:00
|
|
|
|
return { TargetGroup, AimGroup, Util }
|