mirror of
https://github.com/Qsgs-Fans/FreeKill.git
synced 2024-11-16 11:42:45 +08:00
Vs skill (#44)
* vs skill concept * unify card:clone * virtual card * wusheng * exppattern * use virtual card(WIP) * change cardId to card * virtual card log * notify skill invoked * fix coroutine bug; allow vsskill to response * extra_data for askForUseCard Co-authored-by: Ho-spair <linyuy@163.com>
This commit is contained in:
parent
88d40018db
commit
b6530eae9d
|
@ -160,7 +160,7 @@ end
|
|||
function GetSkillData(skill_name)
|
||||
local skill = Fk.skills[skill_name]
|
||||
local freq = "notactive"
|
||||
if skill:isInstanceOf(ActiveSkill) then
|
||||
if skill:isInstanceOf(ActiveSkill) or skill:isInstanceOf(ViewAsSkill) then
|
||||
freq = "active"
|
||||
end
|
||||
return json.encode{
|
||||
|
@ -173,8 +173,12 @@ end
|
|||
function ActiveCanUse(skill_name)
|
||||
local skill = Fk.skills[skill_name]
|
||||
local ret = false
|
||||
if skill and skill:isInstanceOf(ActiveSkill) then
|
||||
ret = skill:canUse(Self)
|
||||
if skill then
|
||||
if skill:isInstanceOf(ActiveSkill) then
|
||||
ret = skill:canUse(Self)
|
||||
elseif skill:isInstanceOf(ViewAsSkill) then
|
||||
ret = skill:enabledAtPlay(Self)
|
||||
end
|
||||
end
|
||||
return json.encode(ret)
|
||||
end
|
||||
|
@ -182,8 +186,12 @@ end
|
|||
function ActiveCardFilter(skill_name, to_select, selected, selected_targets)
|
||||
local skill = Fk.skills[skill_name]
|
||||
local ret = false
|
||||
if skill and skill:isInstanceOf(ActiveSkill) then
|
||||
ret = skill:cardFilter(to_select, selected, selected_targets)
|
||||
if skill then
|
||||
if skill:isInstanceOf(ActiveSkill) then
|
||||
ret = skill:cardFilter(to_select, selected, selected_targets)
|
||||
elseif skill:isInstanceOf(ViewAsSkill) then
|
||||
ret = skill:cardFilter(to_select, selected)
|
||||
end
|
||||
end
|
||||
return json.encode(ret)
|
||||
end
|
||||
|
@ -191,8 +199,15 @@ end
|
|||
function ActiveTargetFilter(skill_name, to_select, selected, selected_cards)
|
||||
local skill = Fk.skills[skill_name]
|
||||
local ret = false
|
||||
if skill and skill:isInstanceOf(ActiveSkill) then
|
||||
ret = skill:targetFilter(to_select, selected, selected_cards)
|
||||
if skill then
|
||||
if skill:isInstanceOf(ActiveSkill) then
|
||||
ret = skill:targetFilter(to_select, selected, selected_cards)
|
||||
elseif skill:isInstanceOf(ViewAsSkill) then
|
||||
local card = skill:viewAs(selected_cards)
|
||||
if card then
|
||||
ret = card.skill:targetFilter(to_select, selected, selected_cards)
|
||||
end
|
||||
end
|
||||
end
|
||||
return json.encode(ret)
|
||||
end
|
||||
|
@ -200,15 +215,46 @@ end
|
|||
function ActiveFeasible(skill_name, selected, selected_cards)
|
||||
local skill = Fk.skills[skill_name]
|
||||
local ret = false
|
||||
if skill and skill:isInstanceOf(ActiveSkill) then
|
||||
ret = skill:feasible(selected, selected_cards)
|
||||
if skill then
|
||||
if skill:isInstanceOf(ActiveSkill) then
|
||||
ret = skill:feasible(selected, selected_cards)
|
||||
elseif skill:isInstanceOf(ViewAsSkill) then
|
||||
local card = skill:viewAs(selected_cards)
|
||||
if card then
|
||||
ret = card.skill:feasible(selected, selected_cards)
|
||||
end
|
||||
end
|
||||
end
|
||||
return json.encode(ret)
|
||||
end
|
||||
|
||||
-- ViewAsSkill (Todo)
|
||||
function CanViewAs(skill_name, card_ids)
|
||||
return "true"
|
||||
local skill = Fk.skills[skill_name]
|
||||
local ret = false
|
||||
if skill then
|
||||
if skill:isInstanceOf(ViewAsSkill) then
|
||||
ret = skill:viewAs(card_ids) ~= nil
|
||||
elseif skill:isInstanceOf(ActiveSkill) then
|
||||
ret = true
|
||||
end
|
||||
end
|
||||
return json.encode(ret)
|
||||
end
|
||||
|
||||
function CardFitPattern(card_name, pattern)
|
||||
local exp = Exppattern:Parse(pattern)
|
||||
local ret = exp:matchExp(card_name)
|
||||
return json.encode(ret)
|
||||
end
|
||||
|
||||
function SkillFitPattern(skill_name, pattern)
|
||||
local skill = Fk.skills[skill_name]
|
||||
local ret = false
|
||||
if skill and skill.pattern then
|
||||
local exp = Exppattern:Parse(pattern)
|
||||
ret = exp:matchExp(skill.pattern)
|
||||
end
|
||||
return json.encode(ret)
|
||||
end
|
||||
|
||||
Fk:loadTranslationTable{
|
||||
|
@ -355,10 +401,22 @@ Fk:loadTranslationTable{
|
|||
-- useCard
|
||||
["#UseCard"] = "%from 使用了牌 %card",
|
||||
["#UseCardToTargets"] = "%from 使用了牌 %card,目标是 %to",
|
||||
["#CardUseCollaborator"] = "%from 在此次 %card 中的子目标是 %to",
|
||||
["#CardUseCollaborator"] = "%from 在此次 %arg 中的子目标是 %to",
|
||||
["#UseCardToCard"] = "%from 使用了牌 %card,目标是 %arg",
|
||||
["#ResponsePlayCard"] = "%from 打出了牌 %card",
|
||||
|
||||
["#UseVCard"] = "%from 将 %card 当 %arg 使用",
|
||||
["#UseVCardToTargets"] = "%from 将 %card 当 %arg 使用,目标是 %to",
|
||||
["#UseVCardToCard"] = "%from 将 %card 当 %arg2 使用,目标是 %arg",
|
||||
["#ResponsePlayVCard"] = "%from 将 %card 当 %arg 打出",
|
||||
["#UseV0Card"] = "%from 使用了 %arg",
|
||||
["#UseV0CardToTargets"] = "%from 使用了 %arg,目标是 %to",
|
||||
["#UseV0CardToCard"] = "%from 使用了 %arg2,目标是 %arg",
|
||||
["#ResponsePlayV0Card"] = "%from 打出了 %arg",
|
||||
|
||||
-- skill
|
||||
["#InvokeSkill"] = "%from 发动了 “%arg”",
|
||||
|
||||
-- judge
|
||||
["#StartJudgeReason"] = "%from 开始了 %arg 的判定",
|
||||
["#InitialJudge"] = "%from 的判定牌为 %card",
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
---@field type CardType
|
||||
---@field sub_type CardSubtype
|
||||
---@field area CardArea
|
||||
---@field subcards integer[]
|
||||
local Card = class("Card")
|
||||
|
||||
---@alias Suit integer
|
||||
|
@ -74,6 +75,74 @@ function Card:initialize(name, suit, number, color)
|
|||
self.type = 0
|
||||
self.sub_type = Card.SubTypeNone
|
||||
self.skill = nil
|
||||
self.subcards = {}
|
||||
end
|
||||
|
||||
---@param suit Suit
|
||||
---@param number integer
|
||||
---@return Card
|
||||
function Card:clone(suit, number)
|
||||
local newCard = self.class:new(self.name, suit, number)
|
||||
newCard.skill = self.skill
|
||||
return newCard
|
||||
end
|
||||
|
||||
function Card:isVirtual()
|
||||
return self.id <= 0
|
||||
end
|
||||
|
||||
function Card:getEffectiveId()
|
||||
if self:isVirtual() then
|
||||
return #self.subcards > 0 and self.subcards[1] or nil
|
||||
end
|
||||
return self.id
|
||||
end
|
||||
|
||||
local function updateColorAndNumber(card)
|
||||
local color = Card.NoColor
|
||||
local number = 0
|
||||
local different_color = false
|
||||
for _, id in ipairs(card.subcards) do
|
||||
local c = Fk:getCardById(id)
|
||||
number = math.min(number + c.number, 13)
|
||||
if color ~= c.color then
|
||||
if not different_color then
|
||||
if color ~= Card.NoColor then
|
||||
different_color = true
|
||||
end
|
||||
color = c.color
|
||||
else
|
||||
color = Card.NoColor
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
card.color = color
|
||||
card.number = number
|
||||
end
|
||||
|
||||
---@param card integer|Card
|
||||
function Card:addSubcard(card)
|
||||
if type(card) == "number" then
|
||||
table.insert(self.subcards, card)
|
||||
else
|
||||
assert(card:isInstanceOf(Card))
|
||||
assert(not card:isVirtual(), "Can not add virtual card as subcard")
|
||||
table.insert(self.subcards, card.id)
|
||||
end
|
||||
|
||||
updateColorAndNumber(self)
|
||||
end
|
||||
|
||||
function Card:addSubcards(cards)
|
||||
for _, c in ipairs(cards) do
|
||||
self:addSubcard(c)
|
||||
end
|
||||
end
|
||||
|
||||
function Card:clearSubcards()
|
||||
self.subcards = {}
|
||||
updateColorAndNumber(self)
|
||||
end
|
||||
|
||||
function Card:getSuitString()
|
||||
|
@ -91,6 +160,16 @@ function Card:getSuitString()
|
|||
end
|
||||
end
|
||||
|
||||
function Card:getColorString()
|
||||
local color = self.color
|
||||
if color == Card.Black then
|
||||
return "black"
|
||||
elseif color == Card.Red then
|
||||
return "red"
|
||||
end
|
||||
return "nocolor"
|
||||
end
|
||||
|
||||
local function getNumberStr(num)
|
||||
if num == 1 then
|
||||
return "A"
|
||||
|
@ -107,9 +186,13 @@ end
|
|||
-- for sendLog
|
||||
function Card:toLogString()
|
||||
local ret = string.format('<font color="#0598BC"><b>%s</b></font>', Fk:translate(self.name) .. "[")
|
||||
ret = ret .. Fk:translate("log_" .. self:getSuitString())
|
||||
if self.number > 0 then
|
||||
ret = ret .. string.format('<font color="%s"><b>%s</b></font>', self.color == Card.Red and "#CC3131" or "black", getNumberStr(self.number))
|
||||
if self:isVirtual() then
|
||||
ret = ret .. Fk:translate(self:getColorString())
|
||||
else
|
||||
ret = ret .. Fk:translate("log_" .. self:getSuitString())
|
||||
if self.number > 0 then
|
||||
ret = ret .. string.format('<font color="%s"><b>%s</b></font>', self.color == Card.Red and "#CC3131" or "black", getNumberStr(self.number))
|
||||
end
|
||||
end
|
||||
ret = ret .. '<font color="#0598BC"><b>]</b></font>'
|
||||
return ret
|
||||
|
|
|
@ -6,13 +6,4 @@ function BasicCard:initialize(name, suit, number)
|
|||
self.type = Card.TypeBasic
|
||||
end
|
||||
|
||||
---@param suit Suit
|
||||
---@param number integer
|
||||
---@return BasicCard
|
||||
function BasicCard:clone(suit, number)
|
||||
local newCard = BasicCard:new(self.name, suit, number)
|
||||
newCard.skill = self.skill
|
||||
return newCard
|
||||
end
|
||||
|
||||
return BasicCard
|
||||
|
|
|
@ -17,15 +17,6 @@ function Weapon:initialize(name, suit, number, attackRange)
|
|||
self.attack_range = attackRange or 1
|
||||
end
|
||||
|
||||
---@param suit Suit
|
||||
---@param number integer
|
||||
---@return Weapon
|
||||
function Weapon:clone(suit, number)
|
||||
local newCard = Weapon:new(self.name, suit, number, self.attack_range)
|
||||
newCard.skill = self.skill
|
||||
return newCard
|
||||
end
|
||||
|
||||
---@class Armor : EquipCard
|
||||
local Armor = EquipCard:subclass("armor")
|
||||
|
||||
|
@ -34,15 +25,6 @@ function Armor:initialize(name, suit, number)
|
|||
self.sub_type = Card.SubtypeArmor
|
||||
end
|
||||
|
||||
---@param suit Suit
|
||||
---@param number integer
|
||||
---@return Armor
|
||||
function Armor:clone(suit, number)
|
||||
local newCard = Armor:new(self.name, suit, number)
|
||||
newCard.skill = self.skill
|
||||
return newCard
|
||||
end
|
||||
|
||||
---@class DefensiveRide : EquipCard
|
||||
local DefensiveRide = EquipCard:subclass("DefensiveRide")
|
||||
|
||||
|
@ -51,15 +33,6 @@ function DefensiveRide:initialize(name, suit, number)
|
|||
self.sub_type = Card.SubtypeDefensiveRide
|
||||
end
|
||||
|
||||
---@param suit Suit
|
||||
---@param number integer
|
||||
---@return DefensiveRide
|
||||
function DefensiveRide:clone(suit, number)
|
||||
local newCard = DefensiveRide:new(self.name, suit, number)
|
||||
newCard.skill = self.skill
|
||||
return newCard
|
||||
end
|
||||
|
||||
---@class OffensiveRide : EquipCard
|
||||
local OffensiveRide = EquipCard:subclass("OffensiveRide")
|
||||
|
||||
|
@ -68,15 +41,6 @@ function OffensiveRide:initialize(name, suit, number)
|
|||
self.sub_type = Card.SubtypeOffensiveRide
|
||||
end
|
||||
|
||||
---@param suit Suit
|
||||
---@param number integer
|
||||
---@return OffensiveRide
|
||||
function OffensiveRide:clone(suit, number)
|
||||
local newCard = OffensiveRide:new(self.name, suit, number)
|
||||
newCard.skill = self.skill
|
||||
return newCard
|
||||
end
|
||||
|
||||
---@class Treasure : EquipCard
|
||||
local Treasure = EquipCard:subclass("Treasure")
|
||||
|
||||
|
@ -85,13 +49,4 @@ function Treasure:initialize(name, suit, number)
|
|||
self.sub_type = Card.SubtypeTreasure
|
||||
end
|
||||
|
||||
---@param suit Suit
|
||||
---@param number integer
|
||||
---@return Treasure
|
||||
function Treasure:clone(suit, number)
|
||||
local newCard = Treasure:new(self.name, suit, number)
|
||||
newCard.skill = self.skill
|
||||
return newCard
|
||||
end
|
||||
|
||||
return { EquipCard, Weapon, Armor, DefensiveRide, OffensiveRide, Treasure }
|
||||
|
|
|
@ -6,17 +6,6 @@ function TrickCard:initialize(name, suit, number)
|
|||
self.type = Card.TypeTrick
|
||||
end
|
||||
|
||||
---@param suit Suit
|
||||
---@param number integer
|
||||
---@return TrickCard
|
||||
function TrickCard:clone(suit, number)
|
||||
local newCard = TrickCard:new(self.name, suit, number)
|
||||
|
||||
newCard.skill = self.skill
|
||||
|
||||
return newCard
|
||||
end
|
||||
|
||||
---@class DelayedTrickCard : TrickCard
|
||||
local DelayedTrickCard = TrickCard:subclass("DelayedTrickCard")
|
||||
|
||||
|
@ -25,13 +14,4 @@ function DelayedTrickCard:initialize(name, suit, number)
|
|||
self.sub_type = Card.SubtypeDelayedTrick
|
||||
end
|
||||
|
||||
---@param suit Suit
|
||||
---@param number integer
|
||||
---@return DelayedTrickCard
|
||||
function DelayedTrickCard:clone(suit, number)
|
||||
local newCard = DelayedTrickCard:new(self.name, suit, number)
|
||||
newCard.skill = self.skill
|
||||
return newCard
|
||||
end
|
||||
|
||||
return { TrickCard, DelayedTrickCard }
|
||||
|
|
|
@ -137,12 +137,14 @@ function Engine:addGenerals(generals)
|
|||
end
|
||||
|
||||
local cardId = 1
|
||||
local _card_name_table = {}
|
||||
---@param card Card
|
||||
function Engine:addCard(card)
|
||||
assert(card.class:isSubclassOf(Card))
|
||||
card.id = cardId
|
||||
cardId = cardId + 1
|
||||
table.insert(self.cards, card)
|
||||
_card_name_table[card.name] = card
|
||||
end
|
||||
|
||||
---@param cards Card[]
|
||||
|
@ -152,6 +154,16 @@ function Engine:addCards(cards)
|
|||
end
|
||||
end
|
||||
|
||||
---@param name string
|
||||
---@param suit Suit
|
||||
---@param number integer
|
||||
---@return Card
|
||||
function Engine:cloneCard(name, suit, number)
|
||||
local cd = _card_name_table[name]
|
||||
assert(cd, "Attempt to clone a card that not added to engine")
|
||||
return cd:clone(suit, number)
|
||||
end
|
||||
|
||||
---@param num integer
|
||||
---@param generalPool General[]
|
||||
---@param except string[]
|
||||
|
|
246
lua/core/exppattern.lua
Normal file
246
lua/core/exppattern.lua
Normal file
|
@ -0,0 +1,246 @@
|
|||
--[[
|
||||
|
||||
Exppattern is a string that describes cards of a same 'type', e.g. name,
|
||||
suit, etc.
|
||||
|
||||
The string will be parsed and construct a new Exppattern instance.
|
||||
Then we can use this instance to check the card.
|
||||
|
||||
Syntax for the string form:
|
||||
1. the whole string can be splited by ';'. Every slice stands for a Matcher
|
||||
2. For the matcher string, it can be splited by '|'.
|
||||
3. And the arrays in class Match is concated by ',' in string.
|
||||
|
||||
Example:
|
||||
slash,jink|2~4|spade;.|.|.|.|.|trick
|
||||
|
||||
]]--
|
||||
|
||||
---@class Matcher
|
||||
---@field name string[]
|
||||
---@field number integer[]
|
||||
---@field suit integer[]
|
||||
---@field place string[]
|
||||
---@field generalName string[]
|
||||
---@field cardType integer[]
|
||||
---@field id integer[]
|
||||
|
||||
local numbertable = {
|
||||
["A"] = 1,
|
||||
["J"] = 11,
|
||||
["Q"] = 12,
|
||||
["K"] = 13,
|
||||
}
|
||||
|
||||
local suittable = {
|
||||
[Card.Spade] = "spade",
|
||||
[Card.Club] = "club",
|
||||
[Card.Heart] = "heart",
|
||||
[Card.Diamond] = "diamond",
|
||||
}
|
||||
|
||||
local typetable = {
|
||||
[Card.TypeBasic] = "basic",
|
||||
[Card.TypeTrick] = "trick",
|
||||
[Card.TypeEquip] = "equip",
|
||||
}
|
||||
|
||||
---@param matcher Matcher
|
||||
---@param card Card
|
||||
local function matchCard(matcher, card)
|
||||
if type(card) == "number" then
|
||||
card = Fk:getCardById(card)
|
||||
end
|
||||
|
||||
if matcher.name and not table.contains(matcher.name, card.name) then
|
||||
return false
|
||||
end
|
||||
|
||||
if matcher.number and not table.contains(matcher.number, card.number) then
|
||||
return false
|
||||
end
|
||||
|
||||
if matcher.suit and not table.contains(matcher.suit, card:getSuitString()) then
|
||||
return false
|
||||
end
|
||||
|
||||
-- TODO: place
|
||||
-- TODO: generalName
|
||||
|
||||
if matcher.cardType and not table.contains(matcher.cardType, typetable[card.type]) then
|
||||
return false
|
||||
end
|
||||
|
||||
if matcher.id and not table.contains(matcher.id, card.id) then
|
||||
return false
|
||||
end
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
local function hasIntersection(a, b)
|
||||
if a == nil or b == nil then
|
||||
return true
|
||||
end
|
||||
|
||||
local tmp = {}
|
||||
for _, e in ipairs(a) do
|
||||
tmp[e] = true
|
||||
end
|
||||
for _, e in ipairs(b) do
|
||||
if tmp[e] then
|
||||
return true
|
||||
end
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
---@param a Matcher
|
||||
---@param b Matcher
|
||||
local function matchMatcher(a, b)
|
||||
local keys = {
|
||||
"name",
|
||||
"number",
|
||||
"suit",
|
||||
"place",
|
||||
"generalName",
|
||||
"cardType",
|
||||
"id",
|
||||
}
|
||||
|
||||
for _, k in ipairs(keys) do
|
||||
if not hasIntersection(a[k], b[k]) then
|
||||
return false
|
||||
end
|
||||
end
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
local function parseMatcher(str)
|
||||
local t = str:split("|")
|
||||
if #t < 7 then
|
||||
for i = 1, 7 - #t do
|
||||
table.insert(t, ".")
|
||||
end
|
||||
end
|
||||
|
||||
for i, item in ipairs(t) do
|
||||
t[i] = item:split(",")
|
||||
end
|
||||
|
||||
local ret = {} ---@type Matcher
|
||||
ret.name = not table.contains(t[1], ".") and t[1] or nil
|
||||
|
||||
if not table.contains(t[2], ".") then
|
||||
ret.number = {}
|
||||
for _, num in ipairs(t[2]) do
|
||||
local n = tonumber(num)
|
||||
if not n then
|
||||
n = numbertable[num]
|
||||
end
|
||||
if n then
|
||||
table.insertIfNeed(ret.number, n)
|
||||
else
|
||||
if string.find(n, "~") then
|
||||
local start, _end = table.unpack(n:split("~"))
|
||||
for i = start, _end do
|
||||
table.insertIfNeed(ret.number, n)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if not table.contains(t[3], ".") then
|
||||
ret.suit = {}
|
||||
for _, num in ipairs(t[3]) do
|
||||
local n = suittable[num]
|
||||
if n then
|
||||
table.insertIfNeed(ret.suit, n)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
ret.place = not table.contains(t[4], ".") and t[4] or nil
|
||||
ret.generalName = not table.contains(t[5], ".") and t[5] or nil
|
||||
|
||||
if not table.contains(t[6], ".") then
|
||||
ret.cardType = {}
|
||||
for _, num in ipairs(t[6]) do
|
||||
local n = typetable[num]
|
||||
if n then
|
||||
table.insertIfNeed(ret.cardType, n)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if not table.contains(t[7], ".") then
|
||||
ret.id = {}
|
||||
for _, num in ipairs(t[6]) do
|
||||
local n = tonumber(num)
|
||||
if n and n > 0 then
|
||||
table.insertIfNeed(ret.id, n)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return ret
|
||||
end
|
||||
|
||||
---@class Exppattern: Object
|
||||
---@field matchers Matcher[]
|
||||
local Exppattern = class("Exppattern")
|
||||
|
||||
function Exppattern:initialize(spec)
|
||||
if not spec then
|
||||
self.matchers = {}
|
||||
elseif spec[1] ~= nil then
|
||||
self.matchers = spec
|
||||
else
|
||||
self.matchers = {}
|
||||
self.matchers[1] = spec
|
||||
end
|
||||
end
|
||||
|
||||
---@param str string
|
||||
function Exppattern.static:Parse(str)
|
||||
local ret = Exppattern:new()
|
||||
local t = str:split(";")
|
||||
for i, s in ipairs(t) do
|
||||
ret.matchers[i] = parseMatcher(s)
|
||||
end
|
||||
return ret
|
||||
end
|
||||
|
||||
---@param card Card
|
||||
function Exppattern:match(card)
|
||||
for _, matcher in ipairs(self.matchers) do
|
||||
local result = matchCard(matcher, card)
|
||||
if result then
|
||||
return true
|
||||
end
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
function Exppattern:matchExp(exp)
|
||||
if type(exp) == "string" then
|
||||
exp = Exppattern:Parse(exp)
|
||||
end
|
||||
|
||||
local a = self.matchers
|
||||
local b = exp.matchers
|
||||
|
||||
for _, m in ipairs(a) do
|
||||
for _, n in ipairs(b) do
|
||||
if matchMatcher(m, n) then
|
||||
return true
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return false
|
||||
end
|
||||
|
||||
return Exppattern
|
|
@ -0,0 +1,35 @@
|
|||
---@class ViewAsSkill
|
||||
---@field pattern string @ cards that can be viewAs'ed by this skill
|
||||
local ViewAsSkill = Skill:subclass("ViewAsSkill")
|
||||
|
||||
function ViewAsSkill:initialize(name)
|
||||
Skill.initialize(self, name, Skill.NotFrequent)
|
||||
self.pattern = ""
|
||||
end
|
||||
|
||||
---@param to_select integer @ id of a card not selected
|
||||
---@param selected integer[] @ ids of selected cards
|
||||
---@return boolean
|
||||
function ViewAsSkill:cardFilter(to_select, selected)
|
||||
return false
|
||||
end
|
||||
|
||||
---@param cards integer[] @ ids of cards
|
||||
---@return card
|
||||
function ViewAsSkill:viewAs(cards)
|
||||
return nil
|
||||
end
|
||||
|
||||
-- For extra judgement, like mark or HP
|
||||
|
||||
---@param player Player
|
||||
function ViewAsSkill:enabledAtPlay(player)
|
||||
return true
|
||||
end
|
||||
|
||||
---@param player Player
|
||||
function ViewAsSkill:enabledAtResponse(player)
|
||||
return true
|
||||
end
|
||||
|
||||
return ViewAsSkill
|
|
@ -4,6 +4,7 @@ dofile "lua/server/event.lua"
|
|||
dofile "lua/server/system_enum.lua"
|
||||
TriggerSkill = require "core.skill_type.trigger"
|
||||
ActiveSkill = require "core.skill_type.active_skill"
|
||||
ViewAsSkill = require "core.skill_type.view_as"
|
||||
DistanceSkill = require "core.skill_type.distance"
|
||||
StatusSkills = {
|
||||
DistanceSkill,
|
||||
|
@ -111,6 +112,37 @@ function fk.CreateActiveSkill(spec)
|
|||
return skill
|
||||
end
|
||||
|
||||
---@class ViewAsSkillSpec: SkillSpec
|
||||
---@field card_filter fun(self: ViewAsSkill, to_select: integer, selected: integer[]): boolean
|
||||
---@field view_as fun(self: ViewAsSkill, cards: integer[])
|
||||
---@field pattern string
|
||||
---@field enabled_at_play fun(self: ViewAsSkill, player: Player): boolean
|
||||
---@field enabled_at_response fun(self: ViewAsSkill, player: Player): boolean
|
||||
|
||||
---@param spec ViewAsSkillSpec
|
||||
---@return ViewAsSkill
|
||||
function fk.CreateViewAsSkill(spec)
|
||||
assert(type(spec.name) == "string")
|
||||
assert(type(spec.view_as) == "function")
|
||||
|
||||
local skill = ViewAsSkill:new(spec.name)
|
||||
skill.viewAs = spec.view_as
|
||||
if spec.card_filter then
|
||||
skill.cardFilter = spec.card_filter
|
||||
end
|
||||
if type(spec.pattern) == "string" then
|
||||
skill.pattern = spec.pattern
|
||||
end
|
||||
if type(spec.enabled_at_play) == "function" then
|
||||
skill.enabledAtPlay = spec.enabled_at_play
|
||||
end
|
||||
if type(spec.enabled_at_response) == "function" then
|
||||
skill.enabledAtResponse = spec.enabled_at_response
|
||||
end
|
||||
|
||||
return skill
|
||||
end
|
||||
|
||||
---@class DistanceSpec: SkillSpec
|
||||
---@field correct_func fun(self: DistanceSkill, from: Player, to: Player)
|
||||
---@field global boolean
|
||||
|
|
|
@ -20,6 +20,7 @@ Engine = require "core.engine"
|
|||
Package = require "core.package"
|
||||
General = require "core.general"
|
||||
Card = require "core.card"
|
||||
Exppattern = require "core.exppattern"
|
||||
Skill = require "core.skill"
|
||||
Player = require "core.player"
|
||||
|
||||
|
|
|
@ -55,9 +55,15 @@ function Room:initialize(_room)
|
|||
end
|
||||
local co = coroutine.create(co_func)
|
||||
while not self.game_finished do
|
||||
coroutine.resume(co)
|
||||
local ret, err_msg = coroutine.resume(co)
|
||||
|
||||
-- handle error
|
||||
if ret == false then
|
||||
fk.qCritical(err_msg)
|
||||
print(debug.traceback(co))
|
||||
break
|
||||
end
|
||||
end
|
||||
fk.qInfo("Game Finished.")
|
||||
end
|
||||
|
||||
self.players = {}
|
||||
|
@ -426,6 +432,19 @@ function Room:sendLogEvent(type, data, players)
|
|||
self:doBroadcastNotify("LogEvent", json.encode(data), players)
|
||||
end
|
||||
|
||||
---@param player ServerPlayer
|
||||
---@param skill_name string
|
||||
---@param skill_type nil
|
||||
function Room:notifySkillInvoked(player, skill_name, skill_type)
|
||||
self:sendLog{
|
||||
type = "#InvokeSkill",
|
||||
from = player.id,
|
||||
arg = skill_name,
|
||||
}
|
||||
|
||||
-- TODO: notifySkill animation
|
||||
end
|
||||
|
||||
------------------------------------------------------------------------
|
||||
-- interactive functions
|
||||
------------------------------------------------------------------------
|
||||
|
@ -604,64 +623,101 @@ function Room:askForSkillInvoke(player, skill_name, data)
|
|||
end
|
||||
|
||||
---@param player ServerPlayer
|
||||
---@param data string
|
||||
---@return CardUseStruct
|
||||
function Room:askForUseCard(player, card_name, prompt, cancelable, extra_data)
|
||||
function Room:handleUseCardReply(player, data)
|
||||
data = json.decode(data)
|
||||
local card = data.card
|
||||
local targets = data.targets
|
||||
if type(card) == "string" then
|
||||
local card_data = json.decode(card)
|
||||
local skill = Fk.skills[card_data.skill]
|
||||
local selected_cards = card_data.subcards
|
||||
if skill:isInstanceOf(ActiveSkill) then
|
||||
self:notifySkillInvoked(player, skill.name)
|
||||
skill:onEffect(self, {
|
||||
from = player.id,
|
||||
cards = selected_cards,
|
||||
tos = targets,
|
||||
})
|
||||
return nil
|
||||
elseif skill:isInstanceOf(ViewAsSkill) then
|
||||
local c = skill:viewAs(selected_cards)
|
||||
if c then
|
||||
self:notifySkillInvoked(player, skill.name)
|
||||
local use = {} ---@type CardUseStruct
|
||||
use.from = player.id
|
||||
use.tos = {}
|
||||
for _, target in ipairs(targets) do
|
||||
table.insert(use.tos, { target })
|
||||
end
|
||||
use.card = c
|
||||
return use
|
||||
end
|
||||
end
|
||||
else
|
||||
local use = {} ---@type CardUseStruct
|
||||
use.from = player.id
|
||||
use.tos = {}
|
||||
for _, target in ipairs(targets) do
|
||||
table.insert(use.tos, { target })
|
||||
end
|
||||
if #use.tos == 0 then
|
||||
use.tos = nil
|
||||
end
|
||||
use.card = Fk:getCardById(card)
|
||||
return use
|
||||
end
|
||||
end
|
||||
|
||||
-- available extra_data:
|
||||
-- * must_targets: integer[]
|
||||
---@param player ServerPlayer
|
||||
---@param card_name string
|
||||
---@param pattern string
|
||||
---@param prompt string
|
||||
---@return CardUseStruct
|
||||
function Room:askForUseCard(player, card_name, pattern, prompt, cancelable, extra_data)
|
||||
local command = "AskForUseCard"
|
||||
self:notifyMoveFocus(player, card_name)
|
||||
cancelable = cancelable or false
|
||||
extra_data = extra_data or {}
|
||||
pattern = pattern or card_name
|
||||
prompt = prompt or "#AskForUseCard"
|
||||
|
||||
local data = {card_name, prompt, cancelable, extra_data}
|
||||
local data = {card_name, pattern, prompt, cancelable, extra_data}
|
||||
local result = self:doRequest(player, command, json.encode(data))
|
||||
if result ~= "" then
|
||||
data = json.decode(result)
|
||||
local card = data.card
|
||||
local targets = data.targets
|
||||
if type(card) == "string" then
|
||||
-- TODO: ViewAsSkill
|
||||
return nil
|
||||
else
|
||||
local use = {} ---@type CardUseStruct
|
||||
use.from = player.id
|
||||
use.tos = {}
|
||||
for _, target in ipairs(targets) do
|
||||
table.insert(use.tos, { target })
|
||||
end
|
||||
if #use.tos == 0 then
|
||||
use.tos = nil
|
||||
end
|
||||
use.cardId = card
|
||||
return use
|
||||
end
|
||||
return self:handleUseCardReply(player, result)
|
||||
end
|
||||
return nil
|
||||
end
|
||||
|
||||
function Room:askForResponse(player, card_name, prompt, cancelable, extra_data)
|
||||
---@param player ServerPlayer
|
||||
---@param card_name string
|
||||
---@param pattern string
|
||||
---@param prompt string
|
||||
---@param cancelable string
|
||||
function Room:askForResponse(player, card_name, pattern, prompt, cancelable, extra_data)
|
||||
local command = "AskForResponseCard"
|
||||
self:notifyMoveFocus(player, card_name)
|
||||
cancelable = cancelable or false
|
||||
extra_data = extra_data or {}
|
||||
pattern = pattern or card_name
|
||||
prompt = prompt or "#AskForResponseCard"
|
||||
|
||||
local data = {card_name, prompt, cancelable, extra_data}
|
||||
local data = {card_name, pattern, prompt, cancelable, extra_data}
|
||||
local result = self:doRequest(player, command, json.encode(data))
|
||||
if result ~= "" then
|
||||
data = json.decode(result)
|
||||
local card = data.card
|
||||
local targets = data.targets
|
||||
if type(card) == "string" then
|
||||
-- TODO: ViewAsSkill
|
||||
return nil
|
||||
else
|
||||
return card
|
||||
local use = self:handleUseCardReply(player, result)
|
||||
if use then
|
||||
return use.card
|
||||
end
|
||||
end
|
||||
return nil
|
||||
end
|
||||
|
||||
function Room:askForNullification(players, card_name, prompt, cancelable, extra_data)
|
||||
function Room:askForNullification(players, card_name, pattern, prompt, cancelable, extra_data)
|
||||
if #players == 0 then
|
||||
return nil
|
||||
end
|
||||
|
@ -671,33 +727,16 @@ function Room:askForNullification(players, card_name, prompt, cancelable, extra_
|
|||
cancelable = cancelable or false
|
||||
extra_data = extra_data or {}
|
||||
prompt = prompt or "#AskForUseCard"
|
||||
pattern = pattern or card_name
|
||||
|
||||
self:notifyMoveFocus(self.alive_players, card_name)
|
||||
self:doBroadcastNotify("WaitForNullification", "")
|
||||
|
||||
local data = {card_name, prompt, cancelable, extra_data}
|
||||
local data = {card_name, pattern, prompt, cancelable, extra_data}
|
||||
local winner = self:doRaceRequest(command, players, json.encode(data))
|
||||
if winner then
|
||||
local result = winner.client_reply
|
||||
data = json.decode(result)
|
||||
local card = data.card
|
||||
local targets = data.targets
|
||||
if type(card) == "string" then
|
||||
-- TODO: ViewAsSkill
|
||||
return nil
|
||||
else
|
||||
local use = {} ---@type CardUseStruct
|
||||
use.from = winner.id
|
||||
use.tos = {}
|
||||
for _, target in ipairs(targets) do
|
||||
table.insert(use.tos, { target })
|
||||
end
|
||||
if #use.tos == 0 then
|
||||
use.tos = nil
|
||||
end
|
||||
use.cardId = card
|
||||
return use
|
||||
end
|
||||
return self:handleUseCardReply(winner, result)
|
||||
end
|
||||
return nil
|
||||
end
|
||||
|
@ -706,6 +745,114 @@ end
|
|||
-- use card logic, and wrappers
|
||||
------------------------------------------------------------------------
|
||||
|
||||
---@param room Room
|
||||
---@param cardUseEvent CardUseStruct
|
||||
local sendCardEmotionAndLog = function(room, cardUseEvent)
|
||||
local from = cardUseEvent.from
|
||||
local card = cardUseEvent.card
|
||||
room:setEmotion(room:getPlayerById(from), card.name)
|
||||
room:doAnimate("Indicate", {
|
||||
from = from,
|
||||
to = cardUseEvent.tos or {},
|
||||
})
|
||||
|
||||
local useCardIds = card:isVirtual() and card.subcards or { card.id }
|
||||
if cardUseEvent.tos and #cardUseEvent.tos > 0 then
|
||||
local to = {}
|
||||
for _, t in ipairs(cardUseEvent.tos) do
|
||||
table.insert(to, t[1])
|
||||
end
|
||||
|
||||
if card:isVirtual() then
|
||||
if #useCardIds == 0 then
|
||||
room:sendLog{
|
||||
type = "#UseV0CardToTargets",
|
||||
from = from,
|
||||
to = to,
|
||||
arg = card:toLogString(),
|
||||
}
|
||||
else
|
||||
room:sendLog{
|
||||
type = "#UseVCardToTargets",
|
||||
from = from,
|
||||
to = to,
|
||||
card = useCardIds,
|
||||
arg = card:toLogString(),
|
||||
}
|
||||
end
|
||||
else
|
||||
room:sendLog{
|
||||
type = "#UseCardToTargets",
|
||||
from = from,
|
||||
to = to,
|
||||
card = useCardIds
|
||||
}
|
||||
end
|
||||
|
||||
for _, t in ipairs(cardUseEvent.tos) do
|
||||
if t[2] then
|
||||
local temp = {table.unpack(t)}
|
||||
table.remove(temp, 1)
|
||||
room:sendLog{
|
||||
type = "#CardUseCollaborator",
|
||||
from = t[1],
|
||||
to = temp,
|
||||
arg = card.name,
|
||||
}
|
||||
end
|
||||
end
|
||||
elseif cardUseEvent.toCard then
|
||||
if card:isVirtual() then
|
||||
if #useCardIds == 0 then
|
||||
room:sendLog{
|
||||
type = "#UseV0CardToCard",
|
||||
from = from,
|
||||
arg = cardUseEvent.toCard.name,
|
||||
arg2 = card:toLogString(),
|
||||
}
|
||||
else
|
||||
room:sendLog{
|
||||
type = "#UseVCardToCard",
|
||||
from = from,
|
||||
card = useCardIds,
|
||||
arg = cardUseEvent.toCard.name,
|
||||
arg2 = card:toLogString(),
|
||||
}
|
||||
end
|
||||
else
|
||||
room:sendLog{
|
||||
type = "#UseCardToCard",
|
||||
from = from,
|
||||
card = useCardIds,
|
||||
arg = cardUseEvent.toCard.name,
|
||||
}
|
||||
end
|
||||
else
|
||||
if card:isVirtual() then
|
||||
if #useCardIds == 0 then
|
||||
room:sendLog{
|
||||
type = "#UseV0Card",
|
||||
from = from,
|
||||
arg = card:toLogString(),
|
||||
}
|
||||
else
|
||||
room:sendLog{
|
||||
type = "#UseVCard",
|
||||
from = from,
|
||||
card = useCardIds,
|
||||
arg = card:toLogString(),
|
||||
}
|
||||
end
|
||||
else
|
||||
room:sendLog{
|
||||
type = "#UseCard",
|
||||
from = from,
|
||||
card = useCardIds,
|
||||
}
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
---@param room Room
|
||||
---@param cardUseEvent CardUseStruct
|
||||
---@param aimEventCollaborators table<string, AimStruct[]>
|
||||
|
@ -732,7 +879,7 @@ local onAim = function(room, cardUseEvent, aimEventCollaborators)
|
|||
if not aimEventCollaborators[toId] or collaboratorsIndex[toId] > #aimEventCollaborators[toId] then
|
||||
aimStruct = {
|
||||
from = cardUseEvent.from,
|
||||
cardId = cardUseEvent.cardId,
|
||||
card = cardUseEvent.card,
|
||||
to = toId,
|
||||
targetGroup = cardUseEvent.tos,
|
||||
nullifiedTargets = cardUseEvent.nullifiedTargets or {},
|
||||
|
@ -760,7 +907,7 @@ local onAim = function(room, cardUseEvent, aimEventCollaborators)
|
|||
else
|
||||
aimStruct = aimEventCollaborators[toId][collaboratorsIndex[toId]]
|
||||
aimStruct.from = cardUseEvent.from
|
||||
aimStruct.cardId = cardUseEvent.cardId
|
||||
aimStruct.card = cardUseEvent.card
|
||||
aimStruct.tos = aimGroup
|
||||
aimStruct.targetGroup = cardUseEvent.tos
|
||||
aimStruct.nullifiedTargets = cardUseEvent.nullifiedTargets or {}
|
||||
|
@ -820,74 +967,33 @@ end
|
|||
function Room:useCard(cardUseEvent)
|
||||
local from = cardUseEvent.from
|
||||
self:moveCards({
|
||||
ids = { cardUseEvent.cardId },
|
||||
ids = self:getSubcardsByRule(cardUseEvent.card),
|
||||
from = from,
|
||||
toArea = Card.Processing,
|
||||
moveReason = fk.ReasonUse,
|
||||
})
|
||||
|
||||
if Fk:getCardById(cardUseEvent.cardId).skill then
|
||||
Fk:getCardById(cardUseEvent.cardId).skill:onUse(self, cardUseEvent)
|
||||
if cardUseEvent.card.skill then
|
||||
cardUseEvent.card.skill:onUse(self, cardUseEvent)
|
||||
end
|
||||
|
||||
self:setEmotion(self:getPlayerById(from), Fk:getCardById(cardUseEvent.cardId).name)
|
||||
self:doAnimate("Indicate", {
|
||||
from = from,
|
||||
to = cardUseEvent.tos or {},
|
||||
})
|
||||
if cardUseEvent.tos and #cardUseEvent.tos > 0 then
|
||||
local to = {}
|
||||
for _, t in ipairs(cardUseEvent.tos) do
|
||||
table.insert(to, t[1])
|
||||
end
|
||||
self:sendLog{
|
||||
type = "#UseCardToTargets",
|
||||
from = from,
|
||||
to = to,
|
||||
card = {cardUseEvent.cardId},
|
||||
}
|
||||
for _, t in ipairs(cardUseEvent.tos) do
|
||||
if t[2] then
|
||||
local temp = {table.unpack(t)}
|
||||
table.remove(temp, 1)
|
||||
self:sendLog{
|
||||
type = "#CardUseCollaborator",
|
||||
from = t[1],
|
||||
to = temp,
|
||||
card = {cardUseEvent.cardId},
|
||||
}
|
||||
end
|
||||
end
|
||||
elseif cardUseEvent.toCardId then
|
||||
self:sendLog{
|
||||
type = "#UseCardToCard",
|
||||
from = from,
|
||||
card = {cardUseEvent.cardId},
|
||||
arg = Fk:getCardById(cardUseEvent.toCardId).name,
|
||||
}
|
||||
else
|
||||
self:sendLog{
|
||||
type = "#UseCard",
|
||||
from = from,
|
||||
card = {cardUseEvent.cardId},
|
||||
}
|
||||
end
|
||||
sendCardEmotionAndLog(self, cardUseEvent)
|
||||
|
||||
if self.logic:trigger(fk.PreCardUse, self:getPlayerById(cardUseEvent.from), cardUseEvent) then
|
||||
return false
|
||||
end
|
||||
|
||||
if not cardUseEvent.extraUse then
|
||||
self:getPlayerById(cardUseEvent.from):addCardUseHistory(Fk:getCardById(cardUseEvent.cardId).trueName, 1)
|
||||
self:getPlayerById(cardUseEvent.from):addCardUseHistory(cardUseEvent.card.trueName, 1)
|
||||
end
|
||||
|
||||
if cardUseEvent.responseToEvent then
|
||||
cardUseEvent.responseToEvent.cardIdsResponded = cardUseEvent.responseToEvent.cardIdsResponded or {}
|
||||
table.insert(cardUseEvent.responseToEvent.cardIdsResponded, cardUseEvent.cardId)
|
||||
cardUseEvent.responseToEvent.cardsResponded = cardUseEvent.responseToEvent.cardsResponded or {}
|
||||
table.insert(cardUseEvent.responseToEvent.cardsResponded, cardUseEvent.card)
|
||||
end
|
||||
|
||||
for _, event in ipairs({ fk.AfterCardUseDeclared, fk.AfterCardTargetDeclared, fk.BeforeCardUseEffect, fk.CardUsing }) do
|
||||
if not cardUseEvent.toCardId and #TargetGroup:getRealTargets(cardUseEvent.tos) == 0 then
|
||||
if not cardUseEvent.toCard and #TargetGroup:getRealTargets(cardUseEvent.tos) == 0 then
|
||||
break
|
||||
end
|
||||
|
||||
|
@ -899,20 +1005,21 @@ function Room:useCard(cardUseEvent)
|
|||
break
|
||||
end
|
||||
|
||||
if Fk:getCardById(cardUseEvent.cardId).type == Card.TypeEquip then
|
||||
if self:getCardArea(cardUseEvent.cardId) ~= Card.Processing then
|
||||
local realCardIds = self:getSubcardsByRule(cardUseEvent.card, { Card.Processing })
|
||||
if cardUseEvent.card.type == Card.TypeEquip then
|
||||
if #realCardIds == 0 then
|
||||
break
|
||||
end
|
||||
|
||||
if self:getPlayerById(TargetGroup:getRealTargets(cardUseEvent.tos)[1]).dead then
|
||||
self.moveCards({
|
||||
ids = { cardUseEvent.cardId },
|
||||
ids = realCardIds,
|
||||
toArea = Card.DiscardPile,
|
||||
moveReason = fk.ReasonPutIntoDiscardPile,
|
||||
})
|
||||
else
|
||||
local target = TargetGroup:getRealTargets(cardUseEvent.tos)[1]
|
||||
local existingEquipId = self:getPlayerById(target):getEquipment(Fk:getCardById(cardUseEvent.cardId).sub_type)
|
||||
local existingEquipId = self:getPlayerById(target):getEquipment(cardUseEvent.card.sub_type)
|
||||
if existingEquipId then
|
||||
self:moveCards(
|
||||
{
|
||||
|
@ -922,7 +1029,7 @@ function Room:useCard(cardUseEvent)
|
|||
moveReason = fk.ReasonPutIntoDiscardPile,
|
||||
},
|
||||
{
|
||||
ids = { cardUseEvent.cardId },
|
||||
ids = realCardIds,
|
||||
to = target,
|
||||
toArea = Card.PlayerEquip,
|
||||
moveReason = fk.ReasonUse,
|
||||
|
@ -930,7 +1037,7 @@ function Room:useCard(cardUseEvent)
|
|||
)
|
||||
else
|
||||
self:moveCards({
|
||||
ids = { cardUseEvent.cardId },
|
||||
ids = realCardIds,
|
||||
to = target,
|
||||
toArea = Card.PlayerEquip,
|
||||
moveReason = fk.ReasonUse,
|
||||
|
@ -939,8 +1046,8 @@ function Room:useCard(cardUseEvent)
|
|||
end
|
||||
|
||||
break
|
||||
elseif Fk:getCardById(cardUseEvent.cardId).sub_type == Card.SubtypeDelayedTrick then
|
||||
if self:getCardArea(cardUseEvent.cardId) ~= Card.Processing then
|
||||
elseif cardUseEvent.card.sub_type == Card.SubtypeDelayedTrick then
|
||||
if #realCardIds == 0 then
|
||||
break
|
||||
end
|
||||
|
||||
|
@ -948,14 +1055,14 @@ function Room:useCard(cardUseEvent)
|
|||
if not self:getPlayerById(target).dead then
|
||||
local findSameCard = false
|
||||
for _, cardId in ipairs(self:getPlayerById(target):getCardIds(Player.Judge)) do
|
||||
if Fk:getCardById(cardId).trueName == Fk:getCardById(cardUseEvent.cardId) then
|
||||
if Fk:getCardById(cardId).trueName == cardUseEvent.card.trueName then
|
||||
findSameCard = true
|
||||
end
|
||||
end
|
||||
|
||||
if not findSameCard then
|
||||
self:moveCards({
|
||||
ids = { cardUseEvent.cardId },
|
||||
ids = realCardIds,
|
||||
to = target,
|
||||
toArea = Card.PlayerJudge,
|
||||
moveReason = fk.ReasonUse,
|
||||
|
@ -966,7 +1073,7 @@ function Room:useCard(cardUseEvent)
|
|||
end
|
||||
|
||||
self:moveCards({
|
||||
ids = { cardUseEvent.cardId },
|
||||
ids = realCardIds,
|
||||
toArea = Card.DiscardPile,
|
||||
moveReason = fk.ReasonPutIntoDiscardPile,
|
||||
})
|
||||
|
@ -974,13 +1081,13 @@ function Room:useCard(cardUseEvent)
|
|||
break
|
||||
end
|
||||
|
||||
if Fk:getCardById(cardUseEvent.cardId).skill then
|
||||
if cardUseEvent.card.skill then
|
||||
---@type CardEffectEvent
|
||||
local cardEffectEvent = {
|
||||
from = cardUseEvent.from,
|
||||
tos = cardUseEvent.tos,
|
||||
cardId = cardUseEvent.cardId,
|
||||
toCardId = cardUseEvent.toCardId,
|
||||
card = cardUseEvent.card,
|
||||
toCard = cardUseEvent.toCard,
|
||||
responseToEvent = cardUseEvent.responseToEvent,
|
||||
nullifiedTargets = cardUseEvent.nullifiedTargets,
|
||||
disresponsiveList = cardUseEvent.disresponsiveList,
|
||||
|
@ -989,7 +1096,7 @@ function Room:useCard(cardUseEvent)
|
|||
cardIdsResponded = cardUseEvent.nullifiedTargets,
|
||||
}
|
||||
|
||||
if cardUseEvent.toCardId ~= nil then
|
||||
if cardUseEvent.toCard ~= nil then
|
||||
self:doCardEffect(cardEffectEvent)
|
||||
else
|
||||
local collaboratorsIndex = {}
|
||||
|
@ -1034,9 +1141,11 @@ function Room:useCard(cardUseEvent)
|
|||
end
|
||||
|
||||
self.logic:trigger(fk.CardUseFinished, self:getPlayerById(cardUseEvent.from), cardUseEvent)
|
||||
if self:getCardArea(cardUseEvent.cardId) == Card.Processing then
|
||||
|
||||
local leftRealCardIds = self:getSubcardsByRule(cardUseEvent.card, { Card.Processing })
|
||||
if #leftRealCardIds > 0 then
|
||||
self:moveCards({
|
||||
ids = { cardUseEvent.cardId },
|
||||
ids = leftRealCardIds,
|
||||
toArea = Card.DiscardPile,
|
||||
moveReason = fk.ReasonPutIntoDiscardPile,
|
||||
})
|
||||
|
@ -1053,7 +1162,7 @@ function Room:doCardEffect(cardEffectEvent)
|
|||
break
|
||||
end
|
||||
|
||||
if not cardEffectEvent.toCardId and (not (self:getPlayerById(cardEffectEvent.to):isAlive() and cardEffectEvent.to) or #self:deadPlayerFilter(TargetGroup:getRealTargets(cardEffectEvent.tos)) == 0) then
|
||||
if not cardEffectEvent.toCard and (not (self:getPlayerById(cardEffectEvent.to):isAlive() and cardEffectEvent.to) or #self:deadPlayerFilter(TargetGroup:getRealTargets(cardEffectEvent.tos)) == 0) then
|
||||
break
|
||||
end
|
||||
|
||||
|
@ -1066,7 +1175,7 @@ function Room:doCardEffect(cardEffectEvent)
|
|||
end
|
||||
|
||||
if event == fk.PreCardEffect then
|
||||
if Fk:getCardById(cardEffectEvent.cardId).name == 'slash' and
|
||||
if cardEffectEvent.card.name == 'slash' and
|
||||
not (
|
||||
cardEffectEvent.disresponsive or
|
||||
cardEffectEvent.unoffsetable or
|
||||
|
@ -1076,11 +1185,11 @@ function Room:doCardEffect(cardEffectEvent)
|
|||
local to = self:getPlayerById(cardEffectEvent.to)
|
||||
local use = self:askForUseCard(to, "jink")
|
||||
if use then
|
||||
use.toCardId = cardEffectEvent.cardId
|
||||
use.toCard = cardEffectEvent.card
|
||||
use.responseToEvent = cardEffectEvent
|
||||
self:useCard(use)
|
||||
end
|
||||
elseif Fk:getCardById(cardEffectEvent.cardId).type == Card.TypeTrick then
|
||||
elseif cardEffectEvent.card.type == Card.TypeTrick then
|
||||
local players = {}
|
||||
for _, p in ipairs(self.alive_players) do
|
||||
local cards = p.player_cards[Player.Hand]
|
||||
|
@ -1094,7 +1203,7 @@ function Room:doCardEffect(cardEffectEvent)
|
|||
|
||||
local use = self:askForNullification(players)
|
||||
if use then
|
||||
use.toCardId = cardEffectEvent.cardId
|
||||
use.toCard = cardEffectEvent.card
|
||||
use.responseToEvent = cardEffectEvent
|
||||
self:useCard(use)
|
||||
end
|
||||
|
@ -1102,9 +1211,8 @@ function Room:doCardEffect(cardEffectEvent)
|
|||
end
|
||||
|
||||
if event == fk.CardEffecting then
|
||||
local cardEffecting = Fk:getCardById(cardEffectEvent.cardId)
|
||||
if cardEffecting.skill then
|
||||
cardEffecting.skill:onEffect(self, cardEffectEvent)
|
||||
if cardEffectEvent.card.skill then
|
||||
cardEffectEvent.card.skill:onEffect(self, cardEffectEvent)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -1113,22 +1221,48 @@ end
|
|||
---@param cardResponseEvent CardResponseEvent
|
||||
function Room:responseCard(cardResponseEvent)
|
||||
local from = cardResponseEvent.customFrom or cardResponseEvent.from
|
||||
local card = cardResponseEvent.card
|
||||
local cardIds = self:getSubcardsByRule(card)
|
||||
|
||||
if card:isVirtual() then
|
||||
if #cardIds == 0 then
|
||||
self:sendLog{
|
||||
type = "#ResponsePlayV0Card",
|
||||
from = from,
|
||||
arg = card:toLogString(),
|
||||
}
|
||||
else
|
||||
self:sendLog{
|
||||
type = "#ResponsePlayVCard",
|
||||
from = from,
|
||||
card = cardIds,
|
||||
arg = card:toLogString(),
|
||||
}
|
||||
end
|
||||
else
|
||||
self:sendLog{
|
||||
type = "#ResponsePlayCard",
|
||||
from = from,
|
||||
card = cardIds,
|
||||
}
|
||||
end
|
||||
self:moveCards({
|
||||
ids = { cardResponseEvent.cardId },
|
||||
ids = cardIds,
|
||||
from = from,
|
||||
toArea = Card.Processing,
|
||||
moveReason = fk.ReasonResonpse,
|
||||
})
|
||||
|
||||
self:setEmotion(self:getPlayerById(from), Fk:getCardById(cardResponseEvent.cardId).name)
|
||||
self:setEmotion(self:getPlayerById(from), card.name)
|
||||
|
||||
for _, event in ipairs({ fk.PreCardRespond, fk.CardResponding, fk.CardRespondFinished }) do
|
||||
self.logic:trigger(event, self:getPlayerById(cardResponseEvent.from), cardResponseEvent)
|
||||
end
|
||||
|
||||
if self:getCardArea(cardResponseEvent.cardId) == Card.Processing or cardResponseEvent.skipDrop then
|
||||
local realCardIds = self:getSubcardsByRule(cardResponseEvent.card, { Card.Processing })
|
||||
if #realCardIds > 0 and not cardResponseEvent.skipDrop then
|
||||
self:moveCards({
|
||||
ids = { cardResponseEvent.cardId },
|
||||
ids = realCardIds,
|
||||
toArea = Card.DiscardPile,
|
||||
moveReason = fk.ReasonPutIntoDiscardPile,
|
||||
})
|
||||
|
@ -1780,6 +1914,25 @@ function Room:gameOver(winner)
|
|||
coroutine.yield()
|
||||
end
|
||||
|
||||
---@param card Card
|
||||
---@param fromAreas CardArea[]|null
|
||||
---@return integer[]
|
||||
function Room:getSubcardsByRule(card, fromAreas)
|
||||
if card:isVirtual() and #card.subcards == 0 then
|
||||
return {}
|
||||
end
|
||||
|
||||
local cardIds = {}
|
||||
fromAreas = fromAreas or {}
|
||||
for _, cardId in ipairs(card:isVirtual() and card.subcards or { card.id }) do
|
||||
if #fromAreas == 0 or table.contains(fromAreas, self:getCardArea(cardId)) then
|
||||
table.insert(cardIds, cardId)
|
||||
end
|
||||
end
|
||||
|
||||
return cardIds
|
||||
end
|
||||
|
||||
function CreateRoom(_room)
|
||||
RoomInstance = Room:new(_room)
|
||||
end
|
||||
|
|
|
@ -10,13 +10,13 @@
|
|||
---@alias DyingStruct { who: integer, damage: DamageStruct }
|
||||
---@alias DeathStruct { who: integer, damage: DamageStruct }
|
||||
|
||||
---@alias CardUseStruct { from: integer, tos: TargetGroup, cardId: integer, toCardId: integer|null, responseToEvent: CardUseStruct|null, nullifiedTargets: interger[]|null, extraUse: boolean|null, disresponsiveList: integer[]|null, unoffsetableList: integer[]|null, addtionalDamage: integer|null, customFrom: integer|null, cardIdsResponded: integer[]|null }
|
||||
---@alias AimStruct { from: integer, cardId: integer, tos: AimGroup, to: integer, subTargets: integer[]|null, targetGroup: TargetGroup|null, nullifiedTargets: integer[]|null, firstTarget: boolean, additionalDamage: integer|null, disresponsive: boolean|null, unoffsetableList: boolean|null }
|
||||
---@alias CardEffectEvent { from: integer, to: integer, subTargets: integer[]|null, tos: TargetGroup, cardId: integer, toCardId: integer|null, responseToEvent: CardUseStruct|null, nullifiedTargets: interger[]|null, extraUse: boolean|null, disresponsiveList: integer[]|null, unoffsetableList: integer[]|null, addtionalDamage: integer|null, customFrom: integer|null, cardIdsResponded: integer[]|null, disresponsive: boolean|null, unoffsetable: boolean|null }
|
||||
---@alias CardUseStruct { from: integer, tos: TargetGroup, card: Card, toCard: Card|null, responseToEvent: CardUseStruct|null, nullifiedTargets: interger[]|null, extraUse: boolean|null, disresponsiveList: integer[]|null, unoffsetableList: integer[]|null, addtionalDamage: integer|null, customFrom: integer|null, cardsResponded: Card[]|null }
|
||||
---@alias AimStruct { from: integer, card: Card, tos: AimGroup, to: integer, subTargets: integer[]|null, targetGroup: TargetGroup|null, nullifiedTargets: integer[]|null, firstTarget: boolean, additionalDamage: integer|null, disresponsive: boolean|null, unoffsetableList: boolean|null }
|
||||
---@alias CardEffectEvent { from: integer, to: integer, subTargets: integer[]|null, tos: TargetGroup, card: Card, toCard: Card|null, responseToEvent: CardUseStruct|null, nullifiedTargets: interger[]|null, extraUse: boolean|null, disresponsiveList: integer[]|null, unoffsetableList: integer[]|null, addtionalDamage: integer|null, customFrom: integer|null, cardsResponded: Card[]|null, disresponsive: boolean|null, unoffsetable: boolean|null }
|
||||
---@alias SkillEffectEvent { from: integer, tos: integer[], cards: integer[] }
|
||||
|
||||
---@alias JudgeStruct { who: ServerPlayer, card: Card, reason: string }
|
||||
---@alias CardResponseEvent { from: integer, cardId: integer, responseToEvent: CardEffectEvent|null, skipDrop: boolean|null, customFrom: integer|null }
|
||||
---@alias CardResponseEvent { from: integer, card: Card, responseToEvent: CardEffectEvent|null, skipDrop: boolean|null, customFrom: integer|null }
|
||||
|
||||
---@alias CardMoveReason integer
|
||||
|
||||
|
|
|
@ -125,7 +125,7 @@ GameRule = fk.CreateTriggerSkill{
|
|||
|
||||
---@type CardEffectEvent
|
||||
local effect_data = {
|
||||
cardId = cards[i],
|
||||
card = card,
|
||||
to = player.id,
|
||||
tos = { {player.id} },
|
||||
}
|
||||
|
@ -144,26 +144,8 @@ GameRule = fk.CreateTriggerSkill{
|
|||
local result = room:doRequest(player, "PlayCard", player.id)
|
||||
if result == "" then break end
|
||||
|
||||
local data = json.decode(result)
|
||||
local card = data.card
|
||||
local targets = data.targets
|
||||
if type(card) == "string" then
|
||||
local card_data = json.decode(card)
|
||||
local skill = Fk.skills[card_data.skill]
|
||||
local selected_cards = card_data.subcards
|
||||
skill:onEffect(room, {
|
||||
from = player.id,
|
||||
cards = selected_cards,
|
||||
tos = targets,
|
||||
})
|
||||
else
|
||||
local use = {} ---@type CardUseStruct
|
||||
use.from = player.id
|
||||
use.tos = {}
|
||||
for _, target in ipairs(targets) do
|
||||
table.insert(use.tos, { target })
|
||||
end
|
||||
use.cardId = card
|
||||
local use = room:handleUseCardReply(player, result)
|
||||
if use then
|
||||
room:useCard(use)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -11,6 +11,12 @@ Fk:loadTranslationTable{
|
|||
["qun"] = "群",
|
||||
}
|
||||
|
||||
Fk:loadTranslationTable{
|
||||
["black"] = "黑色",
|
||||
["red"] = '<font color="#CC3131">红色</font>',
|
||||
["nocolor"] = '<font color="grey">无色</font>',
|
||||
}
|
||||
|
||||
local caocao = General:new(extension, "caocao", "wei", 4)
|
||||
Fk:loadTranslationTable{
|
||||
["caocao"] = "曹操",
|
||||
|
@ -74,9 +80,27 @@ Fk:loadTranslationTable{
|
|||
["liubei"] = "刘备",
|
||||
}
|
||||
|
||||
local wusheng = fk.CreateViewAsSkill{
|
||||
name = "wusheng",
|
||||
pattern = "slash",
|
||||
card_filter = function(self, to_select, selected)
|
||||
if #selected == 1 then return false end
|
||||
return Fk:getCardById(to_select).color == Card.Red
|
||||
end,
|
||||
view_as = function(self, cards)
|
||||
if #cards ~= 1 then
|
||||
return nil
|
||||
end
|
||||
local c = Fk:cloneCard("slash")
|
||||
c:addSubcard(cards[1])
|
||||
return c
|
||||
end,
|
||||
}
|
||||
local guanyu = General:new(extension, "guanyu", "shu", 4)
|
||||
guanyu:addSkill(wusheng)
|
||||
Fk:loadTranslationTable{
|
||||
["guanyu"] = "关羽",
|
||||
["wusheng"] = "武圣",
|
||||
}
|
||||
|
||||
local zhangfei = General:new(extension, "zhangfei", "shu", 4)
|
||||
|
|
|
@ -195,6 +195,7 @@ local dismantlement = fk.CreateTrickCard{
|
|||
}
|
||||
Fk:loadTranslationTable{
|
||||
["dismantlement"] = "过河拆桥",
|
||||
["dismantlement_skill"] = "过河拆桥",
|
||||
}
|
||||
|
||||
extension:addCards({
|
||||
|
@ -241,6 +242,7 @@ local snatch = fk.CreateTrickCard{
|
|||
}
|
||||
Fk:loadTranslationTable{
|
||||
["snatch"] = "顺手牵羊",
|
||||
["snatch_skill"] = "顺手牵羊",
|
||||
}
|
||||
|
||||
extension:addCards({
|
||||
|
@ -275,11 +277,11 @@ local duelSkill = fk.CreateActiveSkill{
|
|||
break
|
||||
end
|
||||
|
||||
local cardIdResponded = room:askForResponse(currentResponser, 'slash')
|
||||
if cardIdResponded then
|
||||
local cardResponded = room:askForResponse(currentResponser, 'slash')
|
||||
if cardResponded then
|
||||
room:responseCard({
|
||||
from = currentResponser.id,
|
||||
cardId = cardIdResponded,
|
||||
card = cardResponded,
|
||||
responseToEvent = effect,
|
||||
})
|
||||
else
|
||||
|
@ -336,17 +338,13 @@ local collateralSkill = fk.CreateActiveSkill{
|
|||
cardUseEvent.tos = { { cardUseEvent.tos[1][1], cardUseEvent.tos[2][1] } }
|
||||
end,
|
||||
on_effect = function(self, room, effect)
|
||||
local cardIdResponded = nil
|
||||
if not (effect.disresponsive or table.contains(effect.disresponsiveList or {}, effect.to)) then
|
||||
cardIdResponded = room:askForResponse(room:getPlayerById(effect.to), 'slash')
|
||||
end
|
||||
local use = room:askForUseCard(
|
||||
room:getPlayerById(effect.to),
|
||||
"slash", nil, nil, nil, { must_targets = effect.subTargets }
|
||||
)
|
||||
|
||||
if cardIdResponded then
|
||||
room:useCard({
|
||||
from = effect.to,
|
||||
tos = { { effect.subTargets[1] } },
|
||||
cardId = cardIdResponded,
|
||||
})
|
||||
if use then
|
||||
room:useCard(use)
|
||||
else
|
||||
room:obtainCard(effect.from, room:getPlayerById(effect.to):getEquipment(Card.SubtypeWeapon), true, fk.ReasonGive)
|
||||
end
|
||||
|
@ -436,15 +434,15 @@ local savageAssaultSkill = fk.CreateActiveSkill{
|
|||
end
|
||||
end,
|
||||
on_effect = function(self, room, effect)
|
||||
local cardIdResponded = nil
|
||||
local cardResponded = nil
|
||||
if not (effect.disresponsive or table.contains(effect.disresponsiveList or {}, effect.to)) then
|
||||
cardIdResponded = room:askForResponse(room:getPlayerById(effect.to), 'slash')
|
||||
cardResponded = room:askForResponse(room:getPlayerById(effect.to), 'slash')
|
||||
end
|
||||
|
||||
if cardIdResponded then
|
||||
if cardResponded then
|
||||
room:responseCard({
|
||||
from = effect.to,
|
||||
cardId = cardIdResponded,
|
||||
card = cardResponded,
|
||||
responseToEvent = effect,
|
||||
})
|
||||
else
|
||||
|
@ -485,15 +483,15 @@ local archeryAttackSkill = fk.CreateActiveSkill{
|
|||
end
|
||||
end,
|
||||
on_effect = function(self, room, effect)
|
||||
local cardIdResponded = nil
|
||||
local cardResponded = nil
|
||||
if not (effect.disresponsive or table.contains(effect.disresponsiveList or {}, effect.to)) then
|
||||
cardIdResponded = room:askForResponse(room:getPlayerById(effect.to), 'jink')
|
||||
cardResponded = room:askForResponse(room:getPlayerById(effect.to), 'jink')
|
||||
end
|
||||
|
||||
if cardIdResponded then
|
||||
if cardResponded then
|
||||
room:responseCard({
|
||||
from = effect.to,
|
||||
cardId = cardIdResponded,
|
||||
card = cardResponded,
|
||||
responseToEvent = effect,
|
||||
})
|
||||
else
|
||||
|
@ -628,7 +626,7 @@ local lightningSkill = fk.CreateActiveSkill{
|
|||
local to = room:getPlayerById(effect.to)
|
||||
local nextp = to:getNextAlive()
|
||||
room:moveCards{
|
||||
ids = { effect.cardId },
|
||||
ids = room:getSubcardsByRule(effect.card, { Card.Processing }),
|
||||
to = nextp.id,
|
||||
toArea = Card.PlayerJudge,
|
||||
moveReason = fk.ReasonPut
|
||||
|
@ -686,7 +684,7 @@ local indulgenceSkill = fk.CreateActiveSkill{
|
|||
end,
|
||||
on_nullified = function(self, room, effect)
|
||||
room:moveCards{
|
||||
ids = { effect.cardId },
|
||||
ids = room:getSubcardsByRule(effect.card, { Card.Processing }),
|
||||
toArea = Card.DiscardPile,
|
||||
moveReason = fk.ReasonPutIntoDiscardPile
|
||||
}
|
||||
|
|
|
@ -26,6 +26,7 @@ Item {
|
|||
property var selected_targets: []
|
||||
property string responding_card
|
||||
property bool respond_play: false
|
||||
property var extra_data: ({})
|
||||
|
||||
Image {
|
||||
source: AppPath + "/image/gamebg"
|
||||
|
|
|
@ -13,7 +13,6 @@ RowLayout {
|
|||
|
||||
property bool selected: selfPhoto.selected
|
||||
|
||||
property bool is_pending: false
|
||||
property string pending_skill: ""
|
||||
property var pending_card
|
||||
property var pendings: [] // int[], store cid
|
||||
|
@ -116,7 +115,7 @@ RowLayout {
|
|||
if (cname) {
|
||||
let ids = [], cards = handcardAreaItem.cards;
|
||||
for (let i = 0; i < cards.length; i++) {
|
||||
if (cards[i].name === cname)
|
||||
if (JSON.parse(Backend.callLuaFunction("CardFitPattern", [cards[i].name, cname])))
|
||||
ids.push(cards[i].cid);
|
||||
}
|
||||
handcardAreaItem.enableCards(ids);
|
||||
|
@ -242,7 +241,10 @@ RowLayout {
|
|||
|
||||
function enableSkills(cname) {
|
||||
if (cname) {
|
||||
// TODO: vs skill
|
||||
for (let i = 0; i < skillButtons.count; i++) {
|
||||
let item = skillButtons.itemAt(i);
|
||||
item.enabled = JSON.parse(Backend.callLuaFunction("SkillFitPattern", [item.orig, cname]));
|
||||
}
|
||||
return;
|
||||
}
|
||||
for (let i = 0; i < skillButtons.count; i++) {
|
||||
|
|
|
@ -298,6 +298,14 @@ function enableTargets(card) { // card: int | { skill: string, subcards: int[] }
|
|||
okButton.enabled = JSON.parse(Backend.callLuaFunction(
|
||||
"CardFeasible", [card, selected_targets]
|
||||
));
|
||||
if (okButton.enabled) {
|
||||
if (roomScene.extra_data instanceof Object) {
|
||||
let must = roomScene.extra_data.must_targets;
|
||||
if (must instanceof Array) {
|
||||
okButton.enabled = (must.length === 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
all_photos.forEach(photo => {
|
||||
photo.state = "normal";
|
||||
|
@ -337,6 +345,16 @@ function updateSelectedTargets(playerid, selected) {
|
|||
okButton.enabled = JSON.parse(Backend.callLuaFunction(
|
||||
"CardFeasible", [card, selected_targets]
|
||||
));
|
||||
if (okButton.enabled) {
|
||||
if (roomScene.extra_data instanceof Object) {
|
||||
let must = roomScene.extra_data.must_targets;
|
||||
if (must instanceof Array) {
|
||||
okButton.enabled = (must.filter((val) => {
|
||||
return selected_targets.indexOf(val) === -1;
|
||||
}).length === 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
all_photos.forEach(photo => {
|
||||
photo.state = "normal";
|
||||
|
@ -628,14 +646,19 @@ callbacks["GameLog"] = function(jsonData) {
|
|||
}
|
||||
|
||||
callbacks["AskForUseCard"] = function(jsonData) {
|
||||
// jsonData: card, prompt, cancelable, {}
|
||||
// jsonData: card, pattern, prompt, cancelable, {}
|
||||
let data = JSON.parse(jsonData);
|
||||
let cardname = data[0];
|
||||
let prompt = data[1];
|
||||
let pattern = data[1];
|
||||
let prompt = data[2];
|
||||
let extra_data = data[4];
|
||||
if (extra_data != null) {
|
||||
roomScene.extra_data = extra_data;
|
||||
}
|
||||
|
||||
roomScene.promptText = Backend.translate(prompt)
|
||||
.arg(Backend.translate(cardname));
|
||||
roomScene.responding_card = cardname;
|
||||
roomScene.responding_card = pattern;
|
||||
roomScene.respond_play = false;
|
||||
roomScene.state = "responding";
|
||||
okButton.enabled = false;
|
||||
|
@ -643,14 +666,15 @@ callbacks["AskForUseCard"] = function(jsonData) {
|
|||
}
|
||||
|
||||
callbacks["AskForResponseCard"] = function(jsonData) {
|
||||
// jsonData: card, prompt, cancelable, {}
|
||||
// jsonData: card_name, pattern, prompt, cancelable, {}
|
||||
let data = JSON.parse(jsonData);
|
||||
let cardname = data[0];
|
||||
let prompt = data[1];
|
||||
let pattern = data[1];
|
||||
let prompt = data[2];
|
||||
|
||||
roomScene.promptText = Backend.translate(prompt)
|
||||
.arg(Backend.translate(cardname));
|
||||
roomScene.responding_card = cardname;
|
||||
roomScene.responding_card = pattern;
|
||||
roomScene.respond_play = true;
|
||||
roomScene.state = "responding";
|
||||
okButton.enabled = false;
|
||||
|
|
Loading…
Reference in New Issue
Block a user