parent
55703521e9
commit
75d0ad55c8
|
@ -0,0 +1,53 @@
|
|||
-- 针对 core/exppattern.lua 的一些测试用例
|
||||
|
||||
TestExppattern = {
|
||||
testMatchExp = function()
|
||||
local exp1 = Exppattern:Parse("slash,jink")
|
||||
lu.assertTrue(exp1:matchExp("peack,jink"))
|
||||
end,
|
||||
|
||||
testEasyMatchCard = function()
|
||||
local exp1 = Exppattern:Parse("slash,jink")
|
||||
local exp2 = Exppattern:Parse("peach,jink")
|
||||
local slash = Fk:cloneCard("slash")
|
||||
lu.assertTrue(exp1:match(slash))
|
||||
lu.assertFalse(exp2:match(slash))
|
||||
end,
|
||||
|
||||
testMatchWithType = function()
|
||||
local exp3 = Exppattern:Parse(".|.|.|.|.|normal_trick")
|
||||
lu.assertFalse(exp3:matchExp("slash,jink"))
|
||||
lu.assertTrue(exp3:matchExp("peach,ex_nihilo"))
|
||||
|
||||
local basic = Exppattern:Parse(".|.|.|.|.|basic")
|
||||
lu.assertFalse(basic:matchExp("nullification"))
|
||||
lu.assertTrue(basic:matchExp("slash,vine"))
|
||||
lu.assertTrue(Exppattern:Parse(".|.|.|.|.|armor"):matchExp("slash,vine"))
|
||||
lu.assertTrue(Exppattern:Parse(".|.|.|.|.|trick"):matchExp("lightning"))
|
||||
lu.assertFalse(Exppattern:Parse(".|.|.|.|.|delayed_trick"):matchExp("savage_assault"))
|
||||
end,
|
||||
|
||||
testMatchNeg = function()
|
||||
lu.assertError(function() Exppattern:Parse("^(a,|1)") end)
|
||||
local not_nul = Exppattern:Parse("^nullification")
|
||||
local not_slash_jink = Exppattern:Parse("^(slash,jink)")
|
||||
local not_basic = Exppattern:Parse(".|.|.|.|.|^basic")
|
||||
local not_black = Exppattern:Parse(".|.|^(spade,club)")
|
||||
local slash_jink = Exppattern:Parse("slash,jink")
|
||||
local no_slash_jink = Exppattern:Parse("^(slash,jink)|.|.|.|.|basic")
|
||||
local slash = Fk:cloneCard("slash")
|
||||
|
||||
lu.assertFalse(not_nul:matchExp("nullification"))
|
||||
lu.assertTrue(not_basic:matchExp("nullification"))
|
||||
lu.assertFalse(not_slash_jink:matchExp("jink"))
|
||||
lu.assertTrue(not_nul:match(slash))
|
||||
lu.assertFalse(not_slash_jink:match(slash))
|
||||
lu.assertFalse(not_basic:match(slash))
|
||||
lu.assertTrue(not_nul:matchExp("peach"))
|
||||
lu.assertFalse(not_basic:matchExp(no_slash_jink))
|
||||
lu.assertTrue(not_slash_jink:matchExp(not_basic))
|
||||
lu.assertFalse(slash_jink:matchExp(not_slash_jink))
|
||||
lu.assertFalse(not_black:matchExp("slash|A~Q|spade"))
|
||||
lu.assertTrue(not_black:matchExp("vine|10|^club"))
|
||||
end,
|
||||
}
|
|
@ -0,0 +1,24 @@
|
|||
---@diagnostic disable
|
||||
|
||||
local testmode = GameMode:new("testmode", 2, 8)
|
||||
testmode.logic = function()
|
||||
---@type GameLogic
|
||||
local l = GameLogic:subclass("testmodelogic")
|
||||
--[[
|
||||
function l:chooseGenerals()
|
||||
local room = self.room
|
||||
room.current = room.players[1]
|
||||
for _, p in ipairs(room.players) do
|
||||
room:setPlayerGeneral(p, "blank_shibing")
|
||||
end
|
||||
end
|
||||
--]]
|
||||
function l:action()
|
||||
if type(self.room.action) == "function" then
|
||||
self.room.action()
|
||||
end
|
||||
end
|
||||
return l
|
||||
end
|
||||
Fk.game_modes["testmode"] = testmode
|
||||
Fk:loadDisabled()
|
|
@ -0,0 +1,28 @@
|
|||
-- 针对 core/util.lua 的一些测试用例
|
||||
|
||||
-- 总感觉没啥好测试的
|
||||
|
||||
TestUtil = {
|
||||
testMisc = function()
|
||||
lu.assertError(function()
|
||||
Util.DummyTable.a = 4
|
||||
end)
|
||||
end,
|
||||
|
||||
testString = function()
|
||||
lu.assertIs("He" + "is", "Heis")
|
||||
local utf8string = "刘备,天下枭雄"
|
||||
lu.assertEquals(utf8string:len(), 7)
|
||||
lu.assertEquals(utf8string:rawlen(), 21)
|
||||
lu.assertEquals(#utf8string, 21)
|
||||
|
||||
local s = "gfsdf%kj.\\ts4!!,34':"
|
||||
lu.assertFalse(s:endsWith("%"))
|
||||
end,
|
||||
|
||||
testTable = function()
|
||||
local t = {1, 2, 5}
|
||||
table.insertIfNeed(t, 2)
|
||||
lu.assertEquals(t, {1, 2, 5})
|
||||
end,
|
||||
}
|
|
@ -1,6 +1,7 @@
|
|||
-- 为纯lua的测试环境捏一个虚拟的fk以便于测试
|
||||
|
||||
local fk = {}
|
||||
local testFail = false
|
||||
|
||||
local os, io = os, io
|
||||
|
||||
|
@ -20,4 +21,53 @@ function fk.QmlBackend_exists(dir)
|
|||
return f:read("*a"):startsWith("OK")
|
||||
end
|
||||
|
||||
function fk.GetDisabledPacks()
|
||||
return "[]"
|
||||
--[[
|
||||
local pkgs = fk.QmlBackend_ls("packages")
|
||||
table.removeOne(pkgs, "test")
|
||||
return json.encode(pkgs)
|
||||
--]]
|
||||
end
|
||||
|
||||
function fk.qCritical(msg) print(string.char(27) .. "[91m[Test/C]" ..
|
||||
string.char(27) .. "[0m " .. msg); testFail = true end
|
||||
function fk.qInfo(msg) print(string.char(27) .. "[95m[Test/I]" ..
|
||||
string.char(27) .. "[0m " .. msg) end
|
||||
function fk.qWarning(msg) print(string.char(27) .. "[94m[Test/W]" ..
|
||||
string.char(27) .. "[0m " .. msg) end
|
||||
function fk.qDebug(msg) print(string.char(27) .. "[90m[Test/D]" ..
|
||||
string.char(27) .. "[0m " .. msg) end
|
||||
|
||||
function fk.GetMicroSecond()
|
||||
return os.time() * 100000
|
||||
end
|
||||
|
||||
function fk.roomtest(croom, f)
|
||||
local room = Room(croom)
|
||||
RoomInstance = room
|
||||
room.action = function() f(room) end
|
||||
while true do
|
||||
local over = room:resume()
|
||||
if over then break else room.in_delay = false end
|
||||
end
|
||||
RoomInstance = nil
|
||||
local fail = testFail
|
||||
if fail then testFail = false end
|
||||
lu.assertFalse(fail, "Test failed!")
|
||||
end
|
||||
|
||||
-- terminal color
|
||||
fk.BOLD = string.char(27) .. "[1m"
|
||||
fk.GRAY = string.char(27) .. "[90m"
|
||||
fk.RED = string.char(27) .. "[91m"
|
||||
fk.GREEN = string.char(27) .. "[92m"
|
||||
fk.BLUE = string.char(27) .. "[94m"
|
||||
fk.YELLOW = string.char(27) .. "[93m"
|
||||
fk.DEEPBLUE = string.char(27) .. "[34m"
|
||||
fk.PURPLE = string.char(27) .. "[95m"
|
||||
fk.CYAN = string.char(27) .. "[96m"
|
||||
fk.RST = string.char(27) .. "[0m"
|
||||
fk.CARET = string.char(27) .. "[92m => ".. fk.RST
|
||||
|
||||
return fk
|
||||
|
|
|
@ -0,0 +1,23 @@
|
|||
-- 仿效 swig 中 class Room 的接口制作,为了便于测试
|
||||
|
||||
local Room = class("fk.Room")
|
||||
|
||||
function Room:getId() return 1 end
|
||||
function Room:getPlayers() return self.players end
|
||||
function Room:getTimeout() return 15 end
|
||||
function Room:updateWinRate() end
|
||||
function Room:gameOver() end
|
||||
function Room:settings()
|
||||
return json.encode{
|
||||
enableFreeAssign = false,
|
||||
enableDeputy = false,
|
||||
gameMode = "testmode",
|
||||
disabledPack = {},
|
||||
generalNum = 2,
|
||||
luckTime = 0,
|
||||
password = "",
|
||||
disabledGenerals = {},
|
||||
}
|
||||
end
|
||||
|
||||
return Room
|
|
@ -0,0 +1,612 @@
|
|||
-- 仿效 swig 中 class ServerPlayer 的接口制作,为了便于测试
|
||||
|
||||
local ServerPlayer = class("fk.ServerPlayer")
|
||||
local io = fk.io
|
||||
|
||||
fk.Player_Invalid = 0
|
||||
fk.Player_Online = 1
|
||||
fk.Player_Trust = 2
|
||||
fk.Player_Run = 3
|
||||
fk.Player_Leave = 4
|
||||
fk.Player_Robot = 5
|
||||
fk.Player_Offline = 6
|
||||
|
||||
local function colorConvert(log)
|
||||
log = log:gsub('<font color="#0598BC"><b>', string.char(27) .. "[34;1m")
|
||||
log = log:gsub('<font color="#0C8F0C"><b>', string.char(27) .. "[32;1m")
|
||||
log = log:gsub('<font color="#CC3131"><b>', string.char(27) .. "[31;1m")
|
||||
log = log:gsub('<font color="red"><b>', string.char(27) .. "[31;1m")
|
||||
log = log:gsub('<font color="black"><b>', string.char(27) .. "[0;1m")
|
||||
log = log:gsub('<font color="#0598BC">', string.char(27) .. "[34m")
|
||||
log = log:gsub('<font color="blue">', string.char(27) .. "[34m")
|
||||
log = log:gsub('<font color="#0C8F0C">', string.char(27) .. "[32m")
|
||||
log = log:gsub('<font color="green">', string.char(27) .. "[32m")
|
||||
log = log:gsub('<font color="#CC3131">', string.char(27) .. "[31m")
|
||||
log = log:gsub('<font color="red">', string.char(27) .. "[31m")
|
||||
log = log:gsub('<font color="grey">', string.char(27) .. "[90m")
|
||||
log = log:gsub("<b>", fk.BOLD)
|
||||
log = log:gsub("</b></font>", fk.RST)
|
||||
log = log:gsub("</font>", fk.RST)
|
||||
log = log:gsub("<b>", fk.BOLD)
|
||||
log = log:gsub("</b>", fk.RST)
|
||||
|
||||
log = log:gsub("<br>", "\n")
|
||||
log = log:gsub("<br/>", "\n")
|
||||
log = log:gsub("<br />", "\n")
|
||||
return log
|
||||
end
|
||||
|
||||
---@param msg LogMessage
|
||||
local function parseMsg(msg, nocolor)
|
||||
local self = Fk:currentRoom()
|
||||
local data = msg
|
||||
local function getPlayerStr(pid, color)
|
||||
if nocolor then color = "white" end
|
||||
if not pid then
|
||||
return ""
|
||||
end
|
||||
local p = self:getPlayerById(pid)
|
||||
local str = '<font color="%s"><b>%s</b></font>'
|
||||
if p.general == "anjiang" and (p.deputyGeneral == "anjiang"
|
||||
or not p.deputyGeneral) then
|
||||
local ret = Fk:translate("seat#" .. p.seat)
|
||||
return string.format(str, color, ret)
|
||||
end
|
||||
|
||||
local ret = p.general
|
||||
ret = Fk:translate(ret)
|
||||
if p.deputyGeneral and p.deputyGeneral ~= "" then
|
||||
ret = ret .. "/" .. Fk:translate(p.deputyGeneral)
|
||||
end
|
||||
ret = string.format(str, color, ret)
|
||||
return ret
|
||||
end
|
||||
|
||||
local from = getPlayerStr(data.from, "#0C8F0C")
|
||||
|
||||
local to = data.to or Util.DummyTable
|
||||
local to_str = {}
|
||||
for _, id in ipairs(to) do
|
||||
table.insert(to_str, getPlayerStr(id, "#CC3131"))
|
||||
end
|
||||
to = table.concat(to_str, ", ")
|
||||
|
||||
local card = data.card or Util.DummyTable
|
||||
local allUnknown = true
|
||||
local unknownCount = 0
|
||||
for _, id in ipairs(card) do
|
||||
if id ~= -1 then
|
||||
allUnknown = false
|
||||
else
|
||||
unknownCount = unknownCount + 1
|
||||
end
|
||||
end
|
||||
|
||||
if allUnknown then
|
||||
card = ""
|
||||
else
|
||||
local card_str = {}
|
||||
for _, id in ipairs(card) do
|
||||
table.insert(card_str, Fk:getCardById(id, true):toLogString())
|
||||
end
|
||||
if unknownCount > 0 then
|
||||
table.insert(card_str, Fk:translate("unknown_card")
|
||||
.. unknownCount == 1 and "x" .. unknownCount or "")
|
||||
end
|
||||
card = table.concat(card_str, ", ")
|
||||
end
|
||||
|
||||
local function parseArg(arg)
|
||||
arg = arg or ""
|
||||
arg = Fk:translate(arg)
|
||||
arg = string.format('<font color="%s"><b>%s</b></font>', nocolor and "white" or "#0598BC", arg)
|
||||
return arg
|
||||
end
|
||||
|
||||
local arg = parseArg(data.arg)
|
||||
local arg2 = parseArg(data.arg2)
|
||||
local arg3 = parseArg(data.arg3)
|
||||
|
||||
local log = Fk:translate(data.type)
|
||||
log = string.gsub(log, "%%from", from)
|
||||
log = string.gsub(log, "%%to", to)
|
||||
log = string.gsub(log, "%%card", card)
|
||||
log = string.gsub(log, "%%arg2", arg2)
|
||||
log = string.gsub(log, "%%arg3", arg3)
|
||||
log = string.gsub(log, "%%arg", arg)
|
||||
|
||||
return colorConvert(log)
|
||||
end
|
||||
|
||||
local function processPrompt(prompt)
|
||||
local data = prompt:split(":")
|
||||
local room = Fk:currentRoom()
|
||||
local raw = Fk:translate(data[1]);
|
||||
local src = tonumber(data[2]);
|
||||
local dest = tonumber(data[3]);
|
||||
if src then raw = raw:gsub("%%src", Fk:translate(room:getPlayerById(src).general)) end
|
||||
if dest then raw = raw:gsub("%%dest", Fk:translate(room:getPlayerById(dest).general)) end
|
||||
if data[5] then raw = raw:gsub("%%arg2", Fk:translate(data[5])) end
|
||||
if data[4] then raw = raw:gsub("%%arg", Fk:translate(data[4])) end
|
||||
return colorConvert(raw)
|
||||
end
|
||||
|
||||
--- separated moves to many moves(one card per move)
|
||||
---@param moves CardsMoveStruct[]
|
||||
local function separateMoves(moves)
|
||||
local ret = {} ---@type CardsMoveInfo[]
|
||||
for _, move in ipairs(moves) do
|
||||
for _, info in ipairs(move.moveInfo) do
|
||||
table.insert(ret, {
|
||||
ids = {info.cardId},
|
||||
from = move.from,
|
||||
to = move.to,
|
||||
toArea = move.toArea,
|
||||
fromArea = info.fromArea,
|
||||
moveReason = move.moveReason,
|
||||
specialName = move.specialName,
|
||||
fromSpecialName = info.fromSpecialName,
|
||||
proposer = move.proposer,
|
||||
})
|
||||
end
|
||||
end
|
||||
return ret
|
||||
end
|
||||
|
||||
--- merge separated moves that information is the same
|
||||
local function mergeMoves(moves)
|
||||
local ret = {}
|
||||
local temp = {}
|
||||
for _, move in ipairs(moves) do
|
||||
local info = string.format("%q,%q,%q,%q,%s,%s,%q",
|
||||
move.from, move.to, move.fromArea, move.toArea,
|
||||
move.specialName, move.fromSpecialName, move.proposer)
|
||||
if temp[info] == nil then
|
||||
temp[info] = {
|
||||
ids = {},
|
||||
from = move.from,
|
||||
to = move.to,
|
||||
fromArea = move.fromArea,
|
||||
toArea = move.toArea,
|
||||
moveReason = move.moveReason,
|
||||
specialName = move.specialName,
|
||||
fromSpecialName = move.fromSpecialName,
|
||||
proposer = move.proposer,
|
||||
}
|
||||
end
|
||||
table.insert(temp[info].ids, move.ids[1])
|
||||
end
|
||||
for _, v in pairs(temp) do
|
||||
table.insert(ret, v)
|
||||
end
|
||||
return ret
|
||||
end
|
||||
|
||||
local function sendMoveCardLog(move)
|
||||
local client = Fk:currentRoom() ---@class Client
|
||||
if #move.ids == 0 then return end
|
||||
local hidden = table.contains(move.ids, -1)
|
||||
local msgtype
|
||||
|
||||
if move.toArea == Card.PlayerHand then
|
||||
if move.fromArea == Card.PlayerSpecial then
|
||||
print(parseMsg({
|
||||
type = "$GetCardsFromPile",
|
||||
from = move.to,
|
||||
arg = move.fromSpecialName,
|
||||
arg2 = #move.ids,
|
||||
card = move.ids,
|
||||
}))
|
||||
elseif move.fromArea == Card.DrawPile then
|
||||
print(parseMsg({
|
||||
type = "$DrawCards",
|
||||
from = move.to,
|
||||
card = move.ids,
|
||||
arg = #move.ids,
|
||||
}))
|
||||
elseif move.fromArea == Card.Processing then
|
||||
print(parseMsg({
|
||||
type = "$GotCardBack",
|
||||
from = move.to,
|
||||
card = move.ids,
|
||||
arg = #move.ids,
|
||||
}))
|
||||
elseif move.fromArea == Card.DiscardPile then
|
||||
print(parseMsg({
|
||||
type = "$RecycleCard",
|
||||
from = move.to,
|
||||
card = move.ids,
|
||||
arg = #move.ids,
|
||||
}))
|
||||
elseif move.from then
|
||||
print(parseMsg({
|
||||
type = "$MoveCards",
|
||||
from = move.from,
|
||||
to = { move.to },
|
||||
arg = #move.ids,
|
||||
card = move.ids,
|
||||
}))
|
||||
else
|
||||
print(parseMsg({
|
||||
type = "$PreyCardsFromPile",
|
||||
from = move.to,
|
||||
card = move.ids,
|
||||
arg = #move.ids,
|
||||
}))
|
||||
end
|
||||
elseif move.toArea == Card.PlayerEquip then
|
||||
print(parseMsg({
|
||||
type = "$InstallEquip",
|
||||
from = move.to,
|
||||
card = move.ids,
|
||||
}))
|
||||
elseif move.toArea == Card.PlayerJudge then
|
||||
if move.from ~= move.to and move.fromArea == Card.PlayerJudge then
|
||||
print(parseMsg({
|
||||
type = "$LightningMove",
|
||||
from = move.from,
|
||||
to = { move.to },
|
||||
card = move.ids,
|
||||
}))
|
||||
elseif move.from then
|
||||
print(parseMsg({
|
||||
type = "$PasteCard",
|
||||
from = move.from,
|
||||
to = { move.to },
|
||||
card = move.ids,
|
||||
}))
|
||||
end
|
||||
elseif move.toArea == Card.PlayerSpecial then
|
||||
print(parseMsg({
|
||||
type = "$AddToPile",
|
||||
arg = move.specialName,
|
||||
arg2 = #move.ids,
|
||||
from = move.to,
|
||||
card = move.ids,
|
||||
}))
|
||||
elseif move.fromArea == Card.PlayerEquip then
|
||||
print(parseMsg({
|
||||
type = "$UninstallEquip",
|
||||
from = move.from,
|
||||
card = move.ids,
|
||||
}))
|
||||
-- elseif move.toArea == Card.Processing then
|
||||
-- nop
|
||||
elseif move.from and move.toArea == Card.DrawPile then
|
||||
msgtype = hidden and "$PutCard" or "$PutKnownCard"
|
||||
print(parseMsg({
|
||||
type = msgtype,
|
||||
from = move.from,
|
||||
card = move.ids,
|
||||
arg = #move.ids,
|
||||
}))
|
||||
elseif move.toArea == Card.DiscardPile then
|
||||
if move.moveReason == fk.ReasonDiscard then
|
||||
if move.proposer and move.proposer ~= move.from then
|
||||
print(parseMsg({
|
||||
type = "$DiscardOther",
|
||||
from = move.from,
|
||||
to = {move.proposer},
|
||||
card = move.ids,
|
||||
arg = #move.ids,
|
||||
}))
|
||||
else
|
||||
print(parseMsg({
|
||||
type = "$DiscardCards",
|
||||
from = move.from,
|
||||
card = move.ids,
|
||||
arg = #move.ids,
|
||||
}))
|
||||
end
|
||||
elseif move.moveReason == fk.ReasonPutIntoDiscardPile then
|
||||
print(parseMsg({
|
||||
type = "$PutToDiscard",
|
||||
card = move.ids,
|
||||
arg = #move.ids,
|
||||
}))
|
||||
end
|
||||
-- elseif move.toArea == Card.Void then
|
||||
-- nop
|
||||
end
|
||||
end
|
||||
|
||||
function ServerPlayer:initialize(id)
|
||||
self.id = id
|
||||
self.screenName = "player" .. id
|
||||
self.state = fk.Player_Online
|
||||
self.died = false
|
||||
self._busy = false
|
||||
self._thinking = false
|
||||
end
|
||||
|
||||
function ServerPlayer:getId() return self.id end
|
||||
function ServerPlayer:setId(id) self.id = id end
|
||||
function ServerPlayer:getScreenName() return self.screenName end
|
||||
function ServerPlayer:getAvatar() return "zhouyu" end
|
||||
function ServerPlayer:getState() return self.state end
|
||||
function ServerPlayer:setState(state) self.state = state end
|
||||
function ServerPlayer:isDied() return self.died end
|
||||
function ServerPlayer:setDied(died) self.died = died end
|
||||
|
||||
local function tr(str)
|
||||
return string.format("%s(%s)", Fk:translate(str), str)
|
||||
end
|
||||
|
||||
local function trcid(cid)
|
||||
local card = Fk:getCardById(cid)
|
||||
return colorConvert(card:toLogString()) .. "(" .. cid .. ")"
|
||||
end
|
||||
|
||||
local function help_rp_yn()
|
||||
print(fk.GRAY .." (reply格式:n或N表示取消,其余确定,如reply n)" .. fk.RST)
|
||||
end
|
||||
|
||||
local function help_rp_choices()
|
||||
print(fk.GRAY .." (reply格式:直接输入文本,有多个则用空格分隔,如:reply kill)" .. fk.RST)
|
||||
end
|
||||
|
||||
local request_processors = {
|
||||
["AskForGeneral"] = function(j)
|
||||
local data = json.decode(j)
|
||||
io.write(string.format("请选择 %d 名武将: ", data[2]))
|
||||
for _, g in ipairs(data[1]) do
|
||||
io.write(tr(g) .. " ")
|
||||
end
|
||||
io.write("\n")
|
||||
help_rp_choices()
|
||||
end,
|
||||
["PlayCard"] = function()
|
||||
print("出牌阶段,请进行操作")
|
||||
end,
|
||||
["AskForSkillInvoke"] = function(j)
|
||||
local data = json.decode(j)
|
||||
local skill = data[1]
|
||||
local prompt = data[2]
|
||||
if prompt then prompt = processPrompt(prompt)
|
||||
else prompt = string.format("你是否发动技能 %s", tr(skill)) end
|
||||
print(prompt)
|
||||
help_rp_yn()
|
||||
end,
|
||||
["AskForUseCard"] = function(j)
|
||||
local cardname, pattern, prompt, _, extra_data, disabledSkillNames =
|
||||
table.unpack(json.decode(j))
|
||||
if prompt then prompt = processPrompt(prompt)
|
||||
else prompt = string.format("请使用卡牌 %s", tr(cardname)) end
|
||||
print(prompt)
|
||||
end,
|
||||
["AskForUseActiveSkill"] = function(j)
|
||||
local skill_name, prompt, cancelable, extra_data =
|
||||
table.unpack(json.decode(j))
|
||||
if prompt then prompt = processPrompt(prompt)
|
||||
else prompt = string.format("请使用技能 %s", tr(skill_name)) end
|
||||
print(prompt)
|
||||
end,
|
||||
}
|
||||
function ServerPlayer:doRequest(cmd, j)
|
||||
if self.id == 1 then
|
||||
io.write(fk.YELLOW .. "[!] " .. fk.RST)
|
||||
if request_processors[cmd] then
|
||||
request_processors[cmd](j)
|
||||
else
|
||||
print(cmd, j)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local cmd_help = function()
|
||||
io.write(""
|
||||
.. fk.BLUE .." <回车>".. fk.CARET .."重复执行上一条命令\n"
|
||||
.. fk.BLUE .." help/h".. fk.CARET .."查看这条帮助\n"
|
||||
.. fk.BLUE .." dbg".. fk.CARET .."使用Debugger\n"
|
||||
.. fk.BLUE .." reply/rp".. fk.CARET .."发送答复,需手搓JSON除非有特殊提示,注意无合法性检测\n"
|
||||
.. fk.GRAY .." -------------------------------\n" .. fk.RST
|
||||
.. fk.BLUE .." room/r".. fk.CARET .."查看房间概况\n"
|
||||
.. fk.BLUE .." player/p".. fk.CARET .."查看玩家概况,参数为玩家id默认1\n"
|
||||
.. fk.BLUE .." skill/s".. fk.CARET .."查看技能描述\n"
|
||||
)
|
||||
end
|
||||
|
||||
local reply_processors = {
|
||||
["AskForGeneral"] = function(args)
|
||||
return json.encode(args)
|
||||
end,
|
||||
["AskForSkillInvoke"] = function(args)
|
||||
local ret = args[1]
|
||||
if ret == 'n' or ret == 'N' then
|
||||
return ''
|
||||
end
|
||||
return '1'
|
||||
end,
|
||||
}
|
||||
local cmd_reply = function(args)
|
||||
if #args == 0 then return "" end
|
||||
local room = Fk:currentRoom()
|
||||
local player = room:getPlayerById(1)
|
||||
local command = player.ai_data.command
|
||||
if reply_processors[command] then
|
||||
return reply_processors[command](args)
|
||||
else
|
||||
return args[1]
|
||||
end
|
||||
end
|
||||
|
||||
local function getRoleStr(str)
|
||||
if str == "lord" then
|
||||
return fk.RED .. fk.BOLD .. "主" .. fk.RST
|
||||
elseif str == "loyalist" then
|
||||
return fk.YELLOW .. fk.BOLD .. "忠" .. fk.RST
|
||||
elseif str == "rebel" then
|
||||
return fk.GREEN .. fk.BOLD .. "反" .. fk.RST
|
||||
elseif str == "renegade" then
|
||||
return fk.BLUE .. fk.BOLD .. "内" .. fk.RST
|
||||
end
|
||||
end
|
||||
|
||||
local function writeCardList(cidlist)
|
||||
for _, id in ipairs(cidlist) do
|
||||
io.write(trcid(id))
|
||||
io.write(" ")
|
||||
end
|
||||
end
|
||||
|
||||
local cmd_room = function()
|
||||
local room = Fk:currentRoom()
|
||||
if not room.players[3].shield then return end
|
||||
printf("第%d轮 牌堆剩%d张", room.tag['RoundCount'], #room.draw_pile)
|
||||
print("\n玩家列表:")
|
||||
for _, p in ipairs(room.players) do
|
||||
io.write(string.format("%s%d ID=%d %s %s %d|%d/%d %d牌",
|
||||
p.id == 1 and "*" or "", p.seat, p.id, getRoleStr(p.role),
|
||||
(p.dead and fk.GRAY or fk.GREEN) .. Fk:translate(p.general) .. fk.RST,
|
||||
p.shield, p.hp, p.maxHp, #p.player_cards[Player.Hand]))
|
||||
if #p.player_cards[Player.Equip] > 0 then io.write(" 有装备") end
|
||||
if #p.player_cards[Player.Judge] > 0 then io.write(" 有判定") end
|
||||
io.write("\n")
|
||||
end
|
||||
--[[
|
||||
print("\n摸牌堆:")
|
||||
writeCardList(room.draw_pile)
|
||||
print("\n弃牌堆:")
|
||||
writeCardList(room.discard_pile)
|
||||
print("\nVoid牌堆:")
|
||||
writeCardList(room.void)
|
||||
--]]
|
||||
end
|
||||
|
||||
local cmd_player = function(args)
|
||||
local room = Fk:currentRoom()
|
||||
if #args == 0 then table.insert(args, "1") end
|
||||
for _, sid in ipairs(args) do
|
||||
local p = room:getPlayerById(tonumber(sid))
|
||||
io.write(fk.BOLD .. tostring(p) .. fk.RST .. " " .. getRoleStr(p.role))
|
||||
if p.general and p.general ~= "" then
|
||||
io.write(" " .. tr(p.general))
|
||||
else
|
||||
io.write("\n"); return
|
||||
end
|
||||
if p.deputyGeneral and p.deputyGeneral ~= "" then
|
||||
io.write("/" .. tr(p.deputyGeneral))
|
||||
end
|
||||
io.write(string.format(" HP: %d|%d/%d", p.shield, p.hp, p.maxHp))
|
||||
if p.dead then io.write(" 已死亡") end
|
||||
io.write("\n")
|
||||
|
||||
if #p.player_cards[Player.Hand] > 0 then
|
||||
io.write(string.format("共%d张手牌: ", #p.player_cards[Player.Hand]))
|
||||
writeCardList(p.player_cards[Player.Hand])
|
||||
io.write("\n")
|
||||
else
|
||||
print("没有手牌")
|
||||
end
|
||||
if #p.player_cards[Player.Equip] > 0 then
|
||||
io.write("装备区内的牌: ")
|
||||
writeCardList(p.player_cards[Player.Equip])
|
||||
io.write("\n")
|
||||
end
|
||||
if #p.player_cards[Player.Judge] > 0 then
|
||||
io.write("判定区内的牌: ")
|
||||
writeCardList(p.player_cards[Player.Judge])
|
||||
io.write("\n")
|
||||
end
|
||||
|
||||
io.write("技能:")
|
||||
for _, s in ipairs(p.player_skills) do
|
||||
if s.visible then io.write(tr(s.name) .. " ") end
|
||||
end
|
||||
io.write("\n")
|
||||
end
|
||||
end
|
||||
|
||||
local cmd_skill = function(args)
|
||||
for _, s in ipairs(args) do
|
||||
print(fk.BOLD .. tr(s) .. fk.RST)
|
||||
print(colorConvert(Fk:getDescription(s)))
|
||||
end
|
||||
end
|
||||
|
||||
local cmd_card = function(args)
|
||||
for _, s in ipairs(args) do
|
||||
local cid = tonumber(s)
|
||||
if not cid then return end
|
||||
local c = Fk:getCardById(cid, true)
|
||||
if not c then return end
|
||||
print(fk.BOLD .. trcid(cid) .. fk.RST)
|
||||
print(colorConvert(Fk:getDescription(c.name)))
|
||||
end
|
||||
end
|
||||
|
||||
local command_table = {
|
||||
help = cmd_help, h = cmd_help,
|
||||
dbg = function() dbg() end,
|
||||
|
||||
reply = cmd_reply, rp = cmd_reply,
|
||||
room = cmd_room, r = cmd_room,
|
||||
player = cmd_player, p = cmd_player,
|
||||
skill = cmd_skill, s = cmd_skill,
|
||||
card = cmd_card, c = cmd_card,
|
||||
}
|
||||
local last_cmd = "help"
|
||||
|
||||
function ServerPlayer:waitForReply()
|
||||
-- dbg() 时的便利变量
|
||||
local room = RoomInstance
|
||||
local logic = room.logic
|
||||
local player = room:getPlayerById(self.id)
|
||||
if self.id == 1 then
|
||||
while true do
|
||||
io.write(string.char(27) .. "[95m(FkTest) " .. fk.RST)
|
||||
io.flush()
|
||||
local line = io.read()
|
||||
if line == nil then break end -- Ctrl-D
|
||||
local args = line:split(" ")
|
||||
for i = #args, 1, -1 do
|
||||
if args[i] == "" then table.remove(args, i) end
|
||||
end
|
||||
|
||||
local command = table.remove(args, 1)
|
||||
if command == nil then
|
||||
command = last_cmd
|
||||
else
|
||||
last_cmd = command
|
||||
end
|
||||
local f = command_table[command]
|
||||
if f then
|
||||
local ret = f(args)
|
||||
if ret then return ret end
|
||||
elseif command then
|
||||
print(fk.RED .. "unknown command '" .. command .. "'" .. fk.RST)
|
||||
end
|
||||
end
|
||||
end
|
||||
return ""
|
||||
end
|
||||
function ServerPlayer:doNotify(cmd, j)
|
||||
if self.id ~= 1 then
|
||||
return
|
||||
end
|
||||
if cmd == "GameLog" then
|
||||
print(parseMsg(json.decode(j)))
|
||||
elseif cmd == "MoveCards" then
|
||||
local raw_moves = json.decode(j)
|
||||
local separated = separateMoves(raw_moves)
|
||||
local merged = mergeMoves(separated)
|
||||
for _, move in ipairs(merged) do
|
||||
sendMoveCardLog(move)
|
||||
end
|
||||
elseif cmd == "GameOver" then
|
||||
print(cmd, j)
|
||||
end
|
||||
end
|
||||
function ServerPlayer:busy() return self._busy end
|
||||
function ServerPlayer:setBusy(b) self._busy = b end
|
||||
function ServerPlayer:thinking() return self._thinking end
|
||||
function ServerPlayer:setThinking(t) self._thinking = t end
|
||||
function ServerPlayer:emitKick()
|
||||
self.state = fk.Player_Offline
|
||||
end
|
||||
function ServerPlayer:getGameData()
|
||||
return {[0]=0,0,0,0,at=function(t,k)return t[k]end}
|
||||
end
|
||||
|
||||
return ServerPlayer
|
||||
|
|
@ -1,20 +1,6 @@
|
|||
TestExppattern = {
|
||||
testUtil = function()
|
||||
local table1 = {1, 3, 5, 8}
|
||||
local table2 = {2, 3, 5, 7}
|
||||
p(table1)
|
||||
p(table2)
|
||||
p(table.connect(table1, table2))
|
||||
p(table1)
|
||||
p(table2)
|
||||
p(table.connectIfNeed(table1, table2))
|
||||
p(table1)
|
||||
p(table2)
|
||||
p(table.slice(table1,3,4))
|
||||
p(table.slice(table1,1,6))
|
||||
p(table.slice(table1,-2,-1))
|
||||
end,
|
||||
-- 针对 core/exppattern.lua 的一些测试用例
|
||||
|
||||
TestExppattern = {
|
||||
testMatchExp = function()
|
||||
local exp1 = Exppattern:Parse("slash,jink")
|
||||
lu.assertTrue(exp1:matchExp("peack,jink"))
|
||||
|
@ -64,5 +50,4 @@ TestExppattern = {
|
|||
lu.assertFalse(not_black:matchExp("slash|A~Q|spade"))
|
||||
lu.assertTrue(not_black:matchExp("vine|10|^club"))
|
||||
end,
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,41 @@
|
|||
-- 在命令行中玩单机版FK吧!在游戏目录下 lua test/lua/play.lua
|
||||
-- 只能在Linux或是Windows-MSYS2之类的环境运行
|
||||
---@diagnostic disable: lowercase-global
|
||||
|
||||
package.path = package.path .. ";./test/lua/lib/?.lua"
|
||||
|
||||
lu = require('luaunit')
|
||||
fk = require('fk')
|
||||
fk.os = os
|
||||
fk.io = io
|
||||
|
||||
local banner =
|
||||
fk.CYAN .. [[ ______ __ __ _ ____]] .. fk.RST .. "\n" ..
|
||||
fk.CYAN .. [[ / ____/_______ ___ / //_/(_) / /]] .. fk.RST .. "\n" ..
|
||||
fk.CYAN .. [[ / /_ / ___/ _ \/ _ \/ ,< / / / / ]] .. fk.RST .. " 命令行版本新月杀,仅供测试用\n" ..
|
||||
fk.BLUE .. [[ / __/ / / / __/ __/ /| |/ / / / ]] .. fk.RST .. "默认五人测试模式,请手动修改相关Lua文件\n" ..
|
||||
fk.BLUE .. [[/_/ /_/ \___/\___/_/ |_/_/_/_/ ]] .. fk.RST .. "\n"
|
||||
print(banner)
|
||||
|
||||
-- load FreeKill core
|
||||
dofile 'lua/freekill.lua'
|
||||
fk.qlist = ipairs
|
||||
dofile 'lua/client/i18n/init.lua'
|
||||
|
||||
-- load test cases
|
||||
dofile 'test/lua/core/util.lua'
|
||||
dofile 'test/lua/core/pattern.lua'
|
||||
dofile 'test/lua/core/testmode.lua'
|
||||
|
||||
-- server tests
|
||||
dofile 'lua/server/scheduler.lua'
|
||||
Room = require 'server.room'
|
||||
fk.Room = require 'test/lua/lib/room'
|
||||
fk.ServerPlayer = require 'test/lua/lib/serverplayer'
|
||||
|
||||
dofile 'test/lua/server/scheduler.lua'
|
||||
dofile 'test/lua/server/logic.lua'
|
||||
|
||||
_TestGameLogic.setup()
|
||||
_TestGameLogic.testTrigger()
|
||||
_TestGameLogic.tearDown()
|
|
@ -1,14 +1,37 @@
|
|||
-- Run tests with `lua5.4 test/lua/run.lua`
|
||||
-- Can only run under Linux
|
||||
|
||||
---@diagnostic disable: lowercase-global
|
||||
|
||||
package.path = package.path .. ";./test/lua/lib/?.lua"
|
||||
|
||||
lu = require('luaunit')
|
||||
local os = os
|
||||
fk = require('fk')
|
||||
function fk.GetDisabledPacks()
|
||||
local pkgs = fk.QmlBackend_ls("packages")
|
||||
table.removeOne(pkgs, "test")
|
||||
return json.encode(pkgs)
|
||||
end
|
||||
fk.os = os
|
||||
fk.io = io
|
||||
|
||||
-- load FreeKill core
|
||||
dofile 'lua/freekill.lua'
|
||||
dofile 'test/lua/pattern.lua'
|
||||
fk.qlist = ipairs
|
||||
dofile 'lua/client/i18n/init.lua'
|
||||
|
||||
os.exit( lu.LuaUnit.run() )
|
||||
-- load test cases
|
||||
dofile 'test/lua/core/util.lua'
|
||||
dofile 'test/lua/core/pattern.lua'
|
||||
dofile 'test/lua/core/testmode.lua'
|
||||
|
||||
-- server tests
|
||||
dofile 'lua/server/scheduler.lua'
|
||||
Room = require 'server.room'
|
||||
fk.Room = require 'test/lua/lib/room'
|
||||
fk.ServerPlayer = require 'test/lua/lib/serverplayer'
|
||||
|
||||
dofile 'test/lua/server/scheduler.lua'
|
||||
dofile 'test/lua/server/logic.lua'
|
||||
|
||||
fk.os.exit( lu.LuaUnit.run() )
|
||||
|
|
|
@ -0,0 +1,37 @@
|
|||
-- 针对 lua/server/gamelogic.lua 以及 GameEvent 的测试
|
||||
|
||||
local croom
|
||||
_TestGameLogic = {
|
||||
setup = function()
|
||||
croom = fk.Room:new()
|
||||
croom.players = {
|
||||
fk.ServerPlayer:new(1),
|
||||
fk.ServerPlayer:new(2),
|
||||
fk.ServerPlayer:new(3),
|
||||
fk.ServerPlayer:new(4),
|
||||
fk.ServerPlayer:new(5),
|
||||
}
|
||||
end,
|
||||
|
||||
testTrigger = function()
|
||||
---@param room Room
|
||||
fk.roomtest(croom, function(room)
|
||||
local logic = room.logic
|
||||
local p = room:getPlayerById(1)
|
||||
room:handleAddLoseSkills(p, table.concat({
|
||||
"luoyi", "wansha", "yaoyi", --"dili",
|
||||
}, "|"))
|
||||
|
||||
logic:trigger(fk.GamePrepared)
|
||||
GameEvent(GameEvent.DrawInitial):exec()
|
||||
GameEvent(GameEvent.Round):exec()
|
||||
|
||||
-- DMG test
|
||||
local victim = room.alive_players[2]
|
||||
room:damage{ from = p, to = victim, damage = 20 }
|
||||
room:damage{ to = p, from = victim, damage = 20 }
|
||||
end)
|
||||
end,
|
||||
|
||||
tearDown = function() croom = nil end
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
-- 针对 lua/server/scheduler.lua 的测试。
|
||||
-- 这要怎么测试啊...
|
||||
|
||||
TestScheduler = {
|
||||
testMain = function()
|
||||
end,
|
||||
}
|
|
@ -0,0 +1,28 @@
|
|||
-- 针对 core/util.lua 的一些测试用例
|
||||
|
||||
-- 总感觉没啥好测试的
|
||||
|
||||
TestUtil = {
|
||||
testMisc = function()
|
||||
lu.assertError(function()
|
||||
Util.DummyTable.a = 4
|
||||
end)
|
||||
end,
|
||||
|
||||
testString = function()
|
||||
lu.assertIs("He" + "is", "Heis")
|
||||
local utf8string = "刘备,天下枭雄"
|
||||
lu.assertEquals(utf8string:len(), 7)
|
||||
lu.assertEquals(utf8string:rawlen(), 21)
|
||||
lu.assertEquals(#utf8string, 21)
|
||||
|
||||
local s = "gfsdf%kj.\\ts4!!,34':"
|
||||
lu.assertFalse(s:endsWith("%"))
|
||||
end,
|
||||
|
||||
testTable = function()
|
||||
local t = {1, 2, 5}
|
||||
table.insertIfNeed(t, 2)
|
||||
lu.assertEquals(t, {1, 2, 5})
|
||||
end,
|
||||
}
|
Loading…
Reference in New Issue