Skip nullification (#227)

- 完善移动场上一张牌移动虚拟牌及选框中显示逻辑,并增加排除特定卡牌id的参数;
- 多目标锦囊牌询问无懈时新增“本轮忽略”功能;
- 修复传导伤害额外传导自身的问题。
This commit is contained in:
Ho-spair 2023-07-16 19:18:43 +08:00 committed by GitHub
parent acda9f4eb8
commit 8ca9f9154c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 128 additions and 31 deletions

View File

@ -42,6 +42,7 @@ Item {
property bool respond_play: false
property bool autoPending: false
property var extra_data: ({})
property var skippedUseEventId: []
Image {
source: config.roomBg
@ -571,6 +572,16 @@ Item {
spacing: 20
visible: false
Button {
id: skipNullificationButton
text: Backend.translate("SkipNullification")
visible: !!extra_data.useEventId && !skippedUseEventId.find(id => id === extra_data.useEventId)
onClicked: {
skippedUseEventId.push(extra_data.useEventId);
Logic.doCancelButton();
}
}
Button {
id: okButton
text: Backend.translate("OK")

View File

@ -999,20 +999,22 @@ callbacks["AskForCardsChosen"] = (jsonData) => {
callbacks["AskForMoveCardInBoard"] = (jsonData) => {
const data = JSON.parse(jsonData);
const { cards, cardsPosition, generalNames } = data;
const { cards, cardsPosition, generalNames, playerIds } = data;
roomScene.state = "replying";
roomScene.popupBox.sourceComponent = Qt.createComponent("../RoomElement/MoveCardInBoardBox.qml");
const boxCards = [];
cards.forEach(id => {
const d = Backend.callLuaFunction("GetCardData", [id]);
const cardPos = cardsPosition[cards.findIndex(cid => cid === id)];
const d = Backend.callLuaFunction("GetCardData", [id, playerIds[cardPos]]);
boxCards.push(JSON.parse(d));
});
const box = roomScene.popupBox.item;
box.cards = boxCards;
box.cardsPosition = cardsPosition;
box.playerIds = playerIds;
box.generalNames = generalNames.map(name => {
const namesSplited = name.split('/');
return namesSplited.length > 1 ? namesSplited.map(nameSplited => Backend.translate(nameSplited)).join('/') : Backend.translate(name)
@ -1127,7 +1129,13 @@ callbacks["AskForUseCard"] = (jsonData) => {
const prompt = data[2];
const extra_data = data[4];
if (extra_data != null) {
roomScene.extra_data = extra_data;
if (extra_data.effectTo !== Self.id && roomScene.skippedUseEventId.find(id => id === extra_data.useEventId)) {
doCancelButton();
return;
} else {
console.log(extra_data);
roomScene.extra_data = extra_data;
}
}
if (prompt === "") {

View File

@ -9,6 +9,7 @@ GraphicsBox {
property var cards: []
property var cardsPosition: []
property var generalNames: []
property var playerIds: []
property var result
property int padding: 25
@ -64,7 +65,7 @@ GraphicsBox {
Text {
horizontalAlignment: Text.AlignHCenter
anchors.centerIn: parent
text: Backend.translate(JSON.parse(Backend.callLuaFunction("GetCardData", [modelData.cid])).subtype)
text: Backend.translate(modelData.subtype)
color: "#90765F"
font.family: fontLibian.name
font.pixelSize: 16
@ -100,6 +101,7 @@ GraphicsBox {
name: modelData.name
suit: modelData.suit
number: modelData.number
virt_name: modelData.virt_name || ''
selectable: !result || result.item === this
onClicked: {

View File

@ -76,7 +76,7 @@ local cardSubtypeStrings = {
[Card.SubtypeTreasure] = "treasure",
}
function GetCardData(id)
function GetCardData(id, virtualCardForm)
local card = Fk:getCardById(id)
if card == nil then return json.encode{
cid = id,
@ -106,6 +106,13 @@ function GetCardData(id)
ret.name = orig.name
ret.virt_name = card.name
end
if virtualCardForm then
local virtualCard = ClientInstance:getPlayerById(virtualCardForm):getVirualEquip(id)
if virtualCard then
ret.virt_name = virtualCard.name
ret.subtype = cardSubtypeStrings[virtualCard.sub_type]
end
end
return json.encode(ret)
end

View File

@ -137,6 +137,7 @@ FreeKill使用的是libgit2的C API与此同时使用Git完成拓展包的下
["Exit Lobby"] = "退出大厅",
["SkipNullification"] = "本轮忽略",
["OK"] = "确定",
["Cancel"] = "取消",
["End"] = "结束",

View File

@ -856,7 +856,7 @@ function Player:canMoveCardInBoardTo(to, id)
return false
end
local card = Fk:getCardById(id)
local card = self:getVirualEquip(id) or Fk:getCardById(id)
assert(card.type == Card.TypeEquip or card.sub_type == Card.SubtypeDelayedTrick)
if card.type == Card.TypeEquip then
@ -868,12 +868,13 @@ function Player:canMoveCardInBoardTo(to, id)
end
end
function Player:canMoveCardsInBoardTo(to, flag)
function Player:canMoveCardsInBoardTo(to, flag, excludeIds)
if self == to then
return false
end
assert(flag == nil or flag == "e" or flag == "j")
excludeIds = type(excludeIds) == "table" and excludeIds or {}
local areas = {}
if flag == "e" then
@ -885,7 +886,7 @@ function Player:canMoveCardsInBoardTo(to, flag)
end
for _, cardId in ipairs(self:getCardIds(areas)) do
if self:canMoveCardInBoardTo(to, cardId) then
if not table.contains(excludeIds, cardId) and self:canMoveCardInBoardTo(to, cardId) then
return true
end
end

View File

@ -191,7 +191,7 @@ GameEvent.exit_funcs[GameEvent.Damage] = function(self)
room.logic:trigger(fk.DamageFinished, damageStruct.to, damageStruct)
if damageStruct.beginnerOfTheDamage and not damageStruct.chain then
local targets = table.filter(room:getAlivePlayers(), function(p)
local targets = table.filter(room:getOtherPlayers(damageStruct.to), function(p)
return p.chained
end)
for _, p in ipairs(targets) do
@ -225,7 +225,7 @@ GameEvent.functions[GameEvent.LoseHp] = function(self)
self.logic:breakEvent(false)
end
if not self:changeHp(player, -num, "loseHp", skillName) then
if not self:changeHp(player, -data.num, "loseHp", skillName) then
self.logic:breakEvent(false)
end

View File

@ -62,12 +62,10 @@ GameEvent.functions[GameEvent.MoveCards] = function(self)
for _, info in ipairs(data.moveInfo) do
local realFromArea = self:getCardArea(info.cardId)
local playerAreas = { Player.Hand, Player.Equip, Player.Judge, Player.Special }
local virtualEquip
if table.contains(playerAreas, realFromArea) and data.from then
local from = self:getPlayerById(data.from)
from:removeCards(realFromArea, { info.cardId }, info.fromSpecialName)
virtualEquip = from:getVirualEquip(info.cardId)
elseif realFromArea ~= Card.Unknown then
local fromAreaIds = {}
@ -86,7 +84,6 @@ GameEvent.functions[GameEvent.MoveCards] = function(self)
if table.contains(playerAreas, data.toArea) and data.to then
local to = self:getPlayerById(data.to)
if virtualEquip then to:addVirtualEquip(virtualEquip) end
to:addCards(data.toArea, { info.cardId }, data.specialName)
else

View File

@ -790,6 +790,20 @@ function Room:notifyMoveCards(players, card_moves, forceVisible)
return false
end
for _, info in ipairs(move.moveInfo) do
local realFromArea = self:getCardArea(info.cardId)
local playerAreas = { Player.Hand, Player.Equip, Player.Judge, Player.Special }
local virtualEquip
if table.contains(playerAreas, realFromArea) and move.from then
virtualEquip = self:getPlayerById(move.from):getVirualEquip(info.cardId)
end
if table.contains(playerAreas, move.toArea) and move.to and virtualEquip then
self:getPlayerById(move.to):addVirtualEquip(virtualEquip)
end
end
-- forceVisible make the move visible
-- FIXME: move.moveInfo is an array, fix this
move.moveVisible = move.moveVisible or (forceVisible)
@ -1840,26 +1854,29 @@ end
---@param skillName string @ 技能名
---@param flag string|null @ 限定可移动的区域值为nil装备区和判定区ej
---@param moveFrom ServerPlayer|null @ 是否只是目标1移动给目标2
---@return table<"card"|"from"|"to"> @ 选择的卡牌、起点玩家id和终点玩家id列表
function Room:askForMoveCardInBoard(player, targetOne, targetTwo, skillName, flag, moveFrom)
---@param excludeIds CardId[]|null @ 本次不可移动的卡牌id
---@return table<"card"|"from"|"to">|null @ 选择的卡牌、起点玩家id和终点玩家id列表
function Room:askForMoveCardInBoard(player, targetOne, targetTwo, skillName, flag, moveFrom, excludeIds)
if flag then
assert(flag == "e" or flag == "j")
end
excludeIds = type(excludeIds) == "table" and excludeIds or {}
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
if not table.contains(excludeIds, equipId) and 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
if not table.contains(excludeIds, equipId) and targetTwo:canMoveCardInBoardTo(targetOne, equipId) then
table.insert(cards, equipId)
end
end
@ -1882,7 +1899,7 @@ function Room:askForMoveCardInBoard(player, targetOne, targetTwo, skillName, fla
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
if not table.contains(excludeIds, trickId) and targetOne:canMoveCardInBoardTo(targetTwo, trickId) then
table.insert(cards, trickId)
table.insert(cardsPosition, 0)
end
@ -1890,7 +1907,7 @@ function Room:askForMoveCardInBoard(player, targetOne, targetTwo, skillName, fla
end
if not moveFrom or moveFrom == targetTwo then
for _, trickId in ipairs(targetTwo:getCardIds(Player.Judge)) do
if targetTwo:canMoveCardInBoardTo(targetOne, trickId) then
if not table.contains(excludeIds, trickId) and targetTwo:canMoveCardInBoardTo(targetOne, trickId) then
table.insert(cards, trickId)
table.insert(cardsPosition, 1)
end
@ -1905,7 +1922,12 @@ function Room:askForMoveCardInBoard(player, targetOne, targetTwo, skillName, fla
local firstGeneralName = targetOne.general + (targetOne.deputyGeneral ~= "" and ("/" .. targetOne.deputyGeneral) or "")
local secGeneralName = targetTwo.general + (targetTwo.deputyGeneral ~= "" and ("/" .. targetTwo.deputyGeneral) or "")
local data = { cards = cards, cardsPosition = cardsPosition, generalNames = { firstGeneralName, secGeneralName } }
local data = {
cards = cards,
cardsPosition = cardsPosition,
generalNames = { firstGeneralName, secGeneralName },
playerIds = { targetOne.id, targetTwo.id }
}
local command = "AskForMoveCardInBoard"
self:notifyMoveFocus(player, command)
local result = self:doRequest(player, command, json.encode(data))
@ -1917,8 +1939,8 @@ function Room:askForMoveCardInBoard(player, targetOne, targetTwo, skillName, fla
result = json.decode(result)
end
local cardToMove = Fk:getCardById(result.cardId)
local from, to = result.pos == 0 and targetOne, targetTwo or targetTwo, targetOne
local cardToMove = self:getCardOwner(result.cardId):getVirualEquip(result.cardId) or Fk:getCardById(result.cardId)
self:moveCardTo(
cardToMove,
cardToMove.type == Card.TypeEquip and Player.Equip or Player.Judge,
@ -1940,16 +1962,18 @@ end
---@param flag string|null @ 限定可移动的区域值为nil装备区和判定区ej
---@param no_indicate boolean|nil @ 是否不显示指示线
---@return integer[] @ 选择的玩家id列表可能为空
function Room:askForChooseToMoveCardInBoard(player, prompt, skillName, cancelable, flag, no_indicate)
function Room:askForChooseToMoveCardInBoard(player, prompt, skillName, cancelable, flag, no_indicate, excludeIds)
if flag then
assert(flag == "e" or flag == "j")
end
cancelable = (cancelable == nil) and true or cancelable
no_indicate = (no_indicate == nil) and true or no_indicate
excludeIds = type(excludeIds) == "table" and excludeIds or {}
local data = {
flag = flag,
skillName = skillName,
excludeIds = excludeIds,
}
local _, ret = self:askForUseActiveSkill(
player,
@ -1966,7 +1990,7 @@ function Room:askForChooseToMoveCardInBoard(player, prompt, skillName, cancelabl
if cancelable then
return {}
else
return self:canMoveCardInBoard(flag)
return self:canMoveCardInBoard(flag, excludeIds)
end
end
end
@ -2371,7 +2395,15 @@ function Room:handleCardEffect(event, cardEffectEvent)
elseif cardEffectEvent.from then
prompt = "#AskForNullificationWithoutTo:" .. cardEffectEvent.from .. "::" .. cardEffectEvent.card.name
end
local use = self:askForNullification(players, nil, nil, prompt)
local extra_data
if #TargetGroup:getRealTargets(cardEffectEvent.tos) > 1 then
local parentUseEvent = self.logic:getCurrentEvent():findParent(GameEvent.UseCard)
if parentUseEvent then
extra_data = { useEventId = parentUseEvent.id, effectTo = cardEffectEvent.to }
end
end
local use = self:askForNullification(players, nil, nil, prompt, true, extra_data)
if use then
use.toCard = cardEffectEvent.card
use.responseToEvent = cardEffectEvent
@ -3062,18 +3094,20 @@ end
---@param flag string|null
---@param players ServerPlayer[]|null
---@param excludeIds CardId[]|null
---@return PlayerId[] @ 可能为空
function Room:canMoveCardInBoard(flag, players)
function Room:canMoveCardInBoard(flag, players, excludeIds)
if flag then
assert(flag == "e" or flag == "j")
end
players = players or self.alive_players
excludeIds = type(excludeIds) == "table" and excludeIds or {}
local targets = {}
table.find(players, function(p)
local canMoveTo = table.find(players, function(another)
return p ~= another and p:canMoveCardsInBoardTo(another, flag)
return p ~= another and p:canMoveCardsInBoardTo(another, flag, excludeIds)
end)
if canMoveTo then

View File

@ -99,10 +99,19 @@ local choosePlayersToMoveCardInBoardSkill = fk.CreateActiveSkill{
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)
return Fk:currentRoom():getPlayerById(selected[1]):canMoveCardsInBoardTo(target, self.flag, self.excludeIds)
end
return #target:getCardIds({ Player.Equip, Player.Judge }) > 0
local fromAreas = { Player.Equip, Player.Judge }
if self.flag == "e" then
fromAreas = { Player.Equip }
elseif self.flag == "j" then
fromAreas = { Player.Judge }
end
return #table.filter(target:getCardIds(fromAreas), function(id)
return not table.contains((type(self.excludeIds) == "table" and self.excludeIds or {}), id)
end) > 0
end,
}

View File

@ -1092,8 +1092,22 @@ local role_mode = fk.CreateGameMode{
roleTable = roleTable[#Fk:currentRoom().players]
if Self.role == "renegade" then
roleCheck = #Fk:currentRoom().alive_players == 2
roleText = "only you and me"
local rebelNum = #table.filter(roleTable, function(role)
return role == "rebel"
end)
for _, p in ipairs(Fk:currentRoom().players) do
if p.role == "rebel" then
if not p.dead then
break
else
rebelNum = rebelNum - 1
end
end
end
roleCheck = rebelNum == 0
roleText = "left lord and loyalist alive"
elseif Self.role == "rebel" then
local rebelNum = #table.filter(roleTable, function(role)
return role == "rebel"
@ -1162,7 +1176,7 @@ local role_mode = fk.CreateGameMode{
extension:addGameMode(role_mode)
Fk:loadTranslationTable{
["time limitation: 5 sec"] = "游戏时长达到5秒测试用",
["only you and me"] = "仅剩你和主公存活",
["left lord and loyalist alive"] = "仅剩你和主忠方存活",
["left one rebel alive"] = "反贼仅剩你存活且不存在存活内奸",
["left you alive"] = "主忠方仅剩你存活且其他阵营仅剩一方",
["loyalist never surrender"] = "忠臣永不投降!",

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB

View File

@ -1,6 +1,7 @@
-- SPDX-License-Identifier: GPL-3.0-or-later
local extension = Package("test_p_0")
extension.extensionName = "test"
local cheat = fk.CreateActiveSkill{
name = "cheat",
@ -270,6 +271,18 @@ test2:addSkill(damage_maker)
test2:addSkill(change_hero)
test2:addSkill(test_zhenggong)
local shibing = General(extension, "blank_shibing", "qun", 5)
shibing.hidden = true
Fk:loadTranslationTable{
["blank_shibing"] = "男士兵",
}
local nvshibing = General(extension, "blank_nvshibing", "qun", 5, 5, General.Female)
Fk:loadTranslationTable{
["blank_nvshibing"] = "女士兵",
}
nvshibing.hidden = true
Fk:loadTranslationTable{
["test_p_0"] = "测试包",
["test"] = "测试",