bugfix (#328)
- 为findParent添加深度限制参数(默认无限制) - 搬运了damageByCardEffect - 修复了ex__choose_skill - 修复了华佗、吕蒙和古锭刀 - 添加势力映射,可以指定一个势力必须变成其他几个势力之一(需要神话再临包/OL包自行处理神将变将范围) - askForCardsChosen界限突破,改成了基于askForPoxi的格式 - 修复了空城虚拟杀可以方天的bug - 给强制平局添加了原因提醒 - 优化了移动牌的视觉逻辑
This commit is contained in:
parent
75d0ad55c8
commit
e840a3b322
|
@ -275,15 +275,30 @@ function moveCards(moves) {
|
|||
const move = moves[i];
|
||||
const from = getAreaItem(move.fromArea, move.from);
|
||||
const to = getAreaItem(move.toArea, move.to);
|
||||
if (!from || !to || from === to)
|
||||
if (!from || !to || (from === to && move.fromArea !== Card.DiscardPile))
|
||||
continue;
|
||||
const items = from.remove(move.ids, move.fromSpecialName);
|
||||
if (items.length > 0)
|
||||
to.add(items, move.specialName);
|
||||
to.updateCardPosition(true);
|
||||
if (to === tablePile) {
|
||||
let vanished = items.filter(c => c.cid === -1);
|
||||
if (vanished.length > 0) {
|
||||
drawPile.add(vanished, move.specialName);
|
||||
drawPile.updateCardPosition(true);
|
||||
}
|
||||
vanished = items.filter(c => c.cid !== -1);
|
||||
if (vanished.length > 0) {
|
||||
to.add(vanished, move.specialName);
|
||||
to.updateCardPosition(true);
|
||||
}
|
||||
} else {
|
||||
if (items.length > 0)
|
||||
to.add(items, move.specialName);
|
||||
to.updateCardPosition(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
function resortHandcards() {
|
||||
if (!dashboard.handcardArea.cards.length) {
|
||||
return;
|
||||
|
@ -1174,9 +1189,9 @@ callbacks["AskForPoxi"] = (jsonData) => {
|
|||
roomScene.popupBox.sourceComponent =
|
||||
Qt.createComponent("../RoomElement/PoxiBox.qml");
|
||||
const box = roomScene.popupBox.item;
|
||||
box.extra_data = JSON.stringify(extra_data);
|
||||
box.poxi_type = type;
|
||||
box.card_data = data;
|
||||
box.extra_data = extra_data;
|
||||
box.cancelable = cancelable;
|
||||
for (let d of data) {
|
||||
const arr = [];
|
||||
|
@ -1185,6 +1200,7 @@ callbacks["AskForPoxi"] = (jsonData) => {
|
|||
ids.forEach(id => arr.push(lcall("GetCardData", id)));
|
||||
box.addCustomCards(d[0], arr);
|
||||
}
|
||||
box.refreshPrompt();
|
||||
|
||||
roomScene.popupBox.moveToCenter();
|
||||
box.cardsSelected.connect((ids) => {
|
||||
|
|
|
@ -2,12 +2,13 @@
|
|||
|
||||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
import Fk
|
||||
import Fk.Pages
|
||||
|
||||
GraphicsBox {
|
||||
id: root
|
||||
|
||||
title.text: lcall("PoxiPrompt", poxi_type, card_data, extra_data)
|
||||
title.text: Util.processPrompt(lcall("PoxiPrompt", poxi_type, card_data, extra_data))
|
||||
|
||||
// TODO: Adjust the UI design in case there are more than 7 cards
|
||||
width: 70 + 700
|
||||
|
@ -71,7 +72,7 @@ GraphicsBox {
|
|||
number: model.number || 0
|
||||
autoBack: false
|
||||
known: model.cid !== -1
|
||||
selectable: root.selected_ids.includes(model.cid) ||
|
||||
selectable: chosenInBox ||
|
||||
lcall("PoxiFilter", root.poxi_type, model.cid, root.selected_ids,
|
||||
root.card_data, root.extra_data);
|
||||
|
||||
|
@ -84,6 +85,7 @@ GraphicsBox {
|
|||
root.selected_ids.splice(root.selected_ids.indexOf(cid), 1);
|
||||
}
|
||||
root.selected_ids = root.selected_ids;
|
||||
refreshPrompt();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -123,7 +125,7 @@ GraphicsBox {
|
|||
let ret;
|
||||
for (let i = 0; i < cardModel.count; i++) {
|
||||
let item = cardModel.get(i);
|
||||
if (item.areaName == name) {
|
||||
if (item.areaName === name) {
|
||||
ret = item;
|
||||
break;
|
||||
}
|
||||
|
@ -149,4 +151,8 @@ GraphicsBox {
|
|||
area.append(cards);
|
||||
}
|
||||
}
|
||||
|
||||
function refreshPrompt() {
|
||||
root.title.text = Util.processPrompt(lcall("PoxiPrompt", poxi_type, card_data, extra_data))
|
||||
}
|
||||
}
|
||||
|
|
|
@ -744,6 +744,7 @@ end
|
|||
|
||||
function PoxiPrompt(poxi_type, data, extra_data)
|
||||
local poxi = Fk.poxi_methods[poxi_type]
|
||||
extra_data = extra_data and json.decode(extra_data)
|
||||
if not poxi or not poxi.prompt then return "" end
|
||||
if type(poxi.prompt) == "string" then return Fk:translate(poxi.prompt) end
|
||||
return Fk:translate(poxi.prompt(data, extra_data))
|
||||
|
@ -751,12 +752,14 @@ end
|
|||
|
||||
function PoxiFilter(poxi_type, to_select, selected, data, extra_data)
|
||||
local poxi = Fk.poxi_methods[poxi_type]
|
||||
extra_data = extra_data and json.decode(extra_data)
|
||||
if not poxi then return "false" end
|
||||
return json.encode(poxi.card_filter(to_select, selected, data, extra_data))
|
||||
end
|
||||
|
||||
function PoxiFeasible(poxi_type, selected, data, extra_data)
|
||||
local poxi = Fk.poxi_methods[poxi_type]
|
||||
extra_data = extra_data and json.decode(extra_data)
|
||||
if not poxi then return "false" end
|
||||
return json.encode(poxi.feasible(selected, data, extra_data))
|
||||
end
|
||||
|
|
|
@ -250,6 +250,12 @@ Fk:loadTranslationTable({
|
|||
-- ["Trusting ..."] = "托管中 ...",
|
||||
-- ["Observing ..."] = "旁观中 ...",
|
||||
|
||||
["#NoCardDraw"] = "Card Pile is empty",
|
||||
["#NoGeneralDraw"] = "General Pile is empty",
|
||||
["#NoEventDraw"] = "All game events terminated",
|
||||
["#NoEnoughGeneralDraw"] = "No enough generals! (%arg/%arg2)",
|
||||
["#TimeOutDraw"] = "It's over 9999 Round!",
|
||||
|
||||
["$GameOver"] = "Game Over",
|
||||
["$Winner"] = "Winner is %1",
|
||||
["$NoWinner"] = "Draw!",
|
||||
|
|
|
@ -307,6 +307,12 @@ FreeKill使用的是libgit2的C API,与此同时使用Git完成拓展包的下
|
|||
["resting..."] = "休整中",
|
||||
["rest round num"] = "轮次",
|
||||
|
||||
["#NoCardDraw"] = "牌堆被摸空了",
|
||||
["#NoGeneralDraw"] = "武将牌堆被摸空了",
|
||||
["#NoEventDraw"] = "没有可执行的事件",
|
||||
["#NoEnoughGeneralDraw"] = "武将数不足!(%arg/%arg2)",
|
||||
["#TimeOutDraw"] = "轮数已经突破极限!",
|
||||
|
||||
["$GameOver"] = "游戏结束",
|
||||
["$Winner"] = "%1 获胜",
|
||||
["$NoWinner"] = "平局!",
|
||||
|
|
|
@ -27,6 +27,8 @@
|
|||
---@field public currentResponseReason string @ 要求用牌的原因(如濒死,被特定牌指定,使用特定技能···)
|
||||
---@field public filtered_cards table<integer, Card> @ 被锁视技影响的卡牌
|
||||
---@field public printed_cards table<integer, Card> @ 被某些房间现场打印的卡牌,id都是负数且从-2开始
|
||||
---@field private kingdoms string[] @ 总势力
|
||||
---@field private kingdom_map table<string, string[]> @ 势力映射表
|
||||
---@field private _custom_events any[] @ 自定义事件列表
|
||||
---@field public poxi_methods table<string, PoxiSpec> @ “魄袭”框操作方法表
|
||||
---@field public qml_marks table<string, QmlMarkSpec> @ 自定义Qml标记的表
|
||||
|
@ -67,6 +69,7 @@ function Engine:initialize()
|
|||
self.game_modes = {}
|
||||
self.game_mode_disabled = {}
|
||||
self.kingdoms = {}
|
||||
self.kingdom_map = {}
|
||||
self._custom_events = {}
|
||||
self.poxi_methods = {}
|
||||
self.qml_marks = {}
|
||||
|
@ -271,6 +274,30 @@ function Engine:addGenerals(generals)
|
|||
end
|
||||
end
|
||||
|
||||
--- 为一个势力添加势力映射
|
||||
---
|
||||
--- 这意味着原势力登场时必须改变为添加的几个势力之一(须存在)
|
||||
---@param kingdom string @ 原势力
|
||||
---@param kingdoms string[] @ 需要映射到的势力
|
||||
function Engine:appendKingdomMap(kingdom, kingdoms)
|
||||
local ret = self.kingdom_map[kingdom] or {}
|
||||
table.insertTableIfNeed(ret, kingdoms)
|
||||
self.kingdom_map[kingdom] = ret
|
||||
end
|
||||
|
||||
---获得一个势力所映射到的势力,若没有,返回空集
|
||||
---@param kingdom string @ 原势力
|
||||
---@return string[] @ 可用势力列表,可能是空的
|
||||
function Engine:getKingdomMap(kingdom)
|
||||
local ret = {}
|
||||
for _, k in ipairs(self.kingdom_map[kingdom] or {}) do
|
||||
if table.contains(self.kingdoms, k) then
|
||||
table.insertIfNeed(ret, k)
|
||||
end
|
||||
end
|
||||
return ret
|
||||
end
|
||||
|
||||
--- 判断一个武将是否在本房间可用。
|
||||
---@param g string @ 武将名
|
||||
function Engine:canUseGeneral(g)
|
||||
|
|
|
@ -874,7 +874,7 @@ end
|
|||
---@param card Card @ 特定牌
|
||||
---@param extra_data? UseExtraData @ 额外数据
|
||||
function Player:canUse(card, extra_data)
|
||||
return card.skill:canUse(self, card, extra_data)
|
||||
return not self:prohibitUse(card) and card.skill:canUse(self, card, extra_data)
|
||||
end
|
||||
|
||||
--- 确认玩家是否可以对特定玩家使用特定牌。
|
||||
|
|
|
@ -57,7 +57,7 @@ end
|
|||
---@param selected? integer[] @ ids of selected targets
|
||||
---@param user? integer @ id of the userdata
|
||||
---@param card? Card @ helper
|
||||
---@param distance_limited boolean @ is limited by distance
|
||||
---@param distance_limited? boolean @ is limited by distance
|
||||
function ActiveSkill:modTargetFilter(to_select, selected, user, card, distance_limited)
|
||||
return false
|
||||
end
|
||||
|
|
|
@ -235,6 +235,7 @@ end
|
|||
---@field public enabled_at_play? fun(self: ViewAsSkill, player: Player): boolean?
|
||||
---@field public enabled_at_response? fun(self: ViewAsSkill, player: Player, response: boolean): boolean?
|
||||
---@field public before_use? fun(self: ViewAsSkill, player: ServerPlayer, use: CardUseStruct): string?
|
||||
---@field public after_use? fun(self: ViewAsSkill, player: ServerPlayer, use: CardUseStruct): string?
|
||||
---@field public prompt? string|fun(self: ActiveSkill, selected: integer[], selected_cards: integer[]): string
|
||||
|
||||
---@param spec ViewAsSkillSpec
|
||||
|
|
|
@ -148,6 +148,10 @@ GameEvent.functions[GameEvent.Round] = function(self)
|
|||
room:doBroadcastNotify("UpdateRoundNum", roundCount)
|
||||
-- 强行平局 防止can_trigger报错导致瞬间几十万轮卡炸服务器
|
||||
if roundCount >= 9999 then
|
||||
room:sendLog{
|
||||
type = "#TimeOutDraw",
|
||||
toast = true,
|
||||
}
|
||||
room:gameOver("")
|
||||
end
|
||||
|
||||
|
@ -354,6 +358,7 @@ GameEvent.functions[GameEvent.Phase] = function(self)
|
|||
end)
|
||||
end
|
||||
) - player:getMaxCards()
|
||||
room:broadcastProperty(player, "MaxCards")
|
||||
if discardNum > 0 then
|
||||
room:askForDiscard(player, discardNum, discardNum, false, "game_rule", false)
|
||||
end
|
||||
|
|
|
@ -84,17 +84,32 @@ function GameEvent:prependExitFunc(f)
|
|||
table.insert(self.extra_exit_funcs, 1, f)
|
||||
end
|
||||
|
||||
function GameEvent:findParent(eventType, includeSelf)
|
||||
-- 找第一个与当前事件有继承关系的特定事件
|
||||
---@param eventType integer @ 事件类型
|
||||
---@param includeSelf bool @ 是否包括本事件
|
||||
---@param depth? integer @ 搜索深度
|
||||
---@return GameEvent?
|
||||
function GameEvent:findParent(eventType, includeSelf, depth)
|
||||
if includeSelf and self.event == eventType then return self end
|
||||
if depth == 0 then return nil end
|
||||
local e = self.parent
|
||||
local l = 1
|
||||
while e do
|
||||
if e.event == eventType then return e end
|
||||
if depth and l >= depth then break end
|
||||
e = e.parent
|
||||
l = l + 1
|
||||
end
|
||||
return nil
|
||||
end
|
||||
|
||||
-- 找n个id介于from和to之间的事件。
|
||||
---@param events GameEvent[] @ 事件数组
|
||||
---@param from integer @ 起始id
|
||||
---@param to integer @ 终止id
|
||||
---@param n integer @ 最多找多少个
|
||||
---@param func fun(e: GameEvent): boolean? @ 过滤用的函数
|
||||
---@return GameEvent[] @ 找到的符合条件的所有事件,最多n个但不保证有n个
|
||||
local function bin_search(events, from, to, n, func)
|
||||
local left = 1
|
||||
local right = #events
|
||||
|
|
|
@ -447,6 +447,10 @@ function GameLogic:start()
|
|||
end
|
||||
|
||||
if not e then -- 没有事件,按理说不应该,平局处理
|
||||
self.room:sendLog{
|
||||
type = "#NoEventDraw",
|
||||
toast = true,
|
||||
}
|
||||
self.room:gameOver("")
|
||||
end
|
||||
|
||||
|
@ -744,6 +748,21 @@ function GameLogic:getActualDamageEvents(n, func, scope, end_id)
|
|||
return ret
|
||||
end
|
||||
|
||||
--检测最近的伤害事件是否由执行牌的效果触发,即通常描述的使用牌对目标角色造成伤害
|
||||
---@param is_exact? bool @ 是否进一步判定使用者和来源是否一致(默认为true)
|
||||
---@return bool
|
||||
function GameLogic:damageByCardEffect(is_exact)
|
||||
is_exact = (is_exact == nil) and true or is_exact
|
||||
local d_event = self:getCurrentEvent():findParent(GameEvent.Damage, true)
|
||||
if d_event == nil then return false end
|
||||
local damage = d_event.data[1]
|
||||
if damage.chain or damage.card == nil then return false end
|
||||
local c_event = d_event:findParent(GameEvent.CardEffect, false, 2)
|
||||
if c_event == nil then return false end
|
||||
return damage.card == c_event.data[1].card and
|
||||
(not is_exact or d_event.data[1].from.id == c_event.data[1].from)
|
||||
end
|
||||
|
||||
function GameLogic:dumpEventStack(detailed)
|
||||
local top = self:getCurrentEvent()
|
||||
local i = self.game_event_stack.p
|
||||
|
|
|
@ -56,6 +56,7 @@ dofile "lua/server/ai/init.lua"
|
|||
gameevent.lua (游戏事件的执行逻辑,以及各种事件的执行方法)
|
||||
game_rule.lua (基础游戏规则,包括执行阶段、决胜负等)
|
||||
aux_skills.lua (某些交互方法是套壳askForUseActiveSkill,就是在这定义的)
|
||||
aux_poxi.lua (有了Poxi之后,一些交互方法改成了以各种PoxiMethod为基础的交互)
|
||||
]]----------------------------------------------------------------------
|
||||
|
||||
------------------------------------------------------------------------
|
||||
|
@ -431,6 +432,10 @@ function Room:getNCards(num, from)
|
|||
if #self.draw_pile < num then
|
||||
self:shuffleDrawPile()
|
||||
if #self.draw_pile < num then
|
||||
self:sendLog{
|
||||
type = "#NoCardDraw",
|
||||
toast = true,
|
||||
}
|
||||
self:gameOver("")
|
||||
end
|
||||
end
|
||||
|
@ -605,14 +610,16 @@ function Room:changeHero(player, new_general, full, isDeputy, sendLog, maxHpChan
|
|||
|
||||
kingdomChange = (kingdomChange == nil) and true or kingdomChange
|
||||
local kingdom = (isDeputy or not kingdomChange) and player.kingdom or new.kingdom
|
||||
if not isDeputy and kingdomChange and (new.kingdom == "god" or new.subkingdom) then
|
||||
if not isDeputy and kingdomChange then
|
||||
local allKingdoms = {}
|
||||
if new.kingdom == "god" then
|
||||
allKingdoms = table.filter({"wei", "shu", "wu", "qun", "jin"}, function(k) return table.contains(Fk.kingdoms, k) end)
|
||||
elseif new.subkingdom then
|
||||
if new.subkingdom then
|
||||
allKingdoms = { new.kingdom, new.subkingdom }
|
||||
else
|
||||
allKingdoms = Fk:getKingdomMap(new.kingdom)
|
||||
end
|
||||
if #allKingdoms > 0 then
|
||||
kingdom = self:askForChoice(player, allKingdoms, "AskForKingdom", "#ChooseInitialKingdom")
|
||||
end
|
||||
kingdom = self:askForChoice(player, allKingdoms, "AskForKingdom", "#ChooseInitialKingdom")
|
||||
end
|
||||
|
||||
execGameEvent(GameEvent.ChangeProperty,
|
||||
|
@ -1372,10 +1379,10 @@ function Room:askForChooseCardsAndPlayers(player, minCardNum, maxCardNum, target
|
|||
|
||||
local data = {
|
||||
targets = targets,
|
||||
max_target_num = maxTargetNum,
|
||||
min_target_num = minTargetNum,
|
||||
max_card_num = maxCardNum,
|
||||
min_card_num = minCardNum,
|
||||
max_t_num = maxTargetNum,
|
||||
min_t_num = minTargetNum,
|
||||
max_c_num = maxCardNum,
|
||||
min_c_num = minCardNum,
|
||||
pattern = pattern,
|
||||
skillName = skillName,
|
||||
-- include_equip = includeEquip, -- FIXME: 预定一个破坏性更新
|
||||
|
@ -1501,6 +1508,10 @@ function Room:getNGenerals(n, position)
|
|||
end
|
||||
|
||||
if #generals < 1 then
|
||||
self:sendLog{
|
||||
type = "#NoGeneralDraw",
|
||||
toast = true,
|
||||
}
|
||||
self:gameOver("")
|
||||
end
|
||||
return generals
|
||||
|
@ -1594,24 +1605,25 @@ end
|
|||
function Room:askForChooseKingdom(players)
|
||||
players = players or self.alive_players
|
||||
local specialKingdomPlayers = table.filter(players, function(p)
|
||||
return p.kingdom == "god" or Fk.generals[p.general].subkingdom
|
||||
return Fk.generals[p.general].subkingdom or #Fk:getKingdomMap(p.kingdom) > 0
|
||||
end)
|
||||
|
||||
if #specialKingdomPlayers > 0 then
|
||||
local choiceMap = {}
|
||||
for _, p in ipairs(specialKingdomPlayers) do
|
||||
local allKingdoms = {}
|
||||
if p.kingdom == "god" then
|
||||
allKingdoms = table.filter({"wei", "shu", "wu", "qun", "jin"}, function(k) return table.contains(Fk.kingdoms, k) end)
|
||||
else
|
||||
local curGeneral = Fk.generals[p.general]
|
||||
local curGeneral = Fk.generals[p.general]
|
||||
if curGeneral.subkingdom then
|
||||
allKingdoms = { curGeneral.kingdom, curGeneral.subkingdom }
|
||||
else
|
||||
allKingdoms = Fk:getKingdomMap(p.kingdom)
|
||||
end
|
||||
if #allKingdoms > 0 then
|
||||
choiceMap[p.id] = allKingdoms
|
||||
|
||||
choiceMap[p.id] = allKingdoms
|
||||
|
||||
local data = json.encode({ allKingdoms, allKingdoms, "AskForKingdom", "#ChooseInitialKingdom" })
|
||||
p.request_data = data
|
||||
local data = json.encode({ allKingdoms, allKingdoms, "AskForKingdom", "#ChooseInitialKingdom" })
|
||||
p.request_data = data
|
||||
end
|
||||
end
|
||||
|
||||
self:notifyMoveFocus(players, "AskForKingdom")
|
||||
|
@ -1674,57 +1686,6 @@ function Room:askForCardChosen(chooser, target, flag, reason, prompt)
|
|||
return result
|
||||
end
|
||||
|
||||
--- 完全类似askForCardChosen,但是可以选择多张牌。
|
||||
--- 相应的,返回的是id的数组而不是单个id。
|
||||
---@param chooser ServerPlayer @ 要被询问的人
|
||||
---@param target ServerPlayer @ 被选牌的人
|
||||
---@param min integer @ 最小选牌数
|
||||
---@param max integer @ 最大选牌数
|
||||
---@param flag any @ 用"hej"三个字母的组合表示能选择哪些区域, h 手牌区, e - 装备区, j - 判定区
|
||||
---@param reason string @ 原因,一般是技能名
|
||||
---@param prompt? string @ 提示信息
|
||||
---@return integer[] @ 选择的id
|
||||
function Room:askForCardsChosen(chooser, target, min, max, flag, reason, prompt)
|
||||
if min == 1 and max == 1 then
|
||||
return { self:askForCardChosen(chooser, target, flag, reason) }
|
||||
end
|
||||
|
||||
local command = "AskForCardsChosen"
|
||||
prompt = prompt or ""
|
||||
self:notifyMoveFocus(chooser, command)
|
||||
local data = {target.id, min, max, flag, reason, prompt}
|
||||
local result = self:doRequest(chooser, command, json.encode(data))
|
||||
|
||||
local ret
|
||||
if result ~= "" then
|
||||
ret = json.decode(result)
|
||||
else
|
||||
local areas = {}
|
||||
local handcards
|
||||
if type(flag) == "string" then
|
||||
if string.find(flag, "h") then table.insert(areas, Player.Hand) end
|
||||
if string.find(flag, "e") then table.insert(areas, Player.Equip) end
|
||||
if string.find(flag, "j") then table.insert(areas, Player.Judge) end
|
||||
handcards = target:getCardIds(areas)
|
||||
else
|
||||
handcards = {}
|
||||
for _, t in ipairs(flag.card_data) do
|
||||
table.insertTable(handcards, t[2])
|
||||
end
|
||||
end
|
||||
if #handcards == 0 then return {} end
|
||||
ret = table.random(handcards, math.min(min, #handcards))
|
||||
end
|
||||
|
||||
local new_ret = table.filter(ret, function(id) return id ~= -1 end)
|
||||
local hand_num = #ret - #new_ret
|
||||
if hand_num > 0 then
|
||||
table.insertTable(new_ret, table.random(target:getCardIds(Player.Hand), hand_num))
|
||||
end
|
||||
|
||||
return new_ret
|
||||
end
|
||||
|
||||
--- 谋askForCardsChosen,需使用Fk:addPoxiMethod定义好方法
|
||||
---
|
||||
--- 选卡规则和返回值啥的全部自己想办法解决,data填入所有卡的列表(类似ui.card_data)
|
||||
|
@ -1756,6 +1717,103 @@ function Room:askForPoxi(player, poxi_type, data, extra_data, cancelable)
|
|||
end
|
||||
end
|
||||
|
||||
--- 完全类似askForCardChosen,但是可以选择多张牌。
|
||||
--- 相应的,返回的是id的数组而不是单个id。
|
||||
---@param chooser ServerPlayer @ 要被询问的人
|
||||
---@param target ServerPlayer @ 被选牌的人
|
||||
---@param min integer @ 最小选牌数
|
||||
---@param max integer @ 最大选牌数
|
||||
---@param flag any @ 用"hej"三个字母的组合表示能选择哪些区域, h 手牌区, e - 装备区, j - 判定区
|
||||
---可以通过flag.card_data = {{牌堆1名, 牌堆1ID表},...}来定制能选择的牌
|
||||
---@param reason string @ 原因,一般是技能名
|
||||
---@param prompt? string @ 提示信息
|
||||
---@return integer[] @ 选择的id
|
||||
function Room:askForCardsChosen(chooser, target, min, max, flag, reason, prompt)
|
||||
if min == 1 and max == 1 then
|
||||
return { self:askForCardChosen(chooser, target, flag, reason) }
|
||||
end
|
||||
|
||||
-- local command = "AskForCardsChosen"
|
||||
-- prompt = prompt or ""
|
||||
-- self:notifyMoveFocus(chooser, command)
|
||||
-- local data = {target.id, min, max, flag, reason, prompt}
|
||||
-- local result = self:doRequest(chooser, command, json.encode(data))
|
||||
|
||||
-- local ret
|
||||
-- if result ~= "" then
|
||||
-- ret = json.decode(result)
|
||||
-- else
|
||||
-- local areas = {}
|
||||
-- local handcards
|
||||
-- if type(flag) == "string" then
|
||||
-- if string.find(flag, "h") then table.insert(areas, Player.Hand) end
|
||||
-- if string.find(flag, "e") then table.insert(areas, Player.Equip) end
|
||||
-- if string.find(flag, "j") then table.insert(areas, Player.Judge) end
|
||||
-- handcards = target:getCardIds(areas)
|
||||
-- else
|
||||
-- handcards = {}
|
||||
-- for _, t in ipairs(flag.card_data) do
|
||||
-- table.insertTable(handcards, t[2])
|
||||
-- end
|
||||
-- end
|
||||
-- if #handcards == 0 then return {} end
|
||||
-- ret = table.random(handcards, math.min(min, #handcards))
|
||||
-- end
|
||||
|
||||
-- local new_ret = table.filter(ret, function(id) return id ~= -1 end)
|
||||
-- local hand_num = #ret - #new_ret
|
||||
-- if hand_num > 0 then
|
||||
-- table.insertTable(new_ret, table.random(target:getCardIds(Player.Hand), hand_num))
|
||||
-- end
|
||||
|
||||
-- return new_ret
|
||||
local areas = {}
|
||||
local cards
|
||||
local data = {
|
||||
to = target.id,
|
||||
min = min,
|
||||
max = max,
|
||||
skillName = reason,
|
||||
prompt = prompt,
|
||||
}
|
||||
if type(flag) == "string" then
|
||||
if string.find(flag, "h") then table.insert(areas, Player.Hand) end
|
||||
if string.find(flag, "e") then table.insert(areas, Player.Equip) end
|
||||
if string.find(flag, "j") then table.insert(areas, Player.Judge) end
|
||||
cards = target:getCardIds(areas)
|
||||
else
|
||||
cards = {}
|
||||
for _, t in ipairs(flag.card_data) do
|
||||
table.insertTable(cards, t[2])
|
||||
end
|
||||
end
|
||||
if #cards <= min then return table.random(cards, math.min(min, #cards)) end
|
||||
local cards_data = {}
|
||||
if type(flag) == "string" then
|
||||
if string.find(flag, "h") and #target:getCardIds(Player.Hand) > 0 then
|
||||
local handcards = {}
|
||||
for _, _ in ipairs(target:getCardIds(Player.Hand)) do
|
||||
table.insert(handcards, -1)
|
||||
end
|
||||
table.insert(cards_data, {"$Hand", handcards})
|
||||
end
|
||||
if string.find(flag, "e") and #target:getCardIds(Player.Equip) > 0 then table.insert(cards_data, {"$Equip", target:getCardIds(Player.Equip)}) end
|
||||
if string.find(flag, "j") and #target:getCardIds(Player.Judge) > 0 then table.insert(cards_data, {"$Judge", target:getCardIds(Player.Judge)}) end
|
||||
local ret = self:askForPoxi(chooser, "AskForCardsChosen", cards_data, data, false)
|
||||
local new_ret = table.filter(ret, function(id) return id ~= -1 end)
|
||||
local hidden_num = #ret - #new_ret
|
||||
if hidden_num > 0 then
|
||||
table.insertTable(new_ret, table.random(target:getCardIds(Player.Hand), hidden_num))
|
||||
end
|
||||
return new_ret
|
||||
else
|
||||
for _, t in ipairs(flag.card_data) do
|
||||
table.insert(cards_data, t)
|
||||
end
|
||||
end
|
||||
return self:askForPoxi(chooser, "AskForCardsChosen", cards_data, data, false)
|
||||
end
|
||||
|
||||
--- 询问一名玩家从众多选项中选择一个。
|
||||
---@param player ServerPlayer @ 要询问的玩家
|
||||
---@param choices string[] @ 可选选项列表
|
||||
|
@ -3027,7 +3085,7 @@ end
|
|||
function Room:obtainCard(player, cid, unhide, reason, proposer)
|
||||
if type(cid) ~= "number" then
|
||||
assert(cid and type(cid) == "table")
|
||||
if cid:isInstanceOf(Card) then
|
||||
if cid[1] == nil then
|
||||
cid = cid:isVirtual() and cid.subcards or {cid.id}
|
||||
end
|
||||
else
|
||||
|
|
|
@ -114,7 +114,7 @@ local analepticSkill = fk.CreateActiveSkill{
|
|||
end)
|
||||
end,
|
||||
can_use = function(self, player, card, extra_data)
|
||||
return ((extra_data and (extra_data.bypass_times or extra_data.analepticRecover)) or
|
||||
return not player:isProhibited(player, card) and ((extra_data and (extra_data.bypass_times or extra_data.analepticRecover)) or
|
||||
self:withinTimesLimit(player, Player.HistoryTurn, card, "analeptic", player))
|
||||
end,
|
||||
on_use = function(_, _, use)
|
||||
|
@ -333,9 +333,17 @@ local gudingSkill = fk.CreateTriggerSkill{
|
|||
frequency = Skill.Compulsory,
|
||||
events = {fk.DamageCaused},
|
||||
can_trigger = function(self, _, target, player, data)
|
||||
return target == player and player:hasSkill(self) and
|
||||
data.to:isKongcheng() and data.card and data.card.trueName == "slash" and
|
||||
not data.chain
|
||||
local logic = player.room.logic
|
||||
if target == player and player:hasSkill(self) and
|
||||
data.to:isKongcheng() and data.card and data.card.trueName == "slash" and not data.chain then
|
||||
local event = logic:getCurrentEvent()
|
||||
if event == nil then return false end
|
||||
event = event.parent
|
||||
if event == nil or event.event ~= GameEvent.SkillEffect then return false end
|
||||
event = event.parent
|
||||
if event == nil or event.event ~= GameEvent.CardEffect then return false end
|
||||
return data.card == event.data[1].card and data.from.id == event.data[1].from
|
||||
end
|
||||
end,
|
||||
on_use = function(_, _, _, _, data)
|
||||
data.damage = data.damage + 1
|
||||
|
|
|
@ -0,0 +1,22 @@
|
|||
-- SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
Fk:addPoxiMethod{
|
||||
name = "AskForCardsChosen",
|
||||
card_filter = function(to_select, selected, data, extra_data)
|
||||
return #selected < extra_data.max
|
||||
end,
|
||||
feasible = function(selected, data, extra_data)
|
||||
return #selected >= extra_data.min and #selected <= extra_data.max
|
||||
end,
|
||||
prompt = function(data, extra_data)
|
||||
if extra_data.prompt then
|
||||
return extra_data.prompt
|
||||
else
|
||||
local ret = Fk:translate("#AskForChooseCards")
|
||||
ret = ret:gsub("%%1", Fk:translate(extra_data.skillName or "AskForCardsChosen"))
|
||||
ret = ret:gsub("%%2", Fk:translate(extra_data.min))
|
||||
ret = ret:gsub("%%3", Fk:translate(extra_data.max))
|
||||
return ret .. ":" ..extra_data.to
|
||||
end
|
||||
end,
|
||||
}
|
|
@ -95,7 +95,7 @@ local choosePlayersSkill = fk.CreateActiveSkill{
|
|||
local exChooseSkill = fk.CreateActiveSkill{
|
||||
name = "ex__choose_skill",
|
||||
card_filter = function(self, to_select, selected)
|
||||
if #selected >= self.max_card_num then return false end
|
||||
if #selected >= self.max_c_num then return false end
|
||||
|
||||
if Fk:currentRoom():getCardArea(to_select) == Card.PlayerSpecial then
|
||||
if not string.find(self.pattern or "", self.expand_pile or "") then return false end
|
||||
|
@ -114,11 +114,15 @@ local exChooseSkill = fk.CreateActiveSkill{
|
|||
return checkpoint
|
||||
end,
|
||||
target_filter = function(self, to_select, selected, cards)
|
||||
if self.pattern ~= "" and #cards < self.min_card_num then return end
|
||||
if #selected < self.max_target_num then
|
||||
if #cards < self.min_c_num then return end
|
||||
if #selected < self.max_t_num then
|
||||
return table.contains(self.targets, to_select)
|
||||
end
|
||||
end,
|
||||
min_target_num = function(self) return self.min_t_num end,
|
||||
max_target_num = function(self) return self.max_t_num end,
|
||||
min_card_num = function(self) return self.min_c_num end,
|
||||
max_card_num = function(self) return self.max_c_num end,
|
||||
}
|
||||
|
||||
local maxCardsSkill = fk.CreateMaxCardsSkill{
|
||||
|
|
|
@ -4,6 +4,7 @@ local extension = Package:new("standard")
|
|||
extension.metadata = require "packages.standard.metadata"
|
||||
dofile "packages/standard/game_rule.lua"
|
||||
dofile "packages/standard/aux_skills.lua"
|
||||
dofile "packages/standard/aux_poxi.lua"
|
||||
|
||||
local jianxiong = fk.CreateTriggerSkill{
|
||||
name = "jianxiong",
|
||||
|
@ -699,29 +700,26 @@ local keji = fk.CreateTriggerSkill{
|
|||
can_trigger = function(self, event, target, player, data)
|
||||
if target == player and player:hasSkill(self) and data.to == Player.Discard then
|
||||
local room = player.room
|
||||
local logic = room.logic
|
||||
local e = logic:getCurrentEvent():findParent(GameEvent.Turn, true)
|
||||
if e == nil then return false end
|
||||
local end_id = e.id
|
||||
local events = logic.event_recorder[GameEvent.UseCard] or Util.DummyTable
|
||||
for i = #events, 1, -1 do
|
||||
e = events[i]
|
||||
if e.id <= end_id then break end
|
||||
local use = e.data[1]
|
||||
if use.from == player.id and use.card.trueName == "slash" then
|
||||
return false
|
||||
local play_ids = {}
|
||||
player.room.logic:getEventsOfScope(GameEvent.Phase, 1, function (e)
|
||||
if e.data[2] == Player.Play and e.end_id then
|
||||
table.insert(play_ids, {e.id, e.end_id})
|
||||
end
|
||||
end
|
||||
events = logic.event_recorder[GameEvent.RespondCard] or Util.DummyTable
|
||||
for i = #events, 1, -1 do
|
||||
e = events[i]
|
||||
if e.id <= end_id then break end
|
||||
local resp = e.data[1]
|
||||
if resp.from == player.id and resp.card.trueName == "slash" then
|
||||
return false
|
||||
return false
|
||||
end, Player.HistoryTurn)
|
||||
if #play_ids == 0 then return true end
|
||||
local function PlayCheck (e)
|
||||
local in_play = false
|
||||
for _, ids in ipairs(play_ids) do
|
||||
if e.id > ids[1] and e.id < ids[2] then
|
||||
in_play = true
|
||||
break
|
||||
end
|
||||
end
|
||||
return in_play and e.data[1].from == player.id and e.data[1].card.trueName == "slash"
|
||||
end
|
||||
return true
|
||||
return #player.room.logic:getEventsOfScope(GameEvent.UseCard, 1, PlayCheck, Player.HistoryTurn) == 0
|
||||
and #player.room.logic:getEventsOfScope(GameEvent.RespondCard, 1, PlayCheck, Player.HistoryTurn) == 0
|
||||
end
|
||||
end,
|
||||
on_use = function(self, event, target, player, data)
|
||||
|
@ -989,9 +987,10 @@ local qingnang = fk.CreateActiveSkill{
|
|||
on_use = function(self, room, effect)
|
||||
local from = room:getPlayerById(effect.from)
|
||||
room:throwCard(effect.cards, self.name, from, from)
|
||||
if from:isAlive() and from:isWounded() then
|
||||
local to = room:getPlayerById(effect.tos[1])
|
||||
if to:isAlive() and to:isWounded() then
|
||||
room:recover({
|
||||
who = room:getPlayerById(effect.tos[1]),
|
||||
who = to,
|
||||
num = 1,
|
||||
recoverBy = from,
|
||||
skillName = self.name
|
||||
|
@ -1117,12 +1116,20 @@ local role_getlogic = function()
|
|||
|
||||
if lord ~= nil then
|
||||
room.current = lord
|
||||
local a1 = #room.general_pile
|
||||
local a2 = #room.players * generalNum + lord_num
|
||||
if a1 < a2 then
|
||||
room:sendLog{
|
||||
type = "#NoEnoughGeneralDraw",
|
||||
arg = a1,
|
||||
arg2 = a2,
|
||||
toast = true,
|
||||
}
|
||||
room:gameOver("")
|
||||
end
|
||||
local generals = table.connect(room:findGenerals(function(g)
|
||||
return table.find(Fk.generals[g].skills, function(s) return s.lordSkill end)
|
||||
end, lord_num), room:getNGenerals(generalNum))
|
||||
if #room.general_pile < (#room.players - 1) * generalNum then
|
||||
room:gameOver("")
|
||||
end
|
||||
lord_generals = room:askForGeneral(lord, generals, n)
|
||||
local lord_general, deputy
|
||||
if type(lord_generals) == "table" then
|
||||
|
|
|
@ -1086,7 +1086,7 @@ local halberdSkill = fk.CreateTargetModSkill{
|
|||
if player:hasSkill(self) and skill.trueName == "slash_skill" then
|
||||
local cards = card:isVirtual() and card.subcards or {card.id}
|
||||
local handcards = player:getCardIds(Player.Hand)
|
||||
if #cards == #handcards and table.every(cards, function(id) return table.contains(handcards, id) end) then
|
||||
if #handcards > 0 and #cards == #handcards and table.every(cards, function(id) return table.contains(handcards, id) end) then
|
||||
return 2
|
||||
end
|
||||
end
|
||||
|
|
|
@ -83,6 +83,7 @@ local control = fk.CreateActiveSkill{
|
|||
-- p(room:askForYiji(from, from:getCardIds(Player.Hand), table.map(effect.tos, Util.Id2PlayerMapper), self.name, 2, 10, nil, false, nil, false, 3, true))
|
||||
for _, pid in ipairs(effect.tos) do
|
||||
local to = room:getPlayerById(pid)
|
||||
-- p(room:askForCardsChosen(from, to, 2, 3, "hej", self.name))
|
||||
-- p(room:askForPoxi(from, "test", {
|
||||
-- { "你自己", from:getCardIds "h" },
|
||||
-- { "对方", to:getCardIds "h" },
|
||||
|
|
Loading…
Reference in New Issue