mirror of
https://github.com/Qsgs-Fans/FreeKill.git
synced 2024-11-16 03:32:34 +08:00
Enhancement (#263)
- smart_ai搭了个壳子 - askForPoxi - 增加判断额外回合之法以及fix - 修trigger - 增加使用和打出的禁止技提示 - 修复卡牌标记,attach主动技显示为蓝色按钮
This commit is contained in:
parent
048071a6d5
commit
203736e38e
|
@ -139,7 +139,7 @@ Item {
|
|||
Text {
|
||||
width: parent.width
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
text: Backend.translate("Room List")
|
||||
text: Backend.translate("Room List").arg(roomModel.count)
|
||||
}
|
||||
ListView {
|
||||
id: roomList
|
||||
|
|
|
@ -59,7 +59,7 @@ Item {
|
|||
id: bgm
|
||||
source: config.bgmFile
|
||||
|
||||
// loops: MediaPlayer.Infinite
|
||||
loops: MediaPlayer.Infinite
|
||||
onPlaybackStateChanged: {
|
||||
if (playbackState == MediaPlayer.StoppedState && roomScene.isStarted)
|
||||
play();
|
||||
|
|
|
@ -1070,6 +1070,31 @@ callbacks["AskForCardsChosen"] = (jsonData) => {
|
|||
});
|
||||
}
|
||||
|
||||
callbacks["AskForPoxi"] = (jsonData) => {
|
||||
const { type, data } = JSON.parse(jsonData);
|
||||
|
||||
roomScene.state = "replying";
|
||||
roomScene.popupBox.sourceComponent = Qt.createComponent("../RoomElement/PoxiBox.qml");
|
||||
const box = roomScene.popupBox.item;
|
||||
box.poxi_type = type;
|
||||
box.card_data = data;
|
||||
for (let d of data) {
|
||||
const arr = [];
|
||||
const ids = d[1];
|
||||
|
||||
ids.forEach(id => {
|
||||
const card_data = JSON.parse(Backend.callLuaFunction("GetCardData", [id]));
|
||||
arr.push(card_data);
|
||||
});
|
||||
box.addCustomCards(d[0], arr);
|
||||
}
|
||||
|
||||
roomScene.popupBox.moveToCenter();
|
||||
box.cardsSelected.connect((ids) => {
|
||||
replyToServer(JSON.stringify(ids));
|
||||
});
|
||||
}
|
||||
|
||||
callbacks["AskForMoveCardInBoard"] = (jsonData) => {
|
||||
const data = JSON.parse(jsonData);
|
||||
const { cards, cardsPosition, generalNames, playerIds } = data;
|
||||
|
|
|
@ -62,7 +62,12 @@ Item {
|
|||
}
|
||||
|
||||
if (mark_name.startsWith('@$')) {
|
||||
params.cardNames = mark_extra.split(',');
|
||||
let data = mark_extra.split(',');
|
||||
if (!Object.is(parseInt(data[0]), NaN)) {
|
||||
params.ids = data.map(s => parseInt(s));
|
||||
} else {
|
||||
params.cardNames = data;
|
||||
}
|
||||
} else {
|
||||
let data = JSON.parse(Backend.callLuaFunction("GetPile", [root.parent.playerid, mark_name]));
|
||||
data = data.filter((e) => e !== -1);
|
||||
|
|
|
@ -31,11 +31,13 @@ Item {
|
|||
property string color: "" // only use when suit is empty
|
||||
property string footnote: "" // footnote, e.g. "A use card to B"
|
||||
property bool footnoteVisible: false
|
||||
property string prohibitReason: ""
|
||||
property bool known: true // if false it only show a card back
|
||||
property bool enabled: true // if false the card will be grey
|
||||
property alias card: cardItem
|
||||
property alias glow: glowItem
|
||||
property var mark: ({})
|
||||
property alias chosenInBox: chosen.visible
|
||||
|
||||
function getColor() {
|
||||
if (suit != "")
|
||||
|
@ -88,6 +90,7 @@ Item {
|
|||
visible: false
|
||||
}
|
||||
|
||||
|
||||
Image {
|
||||
id: cardItem
|
||||
source: known ? SkinBank.getCardPicture(cid || name)
|
||||
|
@ -217,6 +220,15 @@ Item {
|
|||
}
|
||||
}
|
||||
|
||||
Image {
|
||||
id: chosen
|
||||
visible: false
|
||||
source: SkinBank.CARD_DIR + "chosen"
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
y: 90
|
||||
scale: 1.25
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
visible: !root.selectable
|
||||
anchors.fill: parent
|
||||
|
@ -224,6 +236,24 @@ Item {
|
|||
opacity: 0.7
|
||||
}
|
||||
|
||||
Text {
|
||||
id: prohibitText
|
||||
visible: !root.selectable
|
||||
anchors.centerIn: parent
|
||||
font.family: fontLibian.name
|
||||
font.pixelSize: 18
|
||||
opacity: 0.9
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
lineHeight: 18
|
||||
lineHeightMode: Text.FixedHeight
|
||||
color: "snow"
|
||||
width: 20
|
||||
wrapMode: Text.WrapAnywhere
|
||||
style: Text.Outline
|
||||
styleColor: "red"
|
||||
text: prohibitReason
|
||||
}
|
||||
|
||||
TapHandler {
|
||||
acceptedButtons: Qt.LeftButton | Qt.RightButton | Qt.NoButton
|
||||
gesturePolicy: TapHandler.WithinBounds
|
||||
|
|
|
@ -166,17 +166,35 @@ RowLayout {
|
|||
const ids = [];
|
||||
let cards = handcardAreaItem.cards;
|
||||
for (let i = 0; i < cards.length; i++) {
|
||||
cards[i].prohibitReason = "";
|
||||
if (cardValid(cards[i].cid, cname)) {
|
||||
ids.push(cards[i].cid);
|
||||
} else {
|
||||
const prohibitReason = Backend.callLuaFunction(
|
||||
"GetCardProhibitReason",
|
||||
[cards[i].cid, roomScene.respond_play ? "response" : "use", cname]
|
||||
);
|
||||
if (prohibitReason) {
|
||||
cards[i].prohibitReason = prohibitReason;
|
||||
}
|
||||
}
|
||||
}
|
||||
cards = self.equipArea.getAllCards();
|
||||
cards.forEach(c => {
|
||||
c.prohibitReason = "";
|
||||
if (cardValid(c.cid, cname)) {
|
||||
ids.push(c.cid);
|
||||
if (!expanded_piles["_equip"]) {
|
||||
expandPile("_equip");
|
||||
}
|
||||
} else {
|
||||
const prohibitReason = Backend.callLuaFunction(
|
||||
"GetCardProhibitReason",
|
||||
[c.cid, roomScene.respond_play ? "response" : "use", cname]
|
||||
);
|
||||
if (prohibitReason) {
|
||||
c.prohibitReason = prohibitReason;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -202,6 +220,7 @@ RowLayout {
|
|||
|
||||
const ids = [], cards = handcardAreaItem.cards;
|
||||
for (let i = 0; i < cards.length; i++) {
|
||||
cards[i].prohibitReason = "";
|
||||
if (JSON.parse(Backend.callLuaFunction("CanUseCard", [cards[i].cid, Self.id]))) {
|
||||
ids.push(cards[i].cid);
|
||||
} else {
|
||||
|
@ -214,6 +233,14 @@ RowLayout {
|
|||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// still cannot use? show message on card
|
||||
if (!ids.includes(cards[i].cid)) {
|
||||
const prohibitReason = Backend.callLuaFunction("GetCardProhibitReason", [cards[i].cid, "play"]);
|
||||
if (prohibitReason) {
|
||||
cards[i].prohibitReason = prohibitReason;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
handcardAreaItem.enableCards(ids)
|
||||
|
@ -372,6 +399,11 @@ RowLayout {
|
|||
item.enabled = item.pressed;
|
||||
}
|
||||
|
||||
const cards = handcardAreaItem.cards;
|
||||
for (let i = 0; i < cards.length; i++) {
|
||||
cards[i].prohibitReason = "";
|
||||
}
|
||||
|
||||
updatePending();
|
||||
}
|
||||
|
||||
|
|
|
@ -48,6 +48,7 @@ Item {
|
|||
card.selectable = false;
|
||||
card.showDetail = false;
|
||||
card.selectedChanged.disconnect(adjustCards);
|
||||
card.prohibitReason = "";
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
|
|
@ -78,10 +78,10 @@ GraphicsBox {
|
|||
}
|
||||
onSelectedChanged: {
|
||||
if (selected) {
|
||||
virt_name = "$Selected";
|
||||
chosenInBox = true;
|
||||
root.selected_ids.push(cid);
|
||||
} else {
|
||||
virt_name = "";
|
||||
chosenInBox = false;
|
||||
root.selected_ids.splice(root.selected_ids.indexOf(cid), 1);
|
||||
}
|
||||
root.selected_ids = root.selected_ids;
|
||||
|
@ -122,38 +122,6 @@ GraphicsBox {
|
|||
return ret;
|
||||
}
|
||||
|
||||
function addHandcards(cards) {
|
||||
let handcards = findAreaModel('$Hand').areaCards;
|
||||
if (cards instanceof Array) {
|
||||
for (let i = 0; i < cards.length; i++)
|
||||
handcards.append(cards[i]);
|
||||
} else {
|
||||
handcards.append(cards);
|
||||
}
|
||||
}
|
||||
|
||||
function addEquips(cards)
|
||||
{
|
||||
let equips = findAreaModel('$Equip').areaCards;
|
||||
if (cards instanceof Array) {
|
||||
for (let i = 0; i < cards.length; i++)
|
||||
equips.append(cards[i]);
|
||||
} else {
|
||||
equips.append(cards);
|
||||
}
|
||||
}
|
||||
|
||||
function addDelayedTricks(cards)
|
||||
{
|
||||
let delayedTricks = findAreaModel('$Judge').areaCards;
|
||||
if (cards instanceof Array) {
|
||||
for (let i = 0; i < cards.length; i++)
|
||||
delayedTricks.append(cards[i]);
|
||||
} else {
|
||||
delayedTricks.append(cards);
|
||||
}
|
||||
}
|
||||
|
||||
function addCustomCards(name, cards) {
|
||||
let area = findAreaModel(name).areaCards;
|
||||
if (cards instanceof Array) {
|
||||
|
|
138
Fk/RoomElement/PoxiBox.qml
Normal file
138
Fk/RoomElement/PoxiBox.qml
Normal file
|
@ -0,0 +1,138 @@
|
|||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
import Fk.Pages
|
||||
|
||||
GraphicsBox {
|
||||
id: root
|
||||
|
||||
title.text: Backend.callLuaFunction("PoxiPrompt", [poxi_type, card_data])
|
||||
|
||||
// TODO: Adjust the UI design in case there are more than 7 cards
|
||||
width: 70 + 700
|
||||
height: 64 + Math.min(cardView.contentHeight, 400) + 20
|
||||
|
||||
signal cardSelected(int cid)
|
||||
signal cardsSelected(var ids)
|
||||
property var selected_ids: []
|
||||
property string poxi_type
|
||||
property var card_data
|
||||
|
||||
ListModel {
|
||||
id: cardModel
|
||||
}
|
||||
|
||||
ListView {
|
||||
id: cardView
|
||||
anchors.fill: parent
|
||||
anchors.topMargin: 40
|
||||
anchors.leftMargin: 20
|
||||
anchors.rightMargin: 20
|
||||
anchors.bottomMargin: 20
|
||||
spacing: 20
|
||||
model: cardModel
|
||||
clip: true
|
||||
|
||||
delegate: RowLayout {
|
||||
spacing: 15
|
||||
visible: areaCards.count > 0
|
||||
|
||||
Rectangle {
|
||||
border.color: "#A6967A"
|
||||
radius: 5
|
||||
color: "transparent"
|
||||
width: 18
|
||||
height: 130
|
||||
Layout.alignment: Qt.AlignTop
|
||||
|
||||
Text {
|
||||
color: "#E4D5A0"
|
||||
text: Backend.translate(areaName)
|
||||
anchors.fill: parent
|
||||
wrapMode: Text.WrapAnywhere
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
font.pixelSize: 15
|
||||
}
|
||||
}
|
||||
|
||||
GridLayout {
|
||||
columns: 7
|
||||
Repeater {
|
||||
model: areaCards
|
||||
|
||||
CardItem {
|
||||
cid: model.cid
|
||||
name: model.name || ""
|
||||
suit: model.suit || ""
|
||||
number: model.number || 0
|
||||
autoBack: false
|
||||
known: model.cid !== -1
|
||||
selectable: {
|
||||
return root.selected_ids.includes(model.cid) || JSON.parse(Backend.callLuaFunction(
|
||||
"PoxiFilter",
|
||||
[root.poxi_type, model.cid, root.selected_ids, root.card_data]
|
||||
));
|
||||
}
|
||||
onSelectedChanged: {
|
||||
if (selected) {
|
||||
chosenInBox = true;
|
||||
root.selected_ids.push(cid);
|
||||
} else {
|
||||
chosenInBox = false;
|
||||
root.selected_ids.splice(root.selected_ids.indexOf(cid), 1);
|
||||
}
|
||||
root.selected_ids = root.selected_ids;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
MetroButton {
|
||||
anchors.bottom: parent.bottom
|
||||
text: Backend.translate("OK")
|
||||
enabled: {
|
||||
return JSON.parse(Backend.callLuaFunction(
|
||||
"PoxiFeasible",
|
||||
[root.poxi_type, root.selected_ids, root.card_data]
|
||||
));
|
||||
}
|
||||
onClicked: root.cardsSelected(root.selected_ids)
|
||||
}
|
||||
|
||||
onCardSelected: finished();
|
||||
|
||||
function findAreaModel(name) {
|
||||
let ret;
|
||||
for (let i = 0; i < cardModel.count; i++) {
|
||||
let item = cardModel.get(i);
|
||||
if (item.areaName == name) {
|
||||
ret = item;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!ret) {
|
||||
ret = {
|
||||
areaName: name,
|
||||
areaCards: [],
|
||||
}
|
||||
cardModel.append(ret);
|
||||
ret = findAreaModel(name);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
function addCustomCards(name, cards) {
|
||||
let area = findAreaModel(name).areaCards;
|
||||
if (cards instanceof Array) {
|
||||
for (let i = 0; i < cards.length; i++)
|
||||
area.append(cards[i]);
|
||||
} else {
|
||||
area.append(cards);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -23,9 +23,17 @@ Item {
|
|||
x: -13 - 120 * 0.166
|
||||
y: -6 - 55 * 0.166
|
||||
scale: 0.66
|
||||
source: type === "notactive" ? ""
|
||||
: AppPath + "/image/button/skill/" + type + "/"
|
||||
+ (enabled ? (pressed ? "pressed" : "normal") : "disabled")
|
||||
source: {
|
||||
if (type === "notactive") {
|
||||
return "";
|
||||
}
|
||||
let ret = AppPath + "/image/button/skill/" + type + "/";
|
||||
let suffix = enabled ? (pressed ? "pressed" : "normal") : "disabled";
|
||||
if (enabled && type === "active" && orig.endsWith("&")) {
|
||||
suffix += "-attach";
|
||||
}
|
||||
return ret + suffix;
|
||||
}
|
||||
}
|
||||
|
||||
Image {
|
||||
|
|
BIN
image/button/skill/active/normal-attach.png
Normal file
BIN
image/button/skill/active/normal-attach.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 6.2 KiB |
BIN
image/button/skill/active/pressed-attach.png
Normal file
BIN
image/button/skill/active/pressed-attach.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 7.9 KiB |
BIN
image/card/chosen.png
Normal file
BIN
image/card/chosen.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 2.0 KiB |
|
@ -684,4 +684,52 @@ function SaveRecord()
|
|||
c.client:saveRecord(json.encode(c.record), c.record[2])
|
||||
end
|
||||
|
||||
function GetCardProhibitReason(cid, method, pattern)
|
||||
local card = Fk:getCardById(cid)
|
||||
if not card then return "" end
|
||||
if method == "play" and not card.skill:canUse(Self, card) then return "" end
|
||||
if method ~= "play" and not card:matchPattern(pattern) then return "" end
|
||||
if method == "play" then method = "use" end
|
||||
|
||||
local status_skills = Fk:currentRoom().status_skills[ProhibitSkill] or Util.DummyTable
|
||||
local s
|
||||
for _, skill in ipairs(status_skills) do
|
||||
local fn = method == "use" and skill.prohibitUse or skill.prohibitResponse
|
||||
if fn(skill, Self, card) then
|
||||
s = skill
|
||||
break
|
||||
end
|
||||
end
|
||||
if not s then return "" end
|
||||
|
||||
-- try to return a translated string
|
||||
local skillName = s.name
|
||||
local ret = Fk:translate(skillName)
|
||||
if ret ~= skillName then
|
||||
-- TODO: translate
|
||||
return ret .. "禁" .. (method == "use" and "使用" or "打出")
|
||||
elseif skillName:endsWith("_prohibit") and skillName:startsWith("#") then
|
||||
return Fk:translate(skillName:sub(2, -10)) .. "禁" .. (method == "use" and "使用" or "打出")
|
||||
end
|
||||
end
|
||||
|
||||
function PoxiPrompt(poxi_type, data)
|
||||
local poxi = Fk.poxi_methods[poxi_type]
|
||||
if not poxi or not poxi.prompt then return "" end
|
||||
if type(poxi.prompt) == "string" then return Fk:translate(poxi.prompt) end
|
||||
return poxi.prompt(data)
|
||||
end
|
||||
|
||||
function PoxiFilter(poxi_type, to_select, selected, data)
|
||||
local poxi = Fk.poxi_methods[poxi_type]
|
||||
if not poxi then return "false" end
|
||||
return json.encode(poxi.card_filter(to_select, selected, data))
|
||||
end
|
||||
|
||||
function PoxiFeasible(poxi_type, selected, data)
|
||||
local poxi = Fk.poxi_methods[poxi_type]
|
||||
if not poxi then return "false" end
|
||||
return json.encode(poxi.feasible(selected, data))
|
||||
end
|
||||
|
||||
dofile "lua/client/i18n/init.lua"
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
Fk:loadTranslationTable({
|
||||
-- Lobby
|
||||
-- ["Room List"] = "房间列表",
|
||||
["Room List"] = "Room List (currently have %1 rooms)",
|
||||
-- ["Enter"] = "进入",
|
||||
-- ["Observe"] = "旁观",
|
||||
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
Fk:loadTranslationTable{
|
||||
-- Lobby
|
||||
["Room List"] = "房间列表",
|
||||
["Room List"] = "房间列表 (共%1个房间)",
|
||||
["Enter"] = "进入",
|
||||
["Observe"] = "旁观",
|
||||
|
||||
|
|
|
@ -25,6 +25,7 @@
|
|||
---@field public filtered_cards table<integer, Card> @ 被锁视技影响的卡牌
|
||||
---@field public printed_cards table<integer, Card> @ 被某些房间现场打印的卡牌,id都是负数且从-2开始
|
||||
---@field private _custom_events any[] @ 自定义事件列表
|
||||
---@field public poxi_methods table<string, PoxiSpec> @ “魄袭”框操作方法表
|
||||
local Engine = class("Engine")
|
||||
|
||||
--- Engine的构造函数。
|
||||
|
@ -55,6 +56,7 @@ function Engine:initialize()
|
|||
self.game_mode_disabled = {}
|
||||
self.kingdoms = {}
|
||||
self._custom_events = {}
|
||||
self.poxi_methods = {}
|
||||
|
||||
self:loadPackages()
|
||||
self:loadDisabled()
|
||||
|
@ -334,6 +336,16 @@ function Engine:addGameEvent(name, pfunc, mfunc, cfunc, efunc)
|
|||
table.insert(self._custom_events, { name = name, p = pfunc, m = mfunc, c = cfunc, e = efunc })
|
||||
end
|
||||
|
||||
---@param spec PoxiSpec
|
||||
function Engine:addPoxiMethod(spec)
|
||||
assert(type(spec.name) == "string")
|
||||
assert(type(spec.card_filter) == "function")
|
||||
assert(type(spec.feasible) == "function")
|
||||
self.poxi_methods[spec.name] = spec
|
||||
spec.default_choice = spec.default_choice or function() return {} end
|
||||
spec.post_select = spec.post_select or function(s) return s end
|
||||
end
|
||||
|
||||
--- 从已经开启的拓展包中,随机选出若干名武将。
|
||||
---
|
||||
--- 对于同名武将不会重复选取。
|
||||
|
|
|
@ -588,3 +588,13 @@ function fk.CreateGameMode(spec)
|
|||
end
|
||||
return ret
|
||||
end
|
||||
|
||||
-- other
|
||||
|
||||
---@class PoxiSpec
|
||||
---@field name string
|
||||
---@field card_filter fun(to_select: int, selected: int[], data: any): bool
|
||||
---@field feasible fun(selected: int[], data: any): bool
|
||||
---@field post_select nil | fun(selected: int[], data: any): int[]
|
||||
---@field default_choice nil | fun(data: any): int[]
|
||||
---@field prompt nil | string | fun(data: any): string
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
|
||||
---@alias null nil
|
||||
---@alias bool boolean | nil
|
||||
---@alias int integer
|
||||
|
||||
---@class fk
|
||||
---FreeKill's lua API
|
||||
|
|
|
@ -3,3 +3,25 @@
|
|||
AI = require "server.ai.ai"
|
||||
TrustAI = require "server.ai.trust_ai"
|
||||
RandomAI = require "server.ai.random_ai"
|
||||
SmartAI = require "server.ai.smart_ai"
|
||||
|
||||
-- load ai module from packages
|
||||
local directories = FileIO.ls("packages")
|
||||
require "packages.standard.ai"
|
||||
require "packages.standard_cards.ai"
|
||||
require "packages.maneuvering.ai"
|
||||
table.removeOne(directories, "standard")
|
||||
table.removeOne(directories, "standard_cards")
|
||||
table.removeOne(directories, "maneuvering")
|
||||
|
||||
local _disable_packs = json.decode(fk.GetDisabledPacks())
|
||||
|
||||
for _, dir in ipairs(directories) do
|
||||
if (not string.find(dir, ".disabled")) and not table.contains(_disable_packs, dir)
|
||||
and FileIO.isDir("packages/" .. dir)
|
||||
and FileIO.exists("packages/" .. dir .. "/ai/init.lua") then
|
||||
|
||||
require(string.format("packages.%s.ai", dir))
|
||||
|
||||
end
|
||||
end
|
||||
|
|
10
lua/server/ai/smart_ai.lua
Normal file
10
lua/server/ai/smart_ai.lua
Normal file
|
@ -0,0 +1,10 @@
|
|||
-- SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
---@class SmartAI: AI
|
||||
local SmartAI = AI:subclass("RandomAI")
|
||||
|
||||
function SmartAI:initialize(player)
|
||||
AI.initialize(self, player)
|
||||
end
|
||||
|
||||
return SmartAI
|
|
@ -73,6 +73,13 @@ function GameEvent:addExitFunc(f)
|
|||
table.insert(self.extra_exit_funcs, f)
|
||||
end
|
||||
|
||||
function GameEvent:prependExitFunc(f)
|
||||
if self.extra_exit_funcs == Util.DummyTable then
|
||||
self.extra_exit_funcs = {}
|
||||
end
|
||||
table.insert(self.extra_exit_funcs, 1, f)
|
||||
end
|
||||
|
||||
function GameEvent:findParent(eventType, includeSelf)
|
||||
if includeSelf and self.event == eventType then return self end
|
||||
local e = self.parent
|
||||
|
|
|
@ -376,14 +376,14 @@ function GameLogic:trigger(event, target, data, refresh_only)
|
|||
end
|
||||
|
||||
repeat do
|
||||
local triggerables = table.filter(skills, function(skill)
|
||||
local invoked_skills = {}
|
||||
local filter_func = function(skill)
|
||||
return skill.priority_table[event] == prio and
|
||||
not table.contains(invoked_skills, skill) and
|
||||
skill:triggerable(event, target, player, data)
|
||||
end)
|
||||
end
|
||||
|
||||
local skill_names = table.map(triggerables, function(skill)
|
||||
return skill.name
|
||||
end)
|
||||
local skill_names = table.map(table.filter(skills, filter_func), Util.NameMapper)
|
||||
|
||||
while #skill_names > 0 do
|
||||
local skill_name = prio <= 0 and table.random(skill_names) or
|
||||
|
@ -392,23 +392,14 @@ function GameLogic:trigger(event, target, data, refresh_only)
|
|||
local skill = skill_name == "game_rule" and GameRule
|
||||
or Fk.skills[skill_name]
|
||||
|
||||
local len = #skills
|
||||
table.insert(invoked_skills, skill)
|
||||
broken = skill:trigger(event, target, player, data)
|
||||
|
||||
table.insertTable(
|
||||
skill_names,
|
||||
table.map(table.filter(table.slice(skills, len - #skills), function(s)
|
||||
return
|
||||
s.priority_table[event] == prio and
|
||||
s:triggerable(event, target, player, data)
|
||||
end), function(s) return s.name end)
|
||||
)
|
||||
skill_names = table.map(table.filter(skills, filter_func), Util.NameMapper)
|
||||
|
||||
broken = broken or (event == fk.AskForPeaches
|
||||
and room:getPlayerById(data.who).hp > 0)
|
||||
|
||||
if broken then break end
|
||||
table.removeOne(skill_names, skill_name)
|
||||
end
|
||||
|
||||
if broken then break end
|
||||
|
@ -428,6 +419,18 @@ function GameLogic:getCurrentEvent()
|
|||
return self.game_event_stack.t[self.game_event_stack.p]
|
||||
end
|
||||
|
||||
--- 如果当前事件刚好是技能生效事件,就返回那个技能名,否则返回空串。
|
||||
function GameLogic:getCurrentSkillName()
|
||||
local skillEvent = self:getCurrentEvent()
|
||||
local ret = ""
|
||||
if skillEvent.event == GameEvent.SkillEffect then
|
||||
local _, _, _skill = table.unpack(skillEvent.data)
|
||||
local skill = _skill.main_skill and _skill.main_skill or _skill
|
||||
ret = skill.name
|
||||
end
|
||||
return ret
|
||||
end
|
||||
|
||||
-- 在指定历史范围中找至多n个符合条件的事件
|
||||
---@param eventType integer @ 要查找的事件类型
|
||||
---@param n integer @ 最多找多少个
|
||||
|
|
|
@ -1457,6 +1457,33 @@ function Room:askForCardsChosen(chooser, target, min, max, flag, reason)
|
|||
return new_ret
|
||||
end
|
||||
|
||||
--- 谋askForCardsChosen,需使用Fk:addPoxiMethod定义好方法
|
||||
---
|
||||
--- 选卡规则和返回值啥的全部自己想办法解决,data填入所有卡的列表(类似ui.card_data)
|
||||
---
|
||||
--- 注意一定要返回一个表,毕竟本质上是选卡函数
|
||||
---@param player ServerPlayer
|
||||
---@param poxi_type string
|
||||
---@param data any
|
||||
---@return integer[]
|
||||
function Room:askForPoxi(player, poxi_type, data)
|
||||
local poxi = Fk.poxi_methods[poxi_type]
|
||||
if not poxi then return {} end
|
||||
|
||||
local command = "AskForPoxi"
|
||||
self:notifyMoveFocus(player, poxi_type)
|
||||
local result = self:doRequest(player, command, json.encode {
|
||||
type = poxi_type,
|
||||
data = data,
|
||||
})
|
||||
|
||||
if result == "" then
|
||||
return poxi.default_choice(data)
|
||||
else
|
||||
return poxi.post_select(json.decode(result), data)
|
||||
end
|
||||
end
|
||||
|
||||
--- 询问一名玩家从众多选项中选择一个。
|
||||
---@param player ServerPlayer @ 要询问的玩家
|
||||
---@param choices string[] @ 可选选项列表
|
||||
|
|
|
@ -469,7 +469,7 @@ function ServerPlayer:gainAnExtraPhase(phase, delay)
|
|||
local logic = room.logic
|
||||
local turn = logic:getCurrentEvent():findParent(GameEvent.Phase, true)
|
||||
if turn then
|
||||
turn:addExitFunc(function() self:gainAnExtraPhase(phase, false) end)
|
||||
turn:prependExitFunc(function() self:gainAnExtraPhase(phase, false) end)
|
||||
return
|
||||
end
|
||||
end
|
||||
|
@ -484,7 +484,6 @@ function ServerPlayer:gainAnExtraPhase(phase, delay)
|
|||
arg = phase_name_table[phase],
|
||||
}
|
||||
|
||||
|
||||
GameEvent(GameEvent.Phase, self, self.phase):exec()
|
||||
|
||||
self.phase = current
|
||||
|
@ -580,6 +579,7 @@ function ServerPlayer:skip(phase)
|
|||
end
|
||||
end
|
||||
|
||||
--- 当进行到出牌阶段空闲点时,结束出牌阶段。
|
||||
function ServerPlayer:endPlayPhase()
|
||||
self._play_phase_end = true
|
||||
-- TODO: send log
|
||||
|
@ -592,7 +592,7 @@ function ServerPlayer:gainAnExtraTurn(delay)
|
|||
local logic = room.logic
|
||||
local turn = logic:getCurrentEvent():findParent(GameEvent.Turn, true)
|
||||
if turn then
|
||||
turn:addExitFunc(function() self:gainAnExtraTurn(false) end)
|
||||
turn:prependExitFunc(function() self:gainAnExtraTurn(false) end)
|
||||
return
|
||||
end
|
||||
end
|
||||
|
@ -604,10 +604,32 @@ function ServerPlayer:gainAnExtraTurn(delay)
|
|||
|
||||
local current = room.current
|
||||
room.current = self
|
||||
|
||||
self.tag["_extra_turn_count"] = self.tag["_extra_turn_count"] or {}
|
||||
local ex_tag = self.tag["_extra_turn_count"]
|
||||
local skillName = room.logic:getCurrentSkillName()
|
||||
table.insert(ex_tag, skillName)
|
||||
|
||||
GameEvent(GameEvent.Turn, self):exec()
|
||||
|
||||
table.remove(ex_tag)
|
||||
|
||||
room.current = current
|
||||
end
|
||||
|
||||
function ServerPlayer:insideExtraTurn()
|
||||
return self.tag["_extra_turn_count"] and #self.tag["_extra_turn_count"] > 0
|
||||
end
|
||||
|
||||
---@return string
|
||||
function ServerPlayer:getCurrentExtraTurnReason()
|
||||
local ex_tag = self.tag["_extra_turn_count"]
|
||||
if (not ex_tag) or #ex_tag == 0 then
|
||||
return "game_rule"
|
||||
end
|
||||
return ex_tag[#ex_tag]
|
||||
end
|
||||
|
||||
function ServerPlayer:drawCards(num, skillName, fromPlace)
|
||||
return self.room:drawCards(self, num, skillName, fromPlace)
|
||||
end
|
||||
|
|
0
packages/maneuvering/ai/init.lua
Normal file
0
packages/maneuvering/ai/init.lua
Normal file
0
packages/standard/ai/init.lua
Normal file
0
packages/standard/ai/init.lua
Normal file
0
packages/standard_cards/ai/init.lua
Normal file
0
packages/standard_cards/ai/init.lua
Normal file
|
@ -90,6 +90,12 @@ local control = fk.CreateActiveSkill{
|
|||
-- room:swapSeat(from, to)
|
||||
for _, pid in ipairs(effect.tos) do
|
||||
local to = room:getPlayerById(pid)
|
||||
-- p(room:askForPoxi(from, "test", {
|
||||
-- { "你自己", from:getCardIds "h" },
|
||||
-- { "对方", to:getCardIds "h" },
|
||||
-- }))
|
||||
-- room:setPlayerMark(from, "@$a", {1,2,3})
|
||||
-- room:setPlayerMark(from, "@$b", {'slash','duel','axe'})
|
||||
if to:getMark("mouxushengcontrolled") == 0 then
|
||||
room:addPlayerMark(to, "mouxushengcontrolled")
|
||||
from:control(to)
|
||||
|
@ -124,6 +130,22 @@ local control = fk.CreateActiveSkill{
|
|||
-- room:useVirtualCard("slash", nil, from, room:getOtherPlayers(from), self.name, true)
|
||||
end,
|
||||
}
|
||||
--[[
|
||||
Fk:addPoxiMethod{
|
||||
name = "test",
|
||||
card_filter = function(to_select, selected, data)
|
||||
local s = Fk:getCardById(to_select).suit
|
||||
for _, id in ipairs(selected) do
|
||||
if Fk:getCardById(id).suit == s then return false end
|
||||
end
|
||||
return true
|
||||
end,
|
||||
feasible = function(selected, data)
|
||||
return #selected == 0 or #selected == 4
|
||||
end,
|
||||
prompt = "魄袭:选你们俩手牌总共四个花色,或者不选直接按确定按钮"
|
||||
}
|
||||
--]]
|
||||
local test_vs = fk.CreateViewAsSkill{
|
||||
name = "test_vs",
|
||||
pattern = "nullification",
|
||||
|
|
Loading…
Reference in New Issue
Block a user