modify process & quest skill (#166)

This commit is contained in:
Ho-spair 2023-05-28 18:45:54 +08:00 committed by GitHub
parent 910bf6a215
commit 5128100083
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
25 changed files with 485 additions and 184 deletions

View File

@ -30,7 +30,7 @@ Item {
Text {
id: x
opacity: skilltype === "limit" ? 1 : 0
opacity: (skilltype === "limit" || skilltype === "quest") ? 1 : 0
text: "X"
font.family: fontLibian.name
font.pixelSize: 28
@ -62,6 +62,12 @@ Item {
} else if (skilltype === 'switch') {
visible = true;
bg.source = SkinBank.LIMIT_SKILL_DIR + (usedtimes < 1 ? 'switch' : 'switch-yin');
} else if (skilltype === 'quest') {
visible = true
if (usedtimes > 1) {
x.visible = true;
bg.source = SkinBank.LIMIT_SKILL_DIR + "limit-used";
}
}
}
}

View File

@ -541,7 +541,7 @@ local function updateLimitSkill(pid, skill, times)
if skill:isSwitchSkill() then
times = ClientInstance:getPlayerById(pid):getSwitchSkillState(skill.switchSkillName) == fk.SwitchYang and 0 or 1
ClientInstance:notifyUI("UpdateLimitSkill", json.encode{ pid, skill.switchSkillName, times })
elseif skill.frequency == Skill.Limited or skill.frequency == Skill.Wake then
elseif skill.frequency == Skill.Limited or skill.frequency == Skill.Wake or skill.frequency == Skill.Quest then
ClientInstance:notifyUI("UpdateLimitSkill", json.encode{ pid, skill.name, times })
end
end
@ -557,6 +557,10 @@ fk.client_callback["AddSkill"] = function(jsonData)
ClientInstance:notifyUI("AddSkill", jsonData)
end
if skill.frequency == Skill.Quest then
return
end
updateLimitSkill(id, skill, target:usedSkillTimes(skill_name, Player.HistoryGame))
end
@ -663,7 +667,9 @@ fk.client_callback["AddSkillUseHistory"] = function(jsonData)
local playerid, skill_name, time = data[1], data[2], data[3]
local player = ClientInstance:getPlayerById(playerid)
player:addSkillUseHistory(skill_name, time)
if not Fk.skills[skill_name] then return end
local skill = Fk.skills[skill_name]
if not skill or skill.frequency == Skill.Quest then return end
updateLimitSkill(playerid, Fk.skills[skill_name], player:usedSkillTimes(skill_name, Player.HistoryGame))
end
@ -672,7 +678,9 @@ fk.client_callback["SetSkillUseHistory"] = function(jsonData)
local id, skill_name, time, scope = data[1], data[2], data[3], data[4]
local player = ClientInstance:getPlayerById(id)
player:setSkillUseHistory(skill_name, time, scope)
if not Fk.skills[skill_name] then return end
local skill = Fk.skills[skill_name]
if not skill or skill.frequency == Skill.Quest then return end
updateLimitSkill(id, Fk.skills[skill_name], player:usedSkillTimes(skill_name, Player.HistoryGame))
end
@ -702,6 +710,12 @@ fk.client_callback["ChangeSelf"] = function(jsonData)
ClientInstance:notifyUI("ChangeSelf", data.id)
end
fk.client_callback["UpdateQuestSkillUI"] = function(jsonData)
local data = json.decode(jsonData)
local player, skillName, usedTimes = data[1], data[2], data[3]
updateLimitSkill(player, Fk.skills[skillName], usedTimes)
end
-- Create ClientInstance (used by Lua)
ClientInstance = Client:new()
dofile "lua/client/client_util.lua"

View File

@ -286,6 +286,8 @@ function GetSkillData(skill_name)
frequency = "limit"
elseif skill.frequency == Skill.Wake then
frequency = "wake"
elseif skill.frequency == Skill.Quest then
frequency = "quest"
end
return json.encode{
skill = Fk:translate(skill_name),

View File

@ -154,6 +154,7 @@ FreeKill使用的是libgit2的C API与此同时使用Git完成拓展包的下
["AskForChoice"] = "选择",
["AskForKingdom"] = "选择势力",
["AskForPindian"] = "拼点",
["AskForMoveCardInBoard"] = "移动卡牌",
["PlayCard"] = "出牌",
["AskForCardChosen"] = "选牌",

View File

@ -372,7 +372,7 @@ function Player:getAttackRange()
local status_skills = Fk:currentRoom().status_skills[AttackRangeSkill] or {}
for _, skill in ipairs(status_skills) do
local correct = skill:getCorrect(self, other)
local correct = skill:getCorrect(self)
baseAttackRange = baseAttackRange + correct
end
@ -429,6 +429,14 @@ function Player:inMyAttackRange(other)
if self == other then
return false
end
local status_skills = Fk:currentRoom().status_skills[AttackRangeSkill] or {}
for _, skill in ipairs(status_skills) do
if skill:withinAttackRange(self, other) then
return true
end
end
local baseAttackRange = self:getAttackRange()
return self:distanceTo(other) <= baseAttackRange
end
@ -743,6 +751,10 @@ function Player:getSwitchSkillState(skillName, afterUse)
end
function Player:canMoveCardInBoardTo(to, id)
if self == to then
return false
end
local card = Fk:getCardById(id)
assert(card.type == Card.TypeEquip or card.sub_type == Card.SubtypeDelayedTrick)
@ -755,4 +767,34 @@ function Player:canMoveCardInBoardTo(to, id)
end
end
function Player:canMoveCardsInBoardTo(to, flag)
if self == to then
return false
end
assert(flag == nil or flag == "e" or flag == "j")
local areas = {}
if flag == "e" then
table.insert(areas, Player.Equip)
elseif flag == "j" then
table.insert(areas, Player.Judge)
else
areas = { Player.Equip, Player.Judge }
end
for _, cardId in ipairs(self:getCardIds(areas)) do
if self:canMoveCardInBoardTo(to, cardId) then
return true
end
end
return false
end
function Player:getQuestSkillState(skillName)
local questSkillState = self:getMark(MarkEnum.QuestSkillPreName .. skillName)
return type(questSkillState) == "string" and questSkillState or nil
end
return Player

View File

@ -22,6 +22,7 @@ Skill.NotFrequent = 2
Skill.Compulsory = 3
Skill.Limited = 4
Skill.Wake = 5
Skill.Quest = 6
--- 构造函数,不可随意调用。
---@param name string @ 技能名

View File

@ -120,11 +120,11 @@ function ActiveSkill:getMaxCardNum()
end
end
function ActiveSkill:getDistanceLimit(player, card)
function ActiveSkill:getDistanceLimit(player, card, to)
local ret = self.distance_limit or 0
local status_skills = Fk:currentRoom().status_skills[TargetModSkill] or {}
for _, skill in ipairs(status_skills) do
local correct = skill:getDistanceLimit(player, self, card)
local correct = skill:getDistanceLimit(player, self, card, to)
if correct == nil then correct = 0 end
ret = ret + correct
end

View File

@ -6,8 +6,12 @@ local AttackRangeSkill = StatusSkill:subclass("AttackRangeSkill")
---@param from Player
---@param to Player
---@return integer
function AttackRangeSkill:getCorrect(from, to)
function AttackRangeSkill:getCorrect(from)
return 0
end
function AttackRangeSkill:withinAttackRange(from, to)
return false
end
return AttackRangeSkill

View File

@ -7,14 +7,14 @@ local TargetModSkill = StatusSkill:subclass("TargetModSkill")
---@param card_skill ActiveSkill
---@param scope integer
---@param card Card
function TargetModSkill:getResidueNum(player, card_skill, scope, card)
function TargetModSkill:getResidueNum(player, card_skill, scope, card, to)
return 0
end
---@param player Player
---@param card_skill ActiveSkill
---@param card Card
function TargetModSkill:getDistanceLimit(player, card_skill, card)
function TargetModSkill:getDistanceLimit(player, card_skill, card, to)
return 0
end

View File

@ -12,12 +12,12 @@ function UsableSkill:initialize(name, frequency)
self.max_use_time = {9999, 9999, 9999, 9999}
end
function UsableSkill:getMaxUseTime(player, scope, card)
function UsableSkill:getMaxUseTime(player, scope, card, to)
scope = scope or Player.HistoryTurn
local ret = self.max_use_time[scope]
local status_skills = Fk:currentRoom().status_skills[TargetModSkill] or {}
for _, skill in ipairs(status_skills) do
local correct = skill:getResidueNum(player, self, scope, card)
local correct = skill:getResidueNum(player, self, scope, card, to)
if correct == nil then correct = 0 end
ret = ret + correct
end

View File

@ -300,17 +300,23 @@ function fk.CreateProhibitSkill(spec)
end
---@class AttackRangeSpec: StatusSkillSpec
---@field public correct_func fun(self: AttackRangeSkill, from: Player, to: Player)
---@field public correct_func fun(self: AttackRangeSkill, from: Player)
---@field public within_func fun(self: AttackRangeSkill, from: Player, to: Player)
---@param spec AttackRangeSpec
---@return AttackRangeSkill
function fk.CreateAttackRangeSkill(spec)
assert(type(spec.name) == "string")
assert(type(spec.correct_func) == "function")
assert(type(spec.correct_func) == "function" or type(spec.within_func) == "function")
local skill = AttackRangeSkill:new(spec.name)
readStatusSpecToSkill(skill, spec)
if spec.correct_func then
skill.getCorrect = spec.correct_func
end
if spec.within_func then
skill.withinAttackRange = spec.within_func
end
return skill
end
@ -338,8 +344,8 @@ function fk.CreateMaxCardsSkill(spec)
end
---@class TargetModSpec: StatusSkillSpec
---@field public residue_func fun(self: TargetModSkill, player: Player, skill: ActiveSkill, scope: integer, card: Card)
---@field public distance_limit_func fun(self: TargetModSkill, player: Player, skill: ActiveSkill, card: Card)
---@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 extra_target_func fun(self: TargetModSkill, player: Player, skill: ActiveSkill, card: Card)
---@param spec TargetModSpec

View File

@ -43,7 +43,9 @@ fk.FinishJudge = 27
fk.RoundStart = 28
fk.RoundEnd = 29
fk.BeforeTurnOver = 79
fk.TurnedOver = 30
fk.BeforeChainStateChange = 80
fk.ChainStateChanged = 31
fk.PreDamage = 32
@ -106,4 +108,7 @@ fk.CardShown = 77
-- 78 = GamePrepared
fk.NumOfEvents = 79
-- 79 = BeforeTurnOver
-- 80 = BeforeChainStateChange
fk.NumOfEvents = 81

View File

@ -101,6 +101,14 @@ end
GameEvent.functions[GameEvent.Damage] = function(self)
local damageStruct = table.unpack(self.data)
local self = self.room
if damageStruct.card and damageStruct.skillName == damageStruct.card.name .. "_skill" and not damageStruct.chain then
local cardEffectData = self.logic:getCurrentEvent():findParent(GameEvent.CardEffect)
if cardEffectData then
local cardEffectEvent = cardEffectData.data[1]
damageStruct.damage = damageStruct.damage + (cardEffectEvent.additionalDamage or 0)
end
end
if damageStruct.damage < 1 then
return false
end
@ -131,6 +139,15 @@ GameEvent.functions[GameEvent.Damage] = function(self)
return false
end
if damageStruct.card and damageStruct.damage > 0 then
local parentUseData = self.logic:getCurrentEvent():findParent(GameEvent.UseCard)
if parentUseData then
local cardUseEvent = parentUseData.data[1]
cardUseEvent.damageDealt = cardUseEvent.damageDealt or {}
cardUseEvent.damageDealt[damageStruct.to.id] = (cardUseEvent.damageDealt[damageStruct.to.id] or 0) + damageStruct.damage
end
end
-- 先扣减护甲,再扣体力值
local shield_to_lose = math.min(damageStruct.damage, damageStruct.to.shield)
self:changeShield(damageStruct.to, -shield_to_lose)
@ -191,6 +208,14 @@ end
GameEvent.functions[GameEvent.Recover] = function(self)
local recoverStruct = table.unpack(self.data)
local self = self.room
if recoverStruct.card then
local cardEffectData = self.logic:getCurrentEvent():findParent(GameEvent.CardEffect)
if cardEffectData then
local cardEffectEvent = cardEffectData.data[1]
recoverStruct.num = recoverStruct.num + (cardEffectEvent.additionalRecover or 0)
end
end
if recoverStruct.num < 1 then
return false
end

View File

@ -18,6 +18,7 @@ dofile "lua/server/events/movecard.lua"
GameEvent.UseCard = 9
GameEvent.RespondCard = 10
GameEvent.CardEffect = 20
dofile "lua/server/events/usecard.lua"
GameEvent.SkillEffect = 11
@ -51,6 +52,7 @@ local eventTranslations = {
[GameEvent.MoveCards] = "GameEvent.MoveCards",
[GameEvent.UseCard] = "GameEvent.UseCard",
[GameEvent.RespondCard] = "GameEvent.RespondCard",
[GameEvent.CardEffect] = "GameEvent.CardEffect",
[GameEvent.SkillEffect] = "GameEvent.SkillEffect",
[GameEvent.Judge] = "GameEvent.Judge",
[GameEvent.DrawInitial] = "GameEvent.DrawInitial",

View File

@ -275,3 +275,39 @@ GameEvent.cleaners[GameEvent.RespondCard] = function(self)
})
end
end
GameEvent.functions[GameEvent.CardEffect] = function(self)
local cardEffectEvent = table.unpack(self.data)
local self = self.room
for _, event in ipairs({ fk.PreCardEffect, fk.BeforeCardEffect, fk.CardEffecting, fk.CardEffectFinished }) do
if cardEffectEvent.isCancellOut then
local user = cardEffectEvent.from and self:getPlayerById(cardEffectEvent.from) or nil
if self.logic:trigger(fk.CardEffectCancelledOut, user, cardEffectEvent) then
cardEffectEvent.isCancellOut = false
else
self.logic:breakEvent()
end
end
if
not cardEffectEvent.toCard and
(
not (self:getPlayerById(cardEffectEvent.to):isAlive() and cardEffectEvent.to)
or #self:deadPlayerFilter(TargetGroup:getRealTargets(cardEffectEvent.tos)) == 0
)
then
self.logic:breakEvent()
end
if table.contains((cardEffectEvent.nullifiedTargets or {}), cardEffectEvent.to) then
self.logic:breakEvent()
end
if cardEffectEvent.from and self.logic:trigger(event, self:getPlayerById(cardEffectEvent.from), cardEffectEvent) then
self.logic:breakEvent()
end
self:handleCardEffect(event, cardEffectEvent)
end
end

View File

@ -5,4 +5,16 @@ MarkEnum = {}
---@field StraightToWake string @ 跳过觉醒标记(值为技能名通过+连接)
MarkEnum.StraightToWake = "_straight_to_wake"
---@field SwithSkillPreName string @ 转换技状态标记前缀(整体为前缀+转换技技能)
MarkEnum.SwithSkillPreName = "__switcher_"
---@field SwithSkillPreName string @ 转换技状态标记前缀(整体为前缀+转换技技能)
MarkEnum.QuestSkillPreName = "__questPre_"
---@field AddMaxCards string @ 增加标记值数量的手牌上限
MarkEnum.AddMaxCards = "AddMaxCards"
---@field AddMaxCardsInTurn string @ 于本回合内增加标记值数量的手牌上限
MarkEnum.AddMaxCardsInTurn = "AddMaxCards-turn"
---@field MinusMaxCards string @ 减少标记值数量的手牌上限
MarkEnum.MinusMaxCards = "MinusMaxCards"
---@field AddMaxCards string @ 于本回合内减少标记值数量的手牌上限
MarkEnum.MinusMaxCardsInTurn = "MinusMaxCards-turn"

View File

@ -927,7 +927,7 @@ Room.askForUseViewAsSkill = Room.askForUseActiveSkill
---@param pattern string @ 弃牌需要符合的规则
---@param prompt string @ 提示信息
---@return integer[] @ 弃掉的牌的id列表可能是空的
function Room:askForDiscard(player, minNum, maxNum, includeEquip, skillName, cancelable, pattern, prompt)
function Room:askForDiscard(player, minNum, maxNum, includeEquip, skillName, cancelable, pattern, prompt, skipDiscard)
cancelable = cancelable or false
pattern = pattern or ""
@ -975,7 +975,10 @@ function Room:askForDiscard(player, minNum, maxNum, includeEquip, skillName, can
toDiscard = table.random(canDiscards, minNum)
end
if not skipDiscard then
self:throwCard(toDiscard, skillName, player, player)
end
return toDiscard
end
@ -1390,6 +1393,23 @@ end
---@param event_data CardEffectEvent|nil @ 事件信息
---@return CardUseStruct | nil @ 返回关于本次使用牌的数据,以便后续处理
function Room:askForUseCard(player, card_name, pattern, prompt, cancelable, extra_data, event_data)
if event_data and (event_data.disresponsive or table.contains(event_data.disresponsiveList or {}, player.id)) then
return nil
end
if event_data and event_data.prohibitedCardNames and card_name then
local splitedCardNames = card_name:split(",")
splitedCardNames = table.filter(splitedCardNames, function(name)
return not table.contains(event_data.prohibitedCardNames, name)
end)
if #splitedCardNames == 0 then
return nil
end
card_name = table.concat(splitedCardNames, ",")
end
local command = "AskForUseCard"
self:notifyMoveFocus(player, card_name)
cancelable = cancelable or false
@ -1429,8 +1449,13 @@ end
---@param prompt string @ 提示信息
---@param cancelable boolean @ 能否取消
---@param extra_data any @ 额外数据
---@param effectData CardEffectEvent @ 关联的卡牌生效流程
---@return Card | nil @ 打出的牌
function Room:askForResponse(player, card_name, pattern, prompt, cancelable, extra_data)
function Room:askForResponse(player, card_name, pattern, prompt, cancelable, extra_data, effectData)
if effectData and (effectData.disresponsive or table.contains(effectData.disresponsiveList or {}, player.id)) then
return nil
end
local command = "AskForResponseCard"
self:notifyMoveFocus(player, card_name)
cancelable = cancelable or false
@ -1576,20 +1601,32 @@ end
---@field targetOne ServerPlayer
---@field targetTwo ServerPlayer
---@field skillName string
---@field flag string|null
---@field moveFrom ServerPlayer|null
---@return cardId
function Room:askForMoveCardInBoard(player, targetOne, targetTwo, skillName)
function Room:askForMoveCardInBoard(player, targetOne, targetTwo, skillName, flag, moveFrom)
if flag then
assert(flag == "e" or flag == "j")
end
local cards = {}
local cardsPosition = {}
if not flag or flag == "e" then
if not moveFrom or moveFrom == targetOne then
for _, equipId in ipairs(targetOne:getCardIds(Player.Equip)) do
if targetOne:canMoveCardInBoardTo(targetTwo, equipId) then
table.insert(cards, equipId)
end
end
end
if not moveFrom or moveFrom == targetTwo then
for _, equipId in ipairs(targetTwo:getCardIds(Player.Equip)) do
if targetTwo:canMoveCardInBoardTo(targetOne, equipId) then
table.insert(cards, equipId)
end
end
end
if #cards > 0 then
table.sort(cards, function(prev, next)
@ -1603,19 +1640,26 @@ function Room:askForMoveCardInBoard(player, targetOne, targetTwo, skillName)
table.insert(cardsPosition, self:getCardOwner(id) == targetOne and 0 or 1)
end
end
end
if not flag or flag == "j" then
if not moveFrom or moveFrom == targetOne then
for _, trickId in ipairs(targetOne:getCardIds(Player.Judge)) do
if targetOne:canMoveCardInBoardTo(targetTwo, trickId) then
table.insert(cards, trickId)
table.insert(cardsPosition, 0)
end
end
end
if not moveFrom or moveFrom == targetTwo then
for _, trickId in ipairs(targetTwo:getCardIds(Player.Judge)) do
if targetTwo:canMoveCardInBoardTo(targetOne, trickId) then
table.insert(cards, trickId)
table.insert(cardsPosition, 1)
end
end
end
end
if #cards == 0 then
return
@ -1648,6 +1692,42 @@ function Room:askForMoveCardInBoard(player, targetOne, targetTwo, skillName)
)
end
--- 询问一名玩家从targets中选择若干名玩家出来。
---@param player ServerPlayer @ 要做选择的玩家
---@param prompt string @ 提示信息
---@param skillName string @ 技能名
---@param cancelable boolean|null @ 是否可以取消选择
---@param flag string|null @ 限定可移动的区域值为ej
---@return integer[] @ 选择的玩家id列表可能为空
function Room:askForChooseToMoveCardInBoard(player, prompt, skillName, cancelable, flag)
if flag then
assert(flag == "e" or flag == "j")
end
cancelable = (not cancelable) and false or true
local data = {
flag = flag,
skillName = skillName,
}
local _, ret = self:askForUseActiveSkill(
player,
"choose_players_to_move_card_in_board",
prompt or "",
cancelable,
data
)
if ret then
return ret.targets
else
if cancelable then
return {}
else
return self:canMoveCardInBoard(flag)
end
end
end
------------------------------------------------------------------------
-- 使用牌
------------------------------------------------------------------------
@ -1698,6 +1778,7 @@ local onAim = function(room, cardUseEvent, aimEventCollaborators)
tos = aimGroup,
firstTarget = firstTarget,
additionalDamage = cardUseEvent.additionalDamage,
additionalRecover = cardUseEvent.additionalRecover,
extra_data = cardUseEvent.extra_data,
}
@ -1884,7 +1965,9 @@ function Room:doCardUseEffect(cardUseEvent)
disresponsiveList = cardUseEvent.disresponsiveList,
unoffsetableList = cardUseEvent.unoffsetableList,
additionalDamage = cardUseEvent.additionalDamage,
additionalRecover = cardUseEvent.additionalRecover,
cardIdsResponded = cardUseEvent.nullifiedTargets,
prohibitedCardNames = cardUseEvent.prohibitedCardNames,
extra_data = cardUseEvent.extra_data,
}
@ -1905,6 +1988,7 @@ function Room:doCardUseEffect(cardUseEvent)
cardEffectEvent.subTargets = curAimEvent.subTargets
cardEffectEvent.additionalDamage = curAimEvent.additionalDamage
cardEffectEvent.additionalRecover = curAimEvent.additionalRecover
if curAimEvent.disresponsiveList then
for _, disresponsivePlayer in ipairs(curAimEvent.disresponsiveList) do
@ -1938,37 +2022,17 @@ end
--- 对卡牌效果数据进行生效
---@param cardEffectEvent CardEffectEvent
function Room:doCardEffect(cardEffectEvent)
for _, event in ipairs({ fk.PreCardEffect, fk.BeforeCardEffect, fk.CardEffecting, fk.CardEffectFinished }) do
if cardEffectEvent.isCancellOut then
local user = cardEffectEvent.from and self:getPlayerById(cardEffectEvent.from) or nil
if self.logic:trigger(fk.CardEffectCancelledOut, user, cardEffectEvent) then
cardEffectEvent.isCancellOut = false
else
break
end
end
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
if table.contains((cardEffectEvent.nullifiedTargets or {}), cardEffectEvent.to) then
break
end
if cardEffectEvent.from and self.logic:trigger(event, self:getPlayerById(cardEffectEvent.from), cardEffectEvent) then
return
return execGameEvent(GameEvent.CardEffect, cardEffectEvent)
end
---@param cardEffectEvent CardEffectEvent
function Room:handleCardEffect(event, cardEffectEvent)
if event == fk.PreCardEffect then
if cardEffectEvent.card.skill:aboutToEffect(self, cardEffectEvent) then return end
if cardEffectEvent.card.trueName == "slash" and
not (
cardEffectEvent.disresponsive or
cardEffectEvent.unoffsetable or
table.contains(cardEffectEvent.disresponsiveList or {}, cardEffectEvent.to) or
table.contains(cardEffectEvent.unoffsetableList or {}, cardEffectEvent.to)
) then
if
cardEffectEvent.card.trueName == "slash" and
not (cardEffectEvent.unoffsetable or table.contains(cardEffectEvent.unoffsetableList or {}, cardEffectEvent.to))
then
local loopTimes = 1
if cardEffectEvent.fixedResponseTimes then
if type(cardEffectEvent.fixedResponseTimes) == "table" then
@ -2006,22 +2070,36 @@ function Room:doCardEffect(cardEffectEvent)
cardEffectEvent.isCancellOut = i == loopTimes
end
elseif cardEffectEvent.card.type == Card.TypeTrick and
not cardEffectEvent.disresponsive then
elseif
cardEffectEvent.card.type == Card.TypeTrick and
not (cardEffectEvent.disresponsive or cardEffectEvent.unoffsetable) and
not table.contains(cardEffectEvent.prohibitedCardNames or {}, "nullification")
then
local players = {}
for _, p in ipairs(self.alive_players) do
local cards = p:getCardIds(Player.Hand)
for _, cid in ipairs(cards) do
if Fk:getCardById(cid).name == "nullification" and
not table.contains(cardEffectEvent.disresponsiveList or {}, p.id) then
if
Fk:getCardById(cid).name == "nullification" and
not (
table.contains(cardEffectEvent.disresponsiveList or {}, p.id) or
table.contains(cardEffectEvent.unoffsetableList or {}, p.id)
)
then
table.insert(players, p)
break
end
end
if not table.contains(players, p) then
for _, s in ipairs(p.player_skills) do
if s.pattern and Exppattern:Parse("nullification"):matchExp(s.pattern)
and not table.contains(cardEffectEvent.disresponsiveList or {}, p.id) then
if
s.pattern and
Exppattern:Parse("nullification"):matchExp(s.pattern) and
not (
table.contains(cardEffectEvent.disresponsiveList or {}, p.id) or
table.contains(cardEffectEvent.unoffsetableList or {}, p.id)
)
then
table.insert(players, p)
break
end
@ -2042,9 +2120,7 @@ function Room:doCardEffect(cardEffectEvent)
self:useCard(use)
end
end
end
if event == fk.CardEffecting then
elseif event == fk.CardEffecting then
if cardEffectEvent.card.skill then
execGameEvent(GameEvent.SkillEffect, function ()
cardEffectEvent.card.skill:onEffect(self, cardEffectEvent)
@ -2052,7 +2128,6 @@ function Room:doCardEffect(cardEffectEvent)
end
end
end
end
--- 对“打出牌”进行处理
---@param cardResponseEvent CardResponseEvent
@ -2687,6 +2762,44 @@ function Room:getCardsFromPileByRule(pattern, num, fromPile)
return cardPack
end
---@param flag string|null
---@param players ServerPlayer[]|null
---@return PlayerId[] @ 可能为空
function Room:canMoveCardInBoard(flag, players)
if flag then
assert(flag == "e" or flag == "j")
end
players = players or self.alive_players
local targets = {}
table.find(players, function(p)
local canMoveTo = table.find(players, function(another)
return p ~= another and p:canMoveCardsInBoardTo(another, flag)
end)
if canMoveTo then
targets = {p.id, canMoveTo.id}
end
return canMoveTo
end)
return targets
end
function Room:updateQuestSkillState(player, skillName, failed)
assert(Fk.skills[skillName].frequency == Skill.Quest)
self:setPlayerMark(player, MarkEnum.QuestSkillPreName .. skillName, failed and "failed" or "succeed")
local updateValue = failed and 2 or 1
self:doBroadcastNotify("UpdateQuestSkillUI", json.encode{
player.id,
skillName,
updateValue,
})
end
function CreateRoom(_room)
RoomInstance = Room:new(_room)
end

View File

@ -324,6 +324,10 @@ function ServerPlayer:getNextAlive()
end
function ServerPlayer:turnOver()
if self.room.logic:trigger(fk.BeforeTurnOver, self) then
return
end
self.faceup = not self.faceup
self.room:broadcastProperty(self, "faceup")
@ -332,6 +336,7 @@ function ServerPlayer:turnOver()
from = self.id,
arg = self.faceup and "face_up" or "face_down",
}
self.room.logic:trigger(fk.TurnedOver, self)
end
@ -593,6 +598,10 @@ end
---@param chained boolean
function ServerPlayer:setChainState(chained)
if self.room.logic:trigger(fk.BeforeChainStateChange, self) then
return
end
self.chained = chained
self.room:broadcastProperty(self, "chained")
self.room:sendLog{
@ -600,6 +609,8 @@ function ServerPlayer:setChainState(chained)
from = self.id,
arg = self.chained and "chained" or "not-chained"
}
self.room.logic:trigger(fk.ChainStateChanged, self)
end
---@param from ServerPlayer

