卡牌标记清除后缀+pattern相关fix (#220)

- 修复了翻译只替换一次参数(%src之类的)的bug
- 添加了卡牌专属的清除后缀,现在cheat获得的牌自带信仰之力
- 补齐了neg判定相关的函数
- 添加multiple_targets,标记多目标牌
- 添加复原武将牌的描述
- 房间名带省略号

---------

Co-authored-by: notify <notify-ctrl@qq.com>
This commit is contained in:
YoumuKon 2023-07-11 23:16:46 +08:00 committed by GitHub
parent fdf2ccc75b
commit e9a81cd185
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
19 changed files with 100 additions and 29 deletions

View File

@ -64,8 +64,9 @@ Item {
Text { Text {
horizontalAlignment: Text.AlignLeft horizontalAlignment: Text.AlignLeft
Layout.fillWidth: true Layout.fillWidth: true
text: roomName + (hasPassword ? "(有密码)" : "") text: (hasPassword ? Backend.translate("Has Password") : "") + roomName
font.pixelSize: 20 font.pixelSize: 20
elide: Label.ElideRight
} }
Text { Text {

View File

@ -1066,10 +1066,10 @@ function processPrompt(prompt) {
let raw = Backend.translate(data[0]); let raw = Backend.translate(data[0]);
const src = parseInt(data[1]); const src = parseInt(data[1]);
const dest = parseInt(data[2]); const dest = parseInt(data[2]);
if (raw.match("%src")) raw = raw.replace("%src", Backend.translate(getPhoto(src).general)); if (raw.match("%src")) raw = raw.replace(/%src/g, Backend.translate(getPhoto(src).general));
if (raw.match("%dest")) raw = raw.replace("%dest", Backend.translate(getPhoto(dest).general)); if (raw.match("%dest")) raw = raw.replace(/%dest/g, Backend.translate(getPhoto(dest).general));
if (raw.match("%arg")) raw = raw.replace("%arg", Backend.translate(data[3])); if (raw.match("%arg")) raw = raw.replace(/%arg/g, Backend.translate(data[3]));
if (raw.match("%arg2")) raw = raw.replace("%arg2", Backend.translate(data[4])); if (raw.match("%arg2")) raw = raw.replace(/%arg2/g, Backend.translate(data[4]));
return raw; return raw;
} }

View File

@ -19,10 +19,10 @@ GraphicsBox {
let raw = Backend.translate(data[0]); let raw = Backend.translate(data[0]);
const src = parseInt(data[1]); const src = parseInt(data[1]);
const dest = parseInt(data[2]); const dest = parseInt(data[2]);
if (raw.match("%src")) raw = raw.replace("%src", Backend.translate(getPhoto(src).general)); if (raw.match("%src")) raw = raw.replace(/%src/g, Backend.translate(getPhoto(src).general));
if (raw.match("%dest")) raw = raw.replace("%dest", Backend.translate(getPhoto(dest).general)); if (raw.match("%dest")) raw = raw.replace(/%dest/g, Backend.translate(getPhoto(dest).general));
if (raw.match("%arg")) raw = raw.replace("%arg", Backend.translate(data[3])); if (raw.match("%arg")) raw = raw.replace(/%arg/g, Backend.translate(data[3]));
if (raw.match("%arg2")) raw = raw.replace("%arg2", Backend.translate(data[4])); if (raw.match("%arg2")) raw = raw.replace(/%arg2/g, Backend.translate(data[4]));
return raw; return raw;
} }

View File

@ -276,10 +276,10 @@ RowLayout {
let raw = Backend.translate(data[0]); let raw = Backend.translate(data[0]);
const src = parseInt(data[1]); const src = parseInt(data[1]);
const dest = parseInt(data[2]); const dest = parseInt(data[2]);
if (raw.match("%src")) raw = raw.replace("%src", Backend.translate(getPhoto(src).general)); if (raw.match("%src")) raw = raw.replace(/%src/g, Backend.translate(getPhoto(src).general));
if (raw.match("%dest")) raw = raw.replace("%dest", Backend.translate(getPhoto(dest).general)); if (raw.match("%dest")) raw = raw.replace(/%dest/g, Backend.translate(getPhoto(dest).general));
if (raw.match("%arg")) raw = raw.replace("%arg", Backend.translate(data[3])); if (raw.match("%arg")) raw = raw.replace(/%arg/g, Backend.translate(data[3]));
if (raw.match("%arg2")) raw = raw.replace("%arg2", Backend.translate(data[4])); if (raw.match("%arg2")) raw = raw.replace(/%arg2/g, Backend.translate(data[4]));
return raw; return raw;
} }

View File

@ -16,10 +16,10 @@ MetroButton {
let raw = Backend.translate(data[0]); let raw = Backend.translate(data[0]);
const src = parseInt(data[1]); const src = parseInt(data[1]);
const dest = parseInt(data[2]); const dest = parseInt(data[2]);
if (raw.match("%src")) raw = raw.replace("%src", Backend.translate(getPhoto(src).general)); if (raw.match("%src")) raw = raw.replace(/%src/g, Backend.translate(getPhoto(src).general));
if (raw.match("%dest")) raw = raw.replace("%dest", Backend.translate(getPhoto(dest).general)); if (raw.match("%dest")) raw = raw.replace(/%dest/g, Backend.translate(getPhoto(dest).general));
if (raw.match("%arg")) raw = raw.replace("%arg", Backend.translate(data[3])); if (raw.match("%arg")) raw = raw.replace(/%arg/g, Backend.translate(data[3]));
if (raw.match("%arg2")) raw = raw.replace("%arg2", Backend.translate(data[4])); if (raw.match("%arg2")) raw = raw.replace(/%arg2/g, Backend.translate(data[4]));
return raw; return raw;
} }

