isInLimit等各种功能 + bugfix (#201)

没来得及转远程url,这是最后一次从fork来的更新了

- 修复了fk.currentresponsepattern = nil的时机
-
添加unlimited/withinTimesLimit和withinDistanceLimit,整合并实现真正的“无次数限制”/“无距离限制”
- 给观星框上了标题(暂时不往上加标题变量)
- 青龙刀会临时追加“对此人不限次数”+“对此人不限距离”的标记,响应(包括取消)后清除
- 添加了“对某人无次数/距离限制”的标记及对应两个global技能
This commit is contained in:
YoumuKon 2023-06-19 00:20:50 +08:00 committed by GitHub
parent 0c9701b74a
commit d5330d5bed
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
15 changed files with 89 additions and 19 deletions

View File

@ -746,6 +746,7 @@ callbacks["AskForGuanxing"] = (jsonData) => {
const max_bottom_cards = data.max_bottom_cards; const max_bottom_cards = data.max_bottom_cards;
const top_area_name = data.top_area_name; const top_area_name = data.top_area_name;
const bottom_area_name = data.bottom_area_name; const bottom_area_name = data.bottom_area_name;
const prompt = data.prompt;
roomScene.state = "replying"; roomScene.state = "replying";
roomScene.popupBox.sourceComponent = Qt.createComponent("../RoomElement/GuanxingBox.qml"); roomScene.popupBox.sourceComponent = Qt.createComponent("../RoomElement/GuanxingBox.qml");
data.cards.forEach(id => { data.cards.forEach(id => {
@ -753,6 +754,7 @@ callbacks["AskForGuanxing"] = (jsonData) => {
cards.push(JSON.parse(d)); cards.push(JSON.parse(d));
}); });
const box = roomScene.popupBox.item; const box = roomScene.popupBox.item;
box.prompt = prompt;
if (max_top_cards === 0) { if (max_top_cards === 0) {
box.areaCapacities = [max_bottom_cards]; box.areaCapacities = [max_bottom_cards];
box.areaLimits = [min_bottom_cards]; box.areaLimits = [min_bottom_cards];

View File

@ -6,6 +6,7 @@ import Fk.Pages
GraphicsBox { GraphicsBox {
id: root id: root
property string prompt
property var cards: [] property var cards: []
property var result: [] property var result: []
property var areaCapacities: [] property var areaCapacities: []
@ -13,7 +14,7 @@ GraphicsBox {
property var areaNames: [] property var areaNames: []
property int padding: 25 property int padding: 25
title.text: Backend.translate("Please arrange cards") title.text: Backend.translate(prompt !== "" ? prompt : "Please arrange cards")
width: body.width + padding * 2 width: body.width + padding * 2
height: title.height + body.height + padding * 2 height: title.height + body.height + padding * 2

View File

@ -3,6 +3,14 @@
---@class TargetModSkill : StatusSkill ---@class TargetModSkill : StatusSkill
local TargetModSkill = StatusSkill:subclass("TargetModSkill") local TargetModSkill = StatusSkill:subclass("TargetModSkill")
---@param player Player
---@param card_skill ActiveSkill
---@param scope integer
---@param card Card
function TargetModSkill:isUnlimited(player, card_skill, scope, card, to)
return false
end
---@param player Player ---@param player Player
---@param card_skill ActiveSkill ---@param card_skill ActiveSkill
---@param scope integer ---@param scope integer

View File

@ -24,4 +24,21 @@ function UsableSkill:getMaxUseTime(player, scope, card, to)
return ret return ret
end end
function UsableSkill:withinTimesLimit(player, scope, card, to)
scope = scope or Player.HistoryTurn
local status_skills = Fk:currentRoom().status_skills[TargetModSkill] or Util.DummyTable
for _, skill in ipairs(status_skills) do
if skill:isUnlimited(player, self, scope, card, to) then return true end
end
return player:usedCardTimes(card.trueName, scope) < self:getMaxUseTime(player, scope, card, to)
end
function UsableSkill:withinDistanceLimit(player, isattack, card, to)
local status_skills = Fk:currentRoom().status_skills[AttackRangeSkill] or Util.DummyTable
for _, skill in ipairs(status_skills) do
if skill:withinAttackRange(player, to) then return true end
end
return isattack and player:inMyAttackRange(to, self:getDistanceLimit(player, card, to)) or player:distanceTo(to) <= self:getDistanceLimit(player, card, to)
end
return UsableSkill return UsableSkill

View File

@ -350,6 +350,7 @@ function fk.CreateMaxCardsSkill(spec)
end end
---@class TargetModSpec: StatusSkillSpec ---@class TargetModSpec: StatusSkillSpec
---@field public unlimited fun(self: TargetModSkill, player: Player, skill: ActiveSkill, scope: integer, card: Card, to: Player)
---@field public residue_func fun(self: TargetModSkill, player: Player, skill: ActiveSkill, scope: integer, card: Card, to: Player) ---@field public residue_func fun(self: TargetModSkill, player: Player, skill: ActiveSkill, scope: integer, card: Card, to: Player)
---@field public distance_limit_func fun(self: TargetModSkill, player: Player, skill: ActiveSkill, card: Card, to: Player) ---@field public distance_limit_func fun(self: TargetModSkill, player: Player, skill: ActiveSkill, card: Card, to: Player)
---@field public extra_target_func fun(self: TargetModSkill, player: Player, skill: ActiveSkill, card: Card) ---@field public extra_target_func fun(self: TargetModSkill, player: Player, skill: ActiveSkill, card: Card)
@ -361,6 +362,9 @@ function fk.CreateTargetModSkill(spec)
local skill = TargetModSkill:new(spec.name) local skill = TargetModSkill:new(spec.name)
readStatusSpecToSkill(skill, spec) readStatusSpecToSkill(skill, spec)
if spec.unlimited then
skill.isUnlimited = spec.unlimited
end
if spec.residue_func then if spec.residue_func then
skill.getResidueNum = spec.residue_func skill.getResidueNum = spec.residue_func
end end

View File

@ -258,7 +258,7 @@ GameEvent.functions[GameEvent.Phase] = function(self)
tos = { {player.id} }, tos = { {player.id} },
} }
room:doCardEffect(effect_data) room:doCardEffect(effect_data)
if effect_data.isCancellOut and card.skill then if effect_data.isCanCellout and card.skill then
card.skill:onNullified(room, effect_data) card.skill:onNullified(room, effect_data)
end end
end end

View File

@ -311,10 +311,10 @@ GameEvent.functions[GameEvent.CardEffect] = function(self)
local self = self.room local self = self.room
for _, event in ipairs({ fk.PreCardEffect, fk.BeforeCardEffect, fk.CardEffecting, fk.CardEffectFinished }) do for _, event in ipairs({ fk.PreCardEffect, fk.BeforeCardEffect, fk.CardEffecting, fk.CardEffectFinished }) do
if cardEffectEvent.isCancellOut then if cardEffectEvent.isCanCellout then
local user = cardEffectEvent.from and self:getPlayerById(cardEffectEvent.from) or nil local user = cardEffectEvent.from and self:getPlayerById(cardEffectEvent.from) or nil
if self.logic:trigger(fk.CardEffectCancelledOut, user, cardEffectEvent) then if self.logic:trigger(fk.CardEffectCancelledOut, user, cardEffectEvent) then
cardEffectEvent.isCancellOut = false cardEffectEvent.isCanCellout = false
else else
self.logic:breakEvent() self.logic:breakEvent()
end end

View File

@ -19,6 +19,10 @@ MarkEnum.MinusMaxCards = "MinusMaxCards"
---@field AddMaxCards string @ 于本回合内减少标记值数量的手牌上限 ---@field AddMaxCards string @ 于本回合内减少标记值数量的手牌上限
MarkEnum.MinusMaxCardsInTurn = "MinusMaxCards-turn" MarkEnum.MinusMaxCardsInTurn = "MinusMaxCards-turn"
---@field BypassTimesLimit string @ 对其使用牌无次数限制,可带清除标记后缀
MarkEnum.BypassTimesLimit = "bypasstimeslimit"
---@field BypassDistanceLimit string @ 对其使用牌无距离限制,可带清除标记后缀
MarkEnum.BypassDistanceLimit = "bypassdistancelimit"
---@field UncompulsoryInvalidity string @ 非锁定技失效,可带清除标记后缀 ---@field UncompulsoryInvalidity string @ 非锁定技失效,可带清除标记后缀
MarkEnum.UncompulsoryInvalidity = "uncompulsoryInvalidity" MarkEnum.UncompulsoryInvalidity = "uncompulsoryInvalidity"

View File

@ -642,6 +642,7 @@ function Room:doRequest(player, command, jsonData, wait)
if wait then if wait then
local ret = player:waitForReply(self.timeout) local ret = player:waitForReply(self.timeout)
player.serverplayer:setBusy(false) player.serverplayer:setBusy(false)
player.serverplayer:setThinking(false)
return ret return ret
end end
end end
@ -667,6 +668,7 @@ function Room:doBroadcastRequest(command, players, jsonData)
for _, p in ipairs(players) do for _, p in ipairs(players) do
p.serverplayer:setBusy(false) p.serverplayer:setBusy(false)
p.serverplayer:setThinking(false)
end end
end end
@ -731,6 +733,7 @@ function Room:doRaceRequest(command, players, jsonData)
for _, p in ipairs(self.players) do for _, p in ipairs(self.players) do
p.serverplayer:setBusy(false) p.serverplayer:setBusy(false)
p.serverplayer:setThinking(false)
end end
return ret return ret
@ -1365,6 +1368,7 @@ end
---@param top_limit integer[] @ 置于牌堆顶的牌的限制(下限,上限),不填写则不限 ---@param top_limit integer[] @ 置于牌堆顶的牌的限制(下限,上限),不填写则不限
---@param bottom_limit integer[] @ 置于牌堆底的牌的限制(下限,上限),不填写则不限 ---@param bottom_limit integer[] @ 置于牌堆底的牌的限制(下限,上限),不填写则不限
---@param customNotify string|null @ 自定义读条操作提示 ---@param customNotify string|null @ 自定义读条操作提示
---@param prompt string|null @ 观星框的标题(暂时雪藏)
---@param noPut boolean|null @ 是否进行放置牌操作 ---@param noPut boolean|null @ 是否进行放置牌操作
---@param areaNames string[]|null @ 左侧提示信息 ---@param areaNames string[]|null @ 左侧提示信息
---@return table<top|bottom, cardId[]> ---@return table<top|bottom, cardId[]>
@ -1389,6 +1393,7 @@ function Room:askForGuanxing(player, cards, top_limit, bottom_limit, customNotif
local command = "AskForGuanxing" local command = "AskForGuanxing"
self:notifyMoveFocus(player, customNotify or command) self:notifyMoveFocus(player, customNotify or command)
local data = { local data = {
prompt = prompt or "",
cards = cards, cards = cards,
min_top_cards = top_limit and top_limit[1] or 0, min_top_cards = top_limit and top_limit[1] or 0,
max_top_cards = top_limit and top_limit[2] or #cards, max_top_cards = top_limit and top_limit[2] or #cards,
@ -2231,11 +2236,11 @@ function Room:handleCardEffect(event, cardEffectEvent)
self:useCard(use) self:useCard(use)
end end
if not cardEffectEvent.isCancellOut then if not cardEffectEvent.isCanCellout then
break break
end end
cardEffectEvent.isCancellOut = i == loopTimes cardEffectEvent.isCanCellout = i == loopTimes
end end
elseif elseif
cardEffectEvent.card.type == Card.TypeTrick and cardEffectEvent.card.type == Card.TypeTrick and

View File

@ -140,10 +140,12 @@ function ServerPlayer:waitForReply(timeout)
result = "" result = ""
self.reply_cancel = true self.reply_cancel = true
self.serverplayer:setBusy(false) self.serverplayer:setBusy(false)
self.serverplayer:setThinking(false)
end end
if result ~= "" then if result ~= "" then
self.reply_ready = true self.reply_ready = true
self.serverplayer:setBusy(false) self.serverplayer:setBusy(false)
self.serverplayer:setThinking(false)
end end
local queue = self.room.request_queue[self.serverplayer] local queue = self.room.request_queue[self.serverplayer]

View File

@ -133,7 +133,7 @@ fk.IceDamage = 4
---@field public cardsResponded Card[]|null ---@field public cardsResponded Card[]|null
---@field public disresponsive boolean|null ---@field public disresponsive boolean|null
---@field public unoffsetable boolean|null ---@field public unoffsetable boolean|null
---@field public isCancellOut boolean|null ---@field public isCanCellout boolean|null
---@field public fixedResponseTimes table<string, integer>|integer|null ---@field public fixedResponseTimes table<string, integer>|integer|null
---@field public fixedAddTimesResponsors integer[] ---@field public fixedAddTimesResponsors integer[]
---@field public prohibitedCardNames string[]|null ---@field public prohibitedCardNames string[]|null

View File

@ -80,7 +80,7 @@ local analepticSkill = fk.CreateActiveSkill{
name = "analeptic_skill", name = "analeptic_skill",
max_turn_use_time = 1, max_turn_use_time = 1,
can_use = function(self, player, card) can_use = function(self, player, card)
return player:usedCardTimes("analeptic", Player.HistoryTurn) < self:getMaxUseTime(Self, Player.HistoryTurn, card, Self) return self:withinTimesLimit(player, Player.HistoryTurn, card, player)
end, end,
on_use = function(self, room, use) on_use = function(self, room, use)
if not use.tos or #TargetGroup:getRealTargets(use.tos) == 0 then if not use.tos or #TargetGroup:getRealTargets(use.tos) == 0 then
@ -240,7 +240,7 @@ local supplyShortageSkill = fk.CreateActiveSkill{
local player = Fk:currentRoom():getPlayerById(to_select) local player = Fk:currentRoom():getPlayerById(to_select)
if Self ~= player then if Self ~= player then
return not player:hasDelayedTrick("supply_shortage") and return not player:hasDelayedTrick("supply_shortage") and
Self:distanceTo(player) <= self:getDistanceLimit(Self, card, player) self:withinDistanceLimit(Self, false, card, player)
end end
end end
return false return false

View File

@ -122,6 +122,27 @@ local uncompulsoryInvalidity = fk.CreateInvaliditySkill {
end end
} }
local noTimesLimit = fk.CreateTargetModSkill{
name = "noTimesLimit",
global = true,
unlimited = function(self, player, skill, scope, card, to)
return to:getMark(MarkEnum.BypassTimesLimit) ~= 0 or
table.find(MarkEnum.TempMarkSuffix, function(s)
return to:getMark(MarkEnum.BypassTimesLimit .. s) ~= 0
end)
end
}
local noDistanceLimit = fk.CreateAttackRangeSkill{
name = "noDistanceLimit",
global = true,
within_func = function(self, player, to)
return to:getMark(MarkEnum.BypassDistanceLimit) ~= 0 or
table.find(MarkEnum.TempMarkSuffix, function(s)
return to:getMark(MarkEnum.BypassDistanceLimit .. s) ~= 0
end)
end
}
AuxSkills = { AuxSkills = {
discardSkill, discardSkill,
chooseCardsSkill, chooseCardsSkill,
@ -129,4 +150,6 @@ AuxSkills = {
maxCardsSkill, maxCardsSkill,
choosePlayersToMoveCardInBoardSkill, choosePlayersToMoveCardInBoardSkill,
uncompulsoryInvalidity, uncompulsoryInvalidity,
noTimesLimit,
noDistanceLimit,
} }

View File

@ -509,10 +509,10 @@ local paoxiaoAudio = fk.CreateTriggerSkill{
} }
local paoxiao = fk.CreateTargetModSkill{ local paoxiao = fk.CreateTargetModSkill{
name = "paoxiao", name = "paoxiao",
residue_func = function(self, player, skill, scope) unlimited = function(self, player, skill, scope)
if player:hasSkill(self.name) and skill.trueName == "slash_skill" if player:hasSkill(self.name) and skill.trueName == "slash_skill"
and scope == Player.HistoryPhase then and scope == Player.HistoryPhase then
return 999 return true
end end
end, end,
} }

View File

@ -50,14 +50,14 @@ local slashSkill = fk.CreateActiveSkill{
can_use = function(self, player, card) can_use = function(self, player, card)
return return
table.find(Fk:currentRoom().alive_players, function(p) table.find(Fk:currentRoom().alive_players, function(p)
return player:usedCardTimes("slash", Player.HistoryPhase) < self:getMaxUseTime(Self, Player.HistoryPhase, card, p) return self:withinTimesLimit(player, Player.HistoryPhase, card, p)
end) end)
end, end,
target_filter = function(self, to_select, selected, _, card) target_filter = function(self, to_select, selected, _, card)
if #selected < self:getMaxTargetNum(Self, card) then if #selected < self:getMaxTargetNum(Self, card) then
local player = Fk:currentRoom():getPlayerById(to_select) local player = Fk:currentRoom():getPlayerById(to_select)
return Self ~= player and Self:inMyAttackRange(player, self:getDistanceLimit(Self, card, player)) and return Self ~= player and self:withinDistanceLimit(Self, true, card, player) and
(#selected > 0 or Self:usedCardTimes("slash", Player.HistoryPhase) < self:getMaxUseTime(Self, Player.HistoryPhase, card, player)) (#selected > 0 or self:withinTimesLimit(Self, Player.HistoryPhase, card, player))
end end
end, end,
on_effect = function(self, room, effect) on_effect = function(self, room, effect)
@ -125,7 +125,7 @@ local jinkSkill = fk.CreateActiveSkill{
end, end,
on_effect = function(self, room, effect) on_effect = function(self, room, effect)
if effect.responseToEvent then if effect.responseToEvent then
effect.responseToEvent.isCancellOut = true effect.responseToEvent.isCanCellout = true
end end
end end
} }
@ -245,7 +245,7 @@ local snatchSkill = fk.CreateActiveSkill{
local player = Fk:currentRoom():getPlayerById(to_select) local player = Fk:currentRoom():getPlayerById(to_select)
return return
Self ~= player and Self ~= player and
Self:distanceTo(player) <= self:getDistanceLimit(Self, card, player) and -- for no distance limit for snatch self:withinDistanceLimit(Self, false, card, player) and -- for no distance limit for snatch
not player:isAllNude() not player:isAllNude()
end end
end, end,
@ -436,7 +436,7 @@ local nullificationSkill = fk.CreateActiveSkill{
end, end,
on_effect = function(self, room, effect) on_effect = function(self, room, effect)
if effect.responseToEvent then if effect.responseToEvent then
effect.responseToEvent.isCancellOut = true effect.responseToEvent.isCanCellout = true
end end
end end
} }
@ -778,10 +778,10 @@ local crossbowAudio = fk.CreateTriggerSkill{
local crossbowSkill = fk.CreateTargetModSkill{ local crossbowSkill = fk.CreateTargetModSkill{
name = "#crossbow_skill", name = "#crossbow_skill",
attached_equip = "crossbow", attached_equip = "crossbow",
residue_func = function(self, player, skill, scope) unlimited = function(self, player, skill, scope)
if player:hasSkill(self.name) and skill.trueName == "slash_skill" if player:hasSkill(self.name) and skill.trueName == "slash_skill"
and scope == Player.HistoryPhase then and scope == Player.HistoryPhase then
return 999 return true
end end
end, end,
} }
@ -941,8 +941,12 @@ local bladeSkill = fk.CreateTriggerSkill{
end, end,
on_cost = function(self, event, target, player, data) on_cost = function(self, event, target, player, data)
local room = player.room local room = player.room
room:setPlayerMark(target, MarkEnum.BypassDistanceLimit, 1)
room:setPlayerMark(target, MarkEnum.BypassTimesLimit, 1)
local use = room:askForUseCard(player, "slash", nil, "#blade_slash:" .. target.id, local use = room:askForUseCard(player, "slash", nil, "#blade_slash:" .. target.id,
true, { must_targets = {target.id}, exclusive_targets = {target.id} }) true, { must_targets = {target.id}, exclusive_targets = {target.id} })
room:setPlayerMark(target, MarkEnum.BypassDistanceLimit, 0)
room:setPlayerMark(target, MarkEnum.BypassTimesLimit, 0)
if use then if use then
use.extraUse = true use.extraUse = true
self.cost_data = use self.cost_data = use