View File

@ -93,8 +93,11 @@ fk.IceDamage = 4
---@field public disresponsiveList integer[]|null
---@field public unoffsetableList integer[]|null
---@field public additionalDamage integer|null
---@field public additionalRecover integer|null
---@field public customFrom integer|null
---@field public cardsResponded Card[]|null
---@field public prohibitedCardNames string[]|null
---@field public damageDealt table<PlayerId, number>|null
---@class AimStruct
---@field public from integer
@ -106,6 +109,7 @@ fk.IceDamage = 4
---@field public nullifiedTargets integer[]|null
---@field public firstTarget boolean
---@field public additionalDamage integer|null
---@field public additionalRecover integer|null
---@field public disresponsive boolean|null
---@field public unoffsetableList boolean|null
---@field public additionalResponseTimes table<string, integer>|integer|null
@ -124,6 +128,7 @@ fk.IceDamage = 4
---@field public disresponsiveList integer[]|null
---@field public unoffsetableList integer[]|null
---@field public additionalDamage integer|null
---@field public additionalRecover integer|null
---@field public customFrom integer|null
---@field public cardsResponded Card[]|null
---@field public disresponsive boolean|null
@ -131,6 +136,7 @@ fk.IceDamage = 4
---@field public isCancellOut boolean|null
---@field public fixedResponseTimes table<string, integer>|integer|null
---@field public fixedAddTimesResponsors integer[]
---@field public prohibitedCardNames string[]|null
---@class SkillEffectEvent
---@field public from integer