View File

@ -36,6 +36,7 @@ Fk:loadTranslationTable{
["No enough generals"] = "可用武将不足!", ["No enough generals"] = "可用武将不足!",
["Operation timeout"] = "操作时长(秒)", ["Operation timeout"] = "操作时长(秒)",
["Luck Card Times"] = "手气卡次数", ["Luck Card Times"] = "手气卡次数",
["Has Password"] = "(有密码)",
["Room Password"] = "房间密码", ["Room Password"] = "房间密码",
["Please input room's password"] = "请输入房间的密码", ["Please input room's password"] = "请输入房间的密码",
["Add Robot"] = "添加机器人", ["Add Robot"] = "添加机器人",
@ -267,13 +268,15 @@ Fk:loadTranslationTable{
["chained"] = "横置", ["chained"] = "横置",
["un-chained"] = "竖置", ["un-chained"] = "竖置",
["not-chained"] = "重置", ["reset-general"] = "重置",
["yang"] = "", ["yang"] = "",
["yin"] = "", ["yin"] = "",
["quest_succeed"] = "成功", ["quest_succeed"] = "成功",
["quest_failed"] = "失败", ["quest_failed"] = "失败",
["card"] = "",
["hand_card"] = "手牌",
["pile_draw"] = "牌堆", ["pile_draw"] = "牌堆",
["pile_discard"] = "弃牌堆", ["pile_discard"] = "弃牌堆",
["processing_area"] = "处理区", ["processing_area"] = "处理区",
@ -379,6 +382,7 @@ Fk:loadTranslationTable{
["#GuanxingResult"] = "%from 的观星结果为 %arg 上 %arg2 下", ["#GuanxingResult"] = "%from 的观星结果为 %arg 上 %arg2 下",
["#ChainStateChange"] = "%from %arg 了武将牌", ["#ChainStateChange"] = "%from %arg 了武将牌",
["#ChainDamage"] = "%from 处于连环状态,将受到传导的伤害", ["#ChainDamage"] = "%from 处于连环状态,将受到传导的伤害",
["#ResetGeneral"] = "%from 复原了武将牌",
} }
-- card footnote -- card footnote

View File

@ -23,6 +23,7 @@
---@field public skill Skill @ 技能(用于实现卡牌效果) ---@field public skill Skill @ 技能(用于实现卡牌效果)
---@field public special_skills string[] | nil @ 衍生技能,如重铸 ---@field public special_skills string[] | nil @ 衍生技能,如重铸
---@field public is_damage_card boolean @ 是否为会造成伤害的牌 ---@field public is_damage_card boolean @ 是否为会造成伤害的牌
---@field public multiple_targets boolean @ 是否为指定多个目标的牌
---@field public is_derived boolean|null @ 判断是否为衍生牌 ---@field public is_derived boolean|null @ 判断是否为衍生牌
local Card = class("Card") local Card = class("Card")
@ -145,6 +146,7 @@ function Card:clone(suit, number)
newCard.equip_skill = self.equip_skill newCard.equip_skill = self.equip_skill
newCard.attack_range = self.attack_range newCard.attack_range = self.attack_range
newCard.is_damage_card = self.is_damage_card newCard.is_damage_card = self.is_damage_card
newCard.multiple_targets = self.multiple_targets
newCard.is_derived = self.is_derived newCard.is_derived = self.is_derived
return newCard return newCard
end end

View File

@ -117,6 +117,7 @@ local function matchCard(matcher, card)
end end
local function hasNegIntersection(a, b) local function hasNegIntersection(a, b)
-- 注意这里是拿a.neg和b比
local neg_pass = false local neg_pass = false
-- 第一次比较: 比较neg和正常值如有不同即认为可以匹配 -- 第一次比较: 比较neg和正常值如有不同即认为可以匹配
@ -134,11 +135,11 @@ local function hasNegIntersection(a, b)
-- 第二次比较: 比较双方neg -- 第二次比较: 比较双方neg
-- 比如 ^jink 可以匹配 ^slash -- 比如 ^jink 可以匹配 ^slash
-- 暂时想不出好方案 -- 没法比
end end
local function hasIntersection(a, b) local function hasIntersection(a, b)
if a == nil or b == nil then if a == nil or b == nil or (#a + #b == 0) then
return true return true
end end
@ -151,7 +152,6 @@ local function hasIntersection(a, b)
return true return true
end end
end end
local neg_pass = hasNegIntersection(a, b) or hasNegIntersection(b, a) local neg_pass = hasNegIntersection(a, b) or hasNegIntersection(b, a)
return neg_pass return neg_pass

View File

@ -394,7 +394,7 @@ function Player:getAttackRange()
local status_skills = Fk:currentRoom().status_skills[AttackRangeSkill] or Util.DummyTable local status_skills = Fk:currentRoom().status_skills[AttackRangeSkill] or Util.DummyTable
for _, skill in ipairs(status_skills) do for _, skill in ipairs(status_skills) do
local correct = skill:getCorrect(self) local correct = skill:getCorrect(self)
baseAttackRange = baseAttackRange + correct baseAttackRange = baseAttackRange + (correct or 0)
end end
return math.max(baseAttackRange, 0) return math.max(baseAttackRange, 0)
@ -460,8 +460,7 @@ function Player:distanceTo(other, mode, ignore_dead)
ret = fixed ret = fixed
break break
end end
if correct == nil then correct = 0 end ret = ret + (correct or 0)
ret = ret + correct
end end
if self.fixedDistance[other] then if self.fixedDistance[other] then

View File

@ -419,7 +419,9 @@ end
---@class CardSpec: Card ---@class CardSpec: Card
---@field public skill Skill ---@field public skill Skill
---@field public equip_skill Skill ---@field public equip_skill Skill
---@field public special_skills string[] | nil
---@field public is_damage_card boolean ---@field public is_damage_card boolean
---@field public multiple_targets boolean
local defaultCardSkill = fk.CreateActiveSkill{ local defaultCardSkill = fk.CreateActiveSkill{
name = "default_card_skill", name = "default_card_skill",
@ -443,6 +445,7 @@ local function readCardSpecToCard(card, spec)
card.skill.cardSkill = true card.skill.cardSkill = true
card.special_skills = spec.special_skills card.special_skills = spec.special_skills
card.is_damage_card = spec.is_damage_card card.is_damage_card = spec.is_damage_card
card.multiple_targets = spec.multiple_targets
end end
---@param spec CardSpec ---@param spec CardSpec

View File

@ -172,6 +172,14 @@ GameEvent.cleaners[GameEvent.Round] = function(self)
end end
end end
end end
for cid, cmark in pairs(room.card_marks) do
for name, _ in pairs(cmark) do
if name:endsWith("-round") then
room:setCardMark(Fk:getCardById(cid), name, 0)
end
end
end
end end
GameEvent.functions[GameEvent.Turn] = function(self) GameEvent.functions[GameEvent.Turn] = function(self)
@ -203,6 +211,14 @@ GameEvent.cleaners[GameEvent.Turn] = function(self)
end end
end end
for cid, cmark in pairs(room.card_marks) do
for name, _ in pairs(cmark) do
if name:endsWith("-turn") then
room:setCardMark(Fk:getCardById(cid), name, 0)
end
end
end
local current = room.current local current = room.current
local logic = room.logic local logic = room.logic
if self.interrupted then if self.interrupted then
@ -324,6 +340,14 @@ GameEvent.cleaners[GameEvent.Phase] = function(self)
end end
end end
for cid, cmark in pairs(room.card_marks) do
for name, _ in pairs(cmark) do
if name:endsWith("-phase") then
room:setCardMark(Fk:getCardById(cid), name, 0)
end
end
end
if self.interrupted then if self.interrupted then
room.logic:trigger(fk.EventPhaseEnd, player, nil, true) room.logic:trigger(fk.EventPhaseEnd, player, nil, true)
end end

View File

@ -124,6 +124,14 @@ GameEvent.functions[GameEvent.MoveCards] = function(self)
end end
local currentCard = Fk:getCardById(info.cardId) local currentCard = Fk:getCardById(info.cardId)
for name, _ in pairs(currentCard.mark) do
if name:endsWith("-inhand") and
realFromArea == Player.Hand and
data.from
then
self:setCardMark(currentCard, name, 0)
end
end
if if
data.toArea == Player.Equip and data.toArea == Player.Equip and
currentCard.type == Card.TypeEquip and currentCard.type == Card.TypeEquip and

View File

@ -28,7 +28,10 @@ MarkEnum.BypassTimesLimitTo = "BypassTimesLimitTo"
---@field BypassDistancesLimitTo string @ 对其使用牌无距离限制,可带清除标记后缀 ---@field BypassDistancesLimitTo string @ 对其使用牌无距离限制,可带清除标记后缀
MarkEnum.BypassDistancesLimitTo = "BypassDistancesLimitTo" MarkEnum.BypassDistancesLimitTo = "BypassDistancesLimitTo"
---@field UncompulsoryInvalidity string @ 非锁定技失效,可带清除标记后缀 ---@field UncompulsoryInvalidity string @ 非锁定技失效,可带清除标记后缀
MarkEnum.UncompulsoryInvalidity = "uncompulsoryInvalidity" MarkEnum.UncompulsoryInvalidity = "UncompulsoryInvalidity"
---@field TempMarkSuffix string[] @ 各种清除标记后缀 ---@field TempMarkSuffix string[] @ 各种清除标记后缀
MarkEnum.TempMarkSuffix = { "-phase", "-turn", "-round" } MarkEnum.TempMarkSuffix = { "-phase", "-turn", "-round" }
---@field CardTempMarkSuffix string[] @ 卡牌标记版本的清除标记后缀
MarkEnum.CardTempMarkSuffix = { "-phase", "-turn", "-round", "-inhand" }

View File

@ -573,8 +573,7 @@ function ServerPlayer:bury()
self:throwAllCards() self:throwAllCards()
self:throwAllMarks() self:throwAllMarks()
self:clearPiles() self:clearPiles()
self:setChainState(false) self:reset()
if not self.faceup then self:turnOver() end
end end
function ServerPlayer:throwAllCards(flag) function ServerPlayer:throwAllCards(flag)
@ -654,12 +653,21 @@ function ServerPlayer:setChainState(chained)
self.room:sendLog{ self.room:sendLog{
type = "#ChainStateChange", type = "#ChainStateChange",
from = self.id, from = self.id,
arg = self.chained and "chained" or "not-chained" arg = self.chained and "chained" or "un-chained"
} }
self.room.logic:trigger(fk.ChainStateChanged, self) self.room.logic:trigger(fk.ChainStateChanged, self)
end end
function ServerPlayer:reset()
self.room:sendLog{
type = "#ResetGeneral",
from = self.id,
}
self:setChainState(false)
if not self.faceup then self:turnOver() end
end
---@param from ServerPlayer ---@param from ServerPlayer
---@param tos ServerPlayer[] ---@param tos ServerPlayer[]
---@param skillName string ---@param skillName string

View File

@ -182,6 +182,7 @@ local ironChain = fk.CreateTrickCard{
name = "iron_chain", name = "iron_chain",
skill = ironChainCardSkill, skill = ironChainCardSkill,
special_skills = { "recast" }, special_skills = { "recast" },
multiple_targets = true,
} }
extension:addCards{ extension:addCards{
ironChain:clone(Card.Spade, 11), ironChain:clone(Card.Spade, 11),

View File

@ -32,6 +32,8 @@ Fk:loadTranslationTable{
["equip_horse"] = "坐骑牌", ["equip_horse"] = "坐骑牌",
["treasure"] = "宝物牌", ["treasure"] = "宝物牌",
["delayed_trick"] = "延时类锦囊牌", ["delayed_trick"] = "延时类锦囊牌",
["damage_card-"] = "伤害类",
["multiple_targets-"] = "多目标",
["type_weapon"] = "武器", ["type_weapon"] = "武器",
["type_armor"] = "防具", ["type_armor"] = "防具",

View File

@ -481,6 +481,7 @@ local savageAssault = fk.CreateTrickCard{
suit = Card.Spade, suit = Card.Spade,
number = 7, number = 7,
is_damage_card = true, is_damage_card = true,
multiple_targets = true,
skill = savageAssaultSkill, skill = savageAssaultSkill,
} }
@ -520,6 +521,7 @@ local archeryAttack = fk.CreateTrickCard{
suit = Card.Heart, suit = Card.Heart,
number = 1, number = 1,
is_damage_card = true, is_damage_card = true,
multiple_targets = true,
skill = archeryAttackSkill, skill = archeryAttackSkill,
} }
@ -554,6 +556,7 @@ local godSalvation = fk.CreateTrickCard{
name = "god_salvation", name = "god_salvation",
suit = Card.Heart, suit = Card.Heart,
number = 1, number = 1,
multiple_targets = true,
skill = godSalvationSkill, skill = godSalvationSkill,
} }
@ -636,6 +639,7 @@ local amazingGrace = fk.CreateTrickCard{
name = "amazing_grace", name = "amazing_grace",
suit = Card.Heart, suit = Card.Heart,
number = 3, number = 3,
multiple_targets = true,
skill = amazingGraceSkill, skill = amazingGraceSkill,
} }

View File

@ -45,6 +45,8 @@ local cheat = fk.CreateActiveSkill{
end end
from:addToPile(self.name, toGain, true, self.name) from:addToPile(self.name, toGain, true, self.name)
room:setCardMark(Fk:getCardById(toGain), "@@test_cheat-phase", 1)
room:setCardMark(Fk:getCardById(toGain), "@@test_cheat-inhand", 1)
room:obtainCard(effect.from, toGain, true, fk.ReasonPrey) room:obtainCard(effect.from, toGain, true, fk.ReasonPrey)
end end
} }
@ -277,6 +279,8 @@ Fk:loadTranslationTable{
-- ["cheat"] = "小开", -- ["cheat"] = "小开",
[":cheat"] = "出牌阶段,你可以获得一张想要的牌。", [":cheat"] = "出牌阶段,你可以获得一张想要的牌。",
["#cheat"] = "cheat你可以获得一张想要的牌", ["#cheat"] = "cheat你可以获得一张想要的牌",
["@@test_cheat-phase"] = "苦肉",
["@@test_cheat-inhand"] = "连营",
--["#test_trig-ask"] = "你可弃置一张手牌", --["#test_trig-ask"] = "你可弃置一张手牌",
["control"] = "控制", ["control"] = "控制",
[":control"] = "出牌阶段,你可以控制/解除控制若干名其他角色。", [":control"] = "出牌阶段,你可以控制/解除控制若干名其他角色。",

View File

@ -21,6 +21,8 @@ TestExppattern = {
lu.assertFalse(basic:matchExp("nullification")) lu.assertFalse(basic:matchExp("nullification"))
lu.assertTrue(basic:matchExp("slash,vine")) lu.assertTrue(basic:matchExp("slash,vine"))
lu.assertTrue(Exppattern:Parse(".|.|.|.|.|armor"):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, end,
testMatchNeg = function() testMatchNeg = function()
@ -28,7 +30,9 @@ TestExppattern = {
local not_nul = Exppattern:Parse("^nullification") local not_nul = Exppattern:Parse("^nullification")
local not_slash_jink = Exppattern:Parse("^(slash,jink)") local not_slash_jink = Exppattern:Parse("^(slash,jink)")
local not_basic = Exppattern:Parse(".|.|.|.|.|^basic") local not_basic = Exppattern:Parse(".|.|.|.|.|^basic")
local not_black = Exppattern:Parse(".|.|^(spade,club)")
local slash_jink = Exppattern:Parse("slash,jink") local slash_jink = Exppattern:Parse("slash,jink")
local no_slash_jink = Exppattern:Parse("^(slash,jink)|.|.|.|.|basic")
local slash = Fk:cloneCard("slash") local slash = Fk:cloneCard("slash")
lu.assertFalse(not_nul:matchExp("nullification")) lu.assertFalse(not_nul:matchExp("nullification"))
@ -38,7 +42,11 @@ TestExppattern = {
lu.assertFalse(not_slash_jink:match(slash)) lu.assertFalse(not_slash_jink:match(slash))
lu.assertFalse(not_basic:match(slash)) lu.assertFalse(not_basic:match(slash))
lu.assertTrue(not_nul:matchExp("peach")) lu.assertTrue(not_nul:matchExp("peach"))
lu.assertFalse(not_slash_jink:matchExp(not_basic)) 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(slash_jink:matchExp(not_slash_jink))
lu.assertFalse(not_black:matchExp("slash|A~Q|spade"))
lu.assertTrue(not_black:matchExp("vine|10|^club"))
end, end,
} }