View File

@ -18,7 +18,7 @@ local thunderSlashSkill = fk.CreateActiveSkill{
from = room:getPlayerById(from),
to = room:getPlayerById(to),
card = effect.card,
damage = 1 + (effect.additionalDamage or 0),
damage = 1,
damageType = fk.ThunderDamage,
skillName = self.name
})
@ -56,7 +56,7 @@ local fireSlashSkill = fk.CreateActiveSkill{
from = room:getPlayerById(from),
to = room:getPlayerById(to),
card = effect.card,
damage = 1 + (effect.additionalDamage or 0),
damage = 1,
damageType = fk.FireDamage,
skillName = self.name
})
@ -80,7 +80,7 @@ local analepticSkill = fk.CreateActiveSkill{
name = "analeptic_skill",
max_turn_use_time = 1,
can_use = function(self, player, card)
return player:usedCardTimes("analeptic", Player.HistoryTurn) < self:getMaxUseTime(Self, Player.HistoryTurn, card)
return player:usedCardTimes("analeptic", Player.HistoryTurn) < self:getMaxUseTime(Self, Player.HistoryTurn, card, Self)
end,
on_use = function(self, room, use)
if not use.tos or #TargetGroup:getRealTargets(use.tos) == 0 then
@ -250,7 +250,7 @@ local fireAttackSkill = fk.CreateActiveSkill{
from = from,
to = to,
card = cardEffectEvent.card,
damage = 1 + (cardEffectEvent.additionalDamage or 0),
damage = 1,
damageType = fk.FireDamage,
skillName = self.name
})
@ -276,7 +276,7 @@ local supplyShortageSkill = fk.CreateActiveSkill{
local player = Fk:currentRoom():getPlayerById(to_select)
if Self ~= player then
return not player:hasDelayedTrick("supply_shortage") and
Self:distanceTo(player) <= self:getDistanceLimit(Self, card)
Self:distanceTo(player) <= self:getDistanceLimit(Self, card, Fk:currentRoom():getPlayerById(to_select))
end
end
return false

View File

@ -74,7 +74,27 @@ local maxCardsSkill = fk.CreateMaxCardsSkill{
name = "max_cards_skill",
global = true,
correct_func = function(self, player)
return player:getMark("AddMaxCards") + player:getMark("AddMaxCards-turn") - player:getMark("MinusMaxCards") - player:getMark("MinusMaxCards-turn")
return
player:getMark(MarkEnum.AddMaxCards) +
player:getMark(MarkEnum.AddMaxCardsInTurn) -
player:getMark(MarkEnum.MinusMaxCards) -
player:getMark(MarkEnum.MinusMaxCardsInTurn)
end,
}
local choosePlayersToMoveCardInBoardSkill = fk.CreateActiveSkill{
name = "choose_players_to_move_card_in_board",
target_num = 2,
card_filter = function(self, to_select)
return false
end,
target_filter = function(self, to_select, selected, cards)
local target = Fk:currentRoom():getPlayerById(to_select)
if #selected > 0 then
return Fk:currentRoom():getPlayerById(selected[1]):canMoveCardsInBoardTo(target, self.flag)
end
return #target:getCardIds({ Player.Equip, Player.Judge }) > 0
end,
}
@ -83,4 +103,5 @@ AuxSkills = {
chooseCardsSkill,
choosePlayersSkill,
maxCardsSkill,
choosePlayersToMoveCardInBoardSkill,
}

View File

@ -418,6 +418,7 @@ Fk:loadTranslationTable{
["discard_skill"] = "弃牌",
["choose_cards_skill"] = "选牌",
["choose_players_skill"] = "选择角色",
["choose_players_to_move_card_in_board"] = "选择角色",
["game_rule"] = "弃牌阶段",
}

View File

@ -1085,9 +1085,7 @@ local lijian = fk.CreateActiveSkill{
new_use.from = use.tos[2]
new_use.tos = { { use.tos[1] } }
new_use.card = duel
new_use.unoffsetableList = table.map(room:getAlivePlayers(), function(e)
return e.id
end)
new_use.prohibitedCardNames = { "nullification" }
room:useCard(new_use)
end,
}

View File

@ -30,13 +30,16 @@ local slashSkill = fk.CreateActiveSkill{
max_phase_use_time = 1,
target_num = 1,
can_use = function(self, player, card)
return player:usedCardTimes("slash", Player.HistoryPhase) < self:getMaxUseTime(Self, Player.HistoryPhase, card)
return
table.find(Fk:currentRoom().alive_players, function(p)
return player:usedCardTimes("slash", Player.HistoryPhase) < self:getMaxUseTime(Self, Player.HistoryPhase, card, p)
end)
end,
target_filter = function(self, to_select, selected, _, card)
if #selected < self:getMaxTargetNum(Self, card) then
local player = Fk:currentRoom():getPlayerById(to_select)
return Self ~= player and
(self:getDistanceLimit(Self, card) -- for no distance limit for slash
(self:getDistanceLimit(Self, card, Fk:currentRoom():getPlayerById(to_select)) -- for no distance limit for slash
+ Self:getAttackRange()
>= Self:distanceTo(player))
end
@ -49,7 +52,7 @@ local slashSkill = fk.CreateActiveSkill{
from = room:getPlayerById(from),
to = room:getPlayerById(to),
card = effect.card,
damage = 1 + (effect.additionalDamage or 0),
damage = 1,
damageType = fk.NormalDamage,
skillName = self.name
})
@ -224,8 +227,10 @@ local snatchSkill = fk.CreateActiveSkill{
target_filter = function(self, to_select, selected, _, card)
if #selected == 0 then
local player = Fk:currentRoom():getPlayerById(to_select)
return Self ~= player and Self:distanceTo(player) <= self:getDistanceLimit(Self, card) -- for no distance limit for snatch
and not player:isAllNude()
return
Self ~= player and
Self:distanceTo(player) <= self:getDistanceLimit(Self, card, Fk:currentRoom():getPlayerById(to_select)) and -- for no distance limit for snatch
not player:isAllNude()
end
end,
target_num = 1,
@ -276,10 +281,6 @@ local duelSkill = fk.CreateActiveSkill{
local currentResponser = to
while currentResponser:isAlive() do
if effect.disresponsive or table.contains(effect.disresponsiveList or {}, currentResponser.id) then
break
end
local loopTimes = 1
if effect.fixedResponseTimes then
local canFix = currentResponser == to
@ -298,7 +299,7 @@ local duelSkill = fk.CreateActiveSkill{
local cardResponded
for i = 1, loopTimes do
cardResponded = room:askForResponse(currentResponser, 'slash')
cardResponded = room:askForResponse(currentResponser, 'slash', nil, nil, false, nil, effect)
if cardResponded then
room:responseCard({
from = currentResponser.id,
@ -323,7 +324,7 @@ local duelSkill = fk.CreateActiveSkill{
from = responsers[currentTurn % 2 + 1],
to = currentResponser,
card = effect.card,
damage = 1 + (effect.additionalDamage or 0),
damage = 1,
damageType = fk.NormalDamage,
skillName = self.name,
})
@ -364,7 +365,7 @@ local collateralSkill = fk.CreateActiveSkill{
local to = room:getPlayerById(effect.to)
if not to:getEquipment(Card.SubtypeWeapon) then return end
local use = room:askForUseCard(to, "slash", nil, nil, nil,
{ must_targets = effect.subTargets })
{ must_targets = effect.subTargets }, effect)
if use then
room:useCard(use)
@ -443,10 +444,7 @@ local savageAssaultSkill = fk.CreateActiveSkill{
name = "savage_assault_skill",
on_use = aoe_on_use,
on_effect = function(self, room, effect)
local cardResponded = nil
if not (effect.disresponsive or table.contains(effect.disresponsiveList or {}, effect.to)) then
cardResponded = room:askForResponse(room:getPlayerById(effect.to), 'slash')
end
local cardResponded = room:askForResponse(room:getPlayerById(effect.to), 'slash', nil, nil, false, nil, effect)
if cardResponded then
room:responseCard({
@ -459,7 +457,7 @@ local savageAssaultSkill = fk.CreateActiveSkill{
from = room:getPlayerById(effect.from),
to = room:getPlayerById(effect.to),
card = effect.card,
damage = 1 + (effect.additionalDamage or 0),
damage = 1,
damageType = fk.NormalDamage,
skillName = self.name,
})
@ -484,10 +482,7 @@ local archeryAttackSkill = fk.CreateActiveSkill{
name = "archery_attack_skill",
on_use = aoe_on_use,
on_effect = function(self, room, effect)
local cardResponded = nil
if not (effect.disresponsive or table.contains(effect.disresponsiveList or {}, effect.to)) then
cardResponded = room:askForResponse(room:getPlayerById(effect.to), 'jink')
end
local cardResponded = room:askForResponse(room:getPlayerById(effect.to), 'jink', nil, nil, false, nil, effect)
if cardResponded then
room:responseCard({
@ -500,7 +495,7 @@ local archeryAttackSkill = fk.CreateActiveSkill{
from = room:getPlayerById(effect.from),
to = room:getPlayerById(effect.to),
card = effect.card,
damage = 1 + (effect.additionalDamage or 0),
damage = 1,
damageType = fk.NormalDamage,
skillName = self.name,
})

View File

@ -37,7 +37,7 @@ local cheat = fk.CreateActiveSkill{
return
end
local cardName = room:askForChoice(from, allCardNames, "cheat")
local cardName = room:askForChoice(from, allCardNames, "cheat", nil, nil, true)
local toGain = nil
if #allCardMapper[cardName] > 0 then
toGain = allCardMapper[cardName][math.random(1, #allCardMapper[cardName])]