commit
044d81b622
|
@ -139,7 +139,7 @@ Item {
|
||||||
Text {
|
Text {
|
||||||
width: parent.width
|
width: parent.width
|
||||||
horizontalAlignment: Text.AlignHCenter
|
horizontalAlignment: Text.AlignHCenter
|
||||||
text: Backend.translate("Room List").arg(roomModel.count)
|
text: Backend.translate("Room List")
|
||||||
}
|
}
|
||||||
ListView {
|
ListView {
|
||||||
id: roomList
|
id: roomList
|
||||||
|
|
|
@ -59,7 +59,7 @@ Item {
|
||||||
id: bgm
|
id: bgm
|
||||||
source: config.bgmFile
|
source: config.bgmFile
|
||||||
|
|
||||||
loops: MediaPlayer.Infinite
|
// loops: MediaPlayer.Infinite
|
||||||
onPlaybackStateChanged: {
|
onPlaybackStateChanged: {
|
||||||
if (playbackState == MediaPlayer.StoppedState && roomScene.isStarted)
|
if (playbackState == MediaPlayer.StoppedState && roomScene.isStarted)
|
||||||
play();
|
play();
|
||||||
|
|
|
@ -1070,31 +1070,6 @@ 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) => {
|
callbacks["AskForMoveCardInBoard"] = (jsonData) => {
|
||||||
const data = JSON.parse(jsonData);
|
const data = JSON.parse(jsonData);
|
||||||
const { cards, cardsPosition, generalNames, playerIds } = data;
|
const { cards, cardsPosition, generalNames, playerIds } = data;
|
||||||
|
|
|
@ -62,12 +62,7 @@ Item {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mark_name.startsWith('@$')) {
|
if (mark_name.startsWith('@$')) {
|
||||||
let data = mark_extra.split(',');
|
params.cardNames = mark_extra.split(',');
|
||||||
if (!Object.is(parseInt(data[0]), NaN)) {
|
|
||||||
params.ids = data.map(s => parseInt(s));
|
|
||||||
} else {
|
|
||||||
params.cardNames = data;
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
let data = JSON.parse(Backend.callLuaFunction("GetPile", [root.parent.playerid, mark_name]));
|
let data = JSON.parse(Backend.callLuaFunction("GetPile", [root.parent.playerid, mark_name]));
|
||||||
data = data.filter((e) => e !== -1);
|
data = data.filter((e) => e !== -1);
|
||||||
|
|
|
@ -31,13 +31,11 @@ Item {
|
||||||
property string color: "" // only use when suit is empty
|
property string color: "" // only use when suit is empty
|
||||||
property string footnote: "" // footnote, e.g. "A use card to B"
|
property string footnote: "" // footnote, e.g. "A use card to B"
|
||||||
property bool footnoteVisible: false
|
property bool footnoteVisible: false
|
||||||
property string prohibitReason: ""
|
|
||||||
property bool known: true // if false it only show a card back
|
property bool known: true // if false it only show a card back
|
||||||
property bool enabled: true // if false the card will be grey
|
property bool enabled: true // if false the card will be grey
|
||||||
property alias card: cardItem
|
property alias card: cardItem
|
||||||
property alias glow: glowItem
|
property alias glow: glowItem
|
||||||
property var mark: ({})
|
property var mark: ({})
|
||||||
property alias chosenInBox: chosen.visible
|
|
||||||
|
|
||||||
function getColor() {
|
function getColor() {
|
||||||
if (suit != "")
|
if (suit != "")
|
||||||
|
@ -90,7 +88,6 @@ Item {
|
||||||
visible: false
|
visible: false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
Image {
|
Image {
|
||||||
id: cardItem
|
id: cardItem
|
||||||
source: known ? SkinBank.getCardPicture(cid || name)
|
source: known ? SkinBank.getCardPicture(cid || name)
|
||||||
|
@ -220,15 +217,6 @@ Item {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Image {
|
|
||||||
id: chosen
|
|
||||||
visible: false
|
|
||||||
source: SkinBank.CARD_DIR + "chosen"
|
|
||||||
anchors.horizontalCenter: parent.horizontalCenter
|
|
||||||
y: 90
|
|
||||||
scale: 1.25
|
|
||||||
}
|
|
||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
visible: !root.selectable
|
visible: !root.selectable
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
|
@ -236,24 +224,6 @@ Item {
|
||||||
opacity: 0.7
|
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 {
|
TapHandler {
|
||||||
acceptedButtons: Qt.LeftButton | Qt.RightButton | Qt.NoButton
|
acceptedButtons: Qt.LeftButton | Qt.RightButton | Qt.NoButton
|
||||||
gesturePolicy: TapHandler.WithinBounds
|
gesturePolicy: TapHandler.WithinBounds
|
||||||
|
|
|
@ -166,35 +166,17 @@ RowLayout {
|
||||||
const ids = [];
|
const ids = [];
|
||||||
let cards = handcardAreaItem.cards;
|
let cards = handcardAreaItem.cards;
|
||||||
for (let i = 0; i < cards.length; i++) {
|
for (let i = 0; i < cards.length; i++) {
|
||||||
cards[i].prohibitReason = "";
|
|
||||||
if (cardValid(cards[i].cid, cname)) {
|
if (cardValid(cards[i].cid, cname)) {
|
||||||
ids.push(cards[i].cid);
|
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 = self.equipArea.getAllCards();
|
||||||
cards.forEach(c => {
|
cards.forEach(c => {
|
||||||
c.prohibitReason = "";
|
|
||||||
if (cardValid(c.cid, cname)) {
|
if (cardValid(c.cid, cname)) {
|
||||||
ids.push(c.cid);
|
ids.push(c.cid);
|
||||||
if (!expanded_piles["_equip"]) {
|
if (!expanded_piles["_equip"]) {
|
||||||
expandPile("_equip");
|
expandPile("_equip");
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
const prohibitReason = Backend.callLuaFunction(
|
|
||||||
"GetCardProhibitReason",
|
|
||||||
[c.cid, roomScene.respond_play ? "response" : "use", cname]
|
|
||||||
);
|
|
||||||
if (prohibitReason) {
|
|
||||||
c.prohibitReason = prohibitReason;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -220,7 +202,6 @@ RowLayout {
|
||||||
|
|
||||||
const ids = [], cards = handcardAreaItem.cards;
|
const ids = [], cards = handcardAreaItem.cards;
|
||||||
for (let i = 0; i < cards.length; i++) {
|
for (let i = 0; i < cards.length; i++) {
|
||||||
cards[i].prohibitReason = "";
|
|
||||||
if (JSON.parse(Backend.callLuaFunction("CanUseCard", [cards[i].cid, Self.id]))) {
|
if (JSON.parse(Backend.callLuaFunction("CanUseCard", [cards[i].cid, Self.id]))) {
|
||||||
ids.push(cards[i].cid);
|
ids.push(cards[i].cid);
|
||||||
} else {
|
} else {
|
||||||
|
@ -233,14 +214,6 @@ RowLayout {
|
||||||
break;
|
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)
|
handcardAreaItem.enableCards(ids)
|
||||||
|
@ -399,11 +372,6 @@ RowLayout {
|
||||||
item.enabled = item.pressed;
|
item.enabled = item.pressed;
|
||||||
}
|
}
|
||||||
|
|
||||||
const cards = handcardAreaItem.cards;
|
|
||||||
for (let i = 0; i < cards.length; i++) {
|
|
||||||
cards[i].prohibitReason = "";
|
|
||||||
}
|
|
||||||
|
|
||||||
updatePending();
|
updatePending();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -48,7 +48,6 @@ Item {
|
||||||
card.selectable = false;
|
card.selectable = false;
|
||||||
card.showDetail = false;
|
card.showDetail = false;
|
||||||
card.selectedChanged.disconnect(adjustCards);
|
card.selectedChanged.disconnect(adjustCards);
|
||||||
card.prohibitReason = "";
|
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
|
@ -78,10 +78,10 @@ GraphicsBox {
|
||||||
}
|
}
|
||||||
onSelectedChanged: {
|
onSelectedChanged: {
|
||||||
if (selected) {
|
if (selected) {
|
||||||
chosenInBox = true;
|
virt_name = "$Selected";
|
||||||
root.selected_ids.push(cid);
|
root.selected_ids.push(cid);
|
||||||
} else {
|
} else {
|
||||||
chosenInBox = false;
|
virt_name = "";
|
||||||
root.selected_ids.splice(root.selected_ids.indexOf(cid), 1);
|
root.selected_ids.splice(root.selected_ids.indexOf(cid), 1);
|
||||||
}
|
}
|
||||||
root.selected_ids = root.selected_ids;
|
root.selected_ids = root.selected_ids;
|
||||||
|
@ -122,6 +122,38 @@ GraphicsBox {
|
||||||
return ret;
|
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) {
|
function addCustomCards(name, cards) {
|
||||||
let area = findAreaModel(name).areaCards;
|
let area = findAreaModel(name).areaCards;
|
||||||
if (cards instanceof Array) {
|
if (cards instanceof Array) {
|
||||||
|
|
|
@ -23,17 +23,9 @@ Item {
|
||||||
x: -13 - 120 * 0.166
|
x: -13 - 120 * 0.166
|
||||||
y: -6 - 55 * 0.166
|
y: -6 - 55 * 0.166
|
||||||
scale: 0.66
|
scale: 0.66
|
||||||
source: {
|
source: type === "notactive" ? ""
|
||||||
if (type === "notactive") {
|
: AppPath + "/image/button/skill/" + type + "/"
|
||||||
return "";
|
+ (enabled ? (pressed ? "pressed" : "normal") : "disabled")
|
||||||
}
|
|
||||||
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 {
|
Image {
|
||||||
|
|
|
@ -63,9 +63,3 @@ ___
|
||||||
## 许可证
|
## 许可证
|
||||||
|
|
||||||
本仓库使用GPLv3作为许可证。详见`LICENSE`文件。
|
本仓库使用GPLv3作为许可证。详见`LICENSE`文件。
|
||||||
|
|
||||||
___
|
|
||||||
|
|
||||||
## 点一下小星星呗!
|
|
||||||
|
|
||||||
[![Star History Chart](https://api.star-history.com/svg?repos=Qsgs-Fans/FreeKill&type=Date)](https://star-history.com/#Qsgs-Fans/FreeKill&Date)
|
|
||||||
|
|
|
@ -0,0 +1,14 @@
|
||||||
|
{
|
||||||
|
"editor.renderLineHighlight": "none",
|
||||||
|
"Lua.diagnostics.disable": [
|
||||||
|
"undefined-field",
|
||||||
|
"inject-field",
|
||||||
|
"return-type-mismatch",
|
||||||
|
"cast-local-type",
|
||||||
|
"param-type-mismatch",
|
||||||
|
"invisible",
|
||||||
|
"missing-fields",
|
||||||
|
"assign-type-mismatch",
|
||||||
|
"undefined-doc-name"
|
||||||
|
]
|
||||||
|
}
|
File diff suppressed because it is too large
Load Diff
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
Fk:loadTranslationTable({
|
Fk:loadTranslationTable({
|
||||||
-- Lobby
|
-- Lobby
|
||||||
["Room List"] = "Room List (currently have %1 rooms)",
|
-- ["Room List"] = "房间列表",
|
||||||
-- ["Enter"] = "进入",
|
-- ["Enter"] = "进入",
|
||||||
-- ["Observe"] = "旁观",
|
-- ["Observe"] = "旁观",
|
||||||
|
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
Fk:loadTranslationTable{
|
Fk:loadTranslationTable{
|
||||||
-- Lobby
|
-- Lobby
|
||||||
["Room List"] = "房间列表 (共%1个房间)",
|
["Room List"] = "房间列表",
|
||||||
["Enter"] = "进入",
|
["Enter"] = "进入",
|
||||||
["Observe"] = "旁观",
|
["Observe"] = "旁观",
|
||||||
|
|
||||||
|
|
|
@ -25,7 +25,6 @@
|
||||||
---@field public filtered_cards table<integer, Card> @ 被锁视技影响的卡牌
|
---@field public filtered_cards table<integer, Card> @ 被锁视技影响的卡牌
|
||||||
---@field public printed_cards table<integer, Card> @ 被某些房间现场打印的卡牌,id都是负数且从-2开始
|
---@field public printed_cards table<integer, Card> @ 被某些房间现场打印的卡牌,id都是负数且从-2开始
|
||||||
---@field private _custom_events any[] @ 自定义事件列表
|
---@field private _custom_events any[] @ 自定义事件列表
|
||||||
---@field public poxi_methods table<string, PoxiSpec> @ “魄袭”框操作方法表
|
|
||||||
local Engine = class("Engine")
|
local Engine = class("Engine")
|
||||||
|
|
||||||
--- Engine的构造函数。
|
--- Engine的构造函数。
|
||||||
|
@ -56,7 +55,6 @@ function Engine:initialize()
|
||||||
self.game_mode_disabled = {}
|
self.game_mode_disabled = {}
|
||||||
self.kingdoms = {}
|
self.kingdoms = {}
|
||||||
self._custom_events = {}
|
self._custom_events = {}
|
||||||
self.poxi_methods = {}
|
|
||||||
|
|
||||||
self:loadPackages()
|
self:loadPackages()
|
||||||
self:loadDisabled()
|
self:loadDisabled()
|
||||||
|
@ -336,16 +334,6 @@ function Engine:addGameEvent(name, pfunc, mfunc, cfunc, efunc)
|
||||||
table.insert(self._custom_events, { name = name, p = pfunc, m = mfunc, c = cfunc, e = efunc })
|
table.insert(self._custom_events, { name = name, p = pfunc, m = mfunc, c = cfunc, e = efunc })
|
||||||
end
|
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
|
|
||||||
|
|
||||||
--- 从已经开启的拓展包中,随机选出若干名武将。
|
--- 从已经开启的拓展包中,随机选出若干名武将。
|
||||||
---
|
---
|
||||||
--- 对于同名武将不会重复选取。
|
--- 对于同名武将不会重复选取。
|
||||||
|
|
|
@ -136,8 +136,10 @@ function Player:setGeneral(general, setHp, addSkills)
|
||||||
end
|
end
|
||||||
|
|
||||||
function Player:getGeneralMaxHp()
|
function Player:getGeneralMaxHp()
|
||||||
local general = Fk.generals[type(self:getMark("__heg_general")) == "string" and self:getMark("__heg_general") or self.general]
|
local general = Fk.generals
|
||||||
local deputy = Fk.generals[type(self:getMark("__heg_deputy")) == "string" and self:getMark("__heg_deputy") or self.deputyGeneral]
|
[type(self:getMark("__heg_general")) == "string" and self:getMark("__heg_general") or self.general]
|
||||||
|
local deputy = Fk.generals
|
||||||
|
[type(self:getMark("__heg_deputy")) == "string" and self:getMark("__heg_deputy") or self.deputyGeneral]
|
||||||
|
|
||||||
if not deputy then
|
if not deputy then
|
||||||
return general.maxHp + general.mainMaxHpAdjustedValue
|
return general.maxHp + general.mainMaxHpAdjustedValue
|
||||||
|
@ -267,7 +269,7 @@ function Player:removeCards(playerArea, cardIds, specialName)
|
||||||
|
|
||||||
if table.contains(fromAreaIds, id) then
|
if table.contains(fromAreaIds, id) then
|
||||||
table.removeOne(fromAreaIds, id)
|
table.removeOne(fromAreaIds, id)
|
||||||
-- FIXME: 为客户端移动id为-1的牌考虑,但总感觉有地方需要商讨啊!
|
-- FIXME: 为客户端移动id为-1的牌考虑,但总感觉有地方需要商讨啊!
|
||||||
elseif table.every(fromAreaIds, function(e) return e == -1 end) then
|
elseif table.every(fromAreaIds, function(e) return e == -1 end) then
|
||||||
table.remove(fromAreaIds, 1)
|
table.remove(fromAreaIds, 1)
|
||||||
elseif id == -1 then
|
elseif id == -1 then
|
||||||
|
@ -329,12 +331,18 @@ end
|
||||||
function Player:getCardIds(playerAreas, specialName)
|
function Player:getCardIds(playerAreas, specialName)
|
||||||
local rightAreas = { Player.Hand, Player.Equip, Player.Judge }
|
local rightAreas = { Player.Hand, Player.Equip, Player.Judge }
|
||||||
playerAreas = playerAreas or rightAreas
|
playerAreas = playerAreas or rightAreas
|
||||||
|
local cardIds = {}
|
||||||
if type(playerAreas) == "string" then
|
if type(playerAreas) == "string" then
|
||||||
local str = playerAreas
|
local str = playerAreas
|
||||||
playerAreas = {}
|
playerAreas = {}
|
||||||
if str:find("h") then
|
if str:find("h") then
|
||||||
table.insert(playerAreas, Player.Hand)
|
table.insert(playerAreas, Player.Hand)
|
||||||
end
|
end
|
||||||
|
if str:find("&") then
|
||||||
|
for k, v in pairs(self.special_cards) do
|
||||||
|
if k:endsWith("&") then table.insertTable(cardIds, v) end
|
||||||
|
end
|
||||||
|
end
|
||||||
if str:find("e") then
|
if str:find("e") then
|
||||||
table.insert(playerAreas, Player.Equip)
|
table.insert(playerAreas, Player.Equip)
|
||||||
end
|
end
|
||||||
|
@ -346,7 +354,6 @@ function Player:getCardIds(playerAreas, specialName)
|
||||||
local areas = type(playerAreas) == "table" and playerAreas or { playerAreas }
|
local areas = type(playerAreas) == "table" and playerAreas or { playerAreas }
|
||||||
|
|
||||||
local rightAreas = { Player.Hand, Player.Equip, Player.Judge, Player.Special }
|
local rightAreas = { Player.Hand, Player.Equip, Player.Judge, Player.Special }
|
||||||
local cardIds = {}
|
|
||||||
for _, area in ipairs(areas) do
|
for _, area in ipairs(areas) do
|
||||||
assert(table.contains(rightAreas, area))
|
assert(table.contains(rightAreas, area))
|
||||||
assert(area ~= Player.Special or type(specialName) == "string")
|
assert(area ~= Player.Special or type(specialName) == "string")
|
||||||
|
@ -510,7 +517,8 @@ function Player:distanceTo(other, mode, ignore_dead)
|
||||||
if temp ~= other then
|
if temp ~= other then
|
||||||
print("Distance malfunction: start and end does not matched.")
|
print("Distance malfunction: start and end does not matched.")
|
||||||
end
|
end
|
||||||
local left = #(ignore_dead and Fk:currentRoom().players or Fk:currentRoom().alive_players) - right - #table.filter(Fk:currentRoom().alive_players, function(p) return p:isRemoved() end)
|
local left = #(ignore_dead and Fk:currentRoom().players or Fk:currentRoom().alive_players) - right -
|
||||||
|
#table.filter(Fk:currentRoom().alive_players, function(p) return p:isRemoved() end)
|
||||||
local ret = 0
|
local ret = 0
|
||||||
if mode == "left" then
|
if mode == "left" then
|
||||||
ret = left
|
ret = left
|
||||||
|
@ -586,7 +594,7 @@ function Player:addCardUseHistory(cardName, num)
|
||||||
num = num or 1
|
num = num or 1
|
||||||
assert(type(num) == "number" and num ~= 0)
|
assert(type(num) == "number" and num ~= 0)
|
||||||
|
|
||||||
self.cardUsedHistory[cardName] = self.cardUsedHistory[cardName] or {0, 0, 0, 0}
|
self.cardUsedHistory[cardName] = self.cardUsedHistory[cardName] or { 0, 0, 0, 0 }
|
||||||
local t = self.cardUsedHistory[cardName]
|
local t = self.cardUsedHistory[cardName]
|
||||||
for i, _ in ipairs(t) do
|
for i, _ in ipairs(t) do
|
||||||
t[i] = t[i] + num
|
t[i] = t[i] + num
|
||||||
|
@ -623,7 +631,7 @@ function Player:addSkillUseHistory(skill_name, num)
|
||||||
num = num or 1
|
num = num or 1
|
||||||
assert(type(num) == "number" and num ~= 0)
|
assert(type(num) == "number" and num ~= 0)
|
||||||
|
|
||||||
self.skillUsedHistory[skill_name] = self.skillUsedHistory[skill_name] or {0, 0, 0, 0}
|
self.skillUsedHistory[skill_name] = self.skillUsedHistory[skill_name] or { 0, 0, 0, 0 }
|
||||||
local t = self.skillUsedHistory[skill_name]
|
local t = self.skillUsedHistory[skill_name]
|
||||||
for i, _ in ipairs(t) do
|
for i, _ in ipairs(t) do
|
||||||
t[i] = t[i] + num
|
t[i] = t[i] + num
|
||||||
|
@ -648,7 +656,7 @@ function Player:setSkillUseHistory(skill_name, num, scope)
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
self.skillUsedHistory[skill_name] = self.skillUsedHistory[skill_name] or {0, 0, 0, 0}
|
self.skillUsedHistory[skill_name] = self.skillUsedHistory[skill_name] or { 0, 0, 0, 0 }
|
||||||
self.skillUsedHistory[skill_name][scope] = num
|
self.skillUsedHistory[skill_name][scope] = num
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -681,7 +689,7 @@ end
|
||||||
|
|
||||||
--- 获取玩家是否无手牌及装备牌。
|
--- 获取玩家是否无手牌及装备牌。
|
||||||
function Player:isNude()
|
function Player:isNude()
|
||||||
return #self:getCardIds{Player.Hand, Player.Equip} == 0
|
return #self:getCardIds { Player.Hand, Player.Equip } == 0
|
||||||
end
|
end
|
||||||
|
|
||||||
--- 获取玩家所有区域是否无牌。
|
--- 获取玩家所有区域是否无牌。
|
||||||
|
@ -729,9 +737,8 @@ function Player:hasSkill(skill, ignoreNullified, ignoreAlive)
|
||||||
end
|
end
|
||||||
|
|
||||||
if self:isInstanceOf(ServerPlayer) and -- isInstanceOf(nil) will return false
|
if self:isInstanceOf(ServerPlayer) and -- isInstanceOf(nil) will return false
|
||||||
table.contains(self._fake_skills, skill) and
|
table.contains(self._fake_skills, skill) and
|
||||||
table.contains(self.prelighted_skills, skill) then
|
table.contains(self.prelighted_skills, skill) then
|
||||||
|
|
||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -751,7 +758,7 @@ end
|
||||||
function Player:addSkill(skill, source_skill)
|
function Player:addSkill(skill, source_skill)
|
||||||
skill = getActualSkill(skill)
|
skill = getActualSkill(skill)
|
||||||
|
|
||||||
local toget = {table.unpack(skill.related_skills)}
|
local toget = { table.unpack(skill.related_skills) }
|
||||||
table.insert(toget, skill)
|
table.insert(toget, skill)
|
||||||
|
|
||||||
local room = Fk:currentRoom()
|
local room = Fk:currentRoom()
|
||||||
|
@ -812,7 +819,7 @@ function Player:loseSkill(skill, source_skill)
|
||||||
table.insert(tolose, skill)
|
table.insert(tolose, skill)
|
||||||
self.derivative_skills[skill] = nil
|
self.derivative_skills[skill] = nil
|
||||||
|
|
||||||
local ret = {} ---@type Skill[]
|
local ret = {} ---@type Skill[]
|
||||||
for _, s in ipairs(tolose) do
|
for _, s in ipairs(tolose) do
|
||||||
if not self:hasSkill(s, true, true) then
|
if not self:hasSkill(s, true, true) then
|
||||||
table.insert(ret, s)
|
table.insert(ret, s)
|
||||||
|
@ -824,7 +831,7 @@ end
|
||||||
--- 获取对应玩家所有技能。
|
--- 获取对应玩家所有技能。
|
||||||
-- return all skills that xxx:hasSkill() == true
|
-- return all skills that xxx:hasSkill() == true
|
||||||
function Player:getAllSkills()
|
function Player:getAllSkills()
|
||||||
local ret = {table.unpack(self.player_skills)}
|
local ret = { table.unpack(self.player_skills) }
|
||||||
for _, t in pairs(self.derivative_skills) do
|
for _, t in pairs(self.derivative_skills) do
|
||||||
for _, s in ipairs(t) do
|
for _, s in ipairs(t) do
|
||||||
table.insertIfNeed(ret, s)
|
table.insertIfNeed(ret, s)
|
||||||
|
@ -844,8 +851,7 @@ end
|
||||||
---@param to Player @ 特定玩家
|
---@param to Player @ 特定玩家
|
||||||
---@param card Card @ 特定牌
|
---@param card Card @ 特定牌
|
||||||
function Player:isProhibited(to, card)
|
function Player:isProhibited(to, card)
|
||||||
local r = Fk:currentRoom()
|
if type(card) == "number" then card = Fk:getCardById(card) end
|
||||||
|
|
||||||
if card.type == Card.TypeEquip and #to:getAvailableEquipSlots(card.sub_type) == 0 then
|
if card.type == Card.TypeEquip and #to:getAvailableEquipSlots(card.sub_type) == 0 then
|
||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
|
@ -855,7 +861,12 @@ function Player:isProhibited(to, card)
|
||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
|
|
||||||
local status_skills = r.status_skills[ProhibitSkill] or Util.DummyTable
|
if fk.useMustTargets and
|
||||||
|
not table.contains(fk.useMustTargets, to.id) then
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
|
||||||
|
local status_skills = Fk:currentRoom().status_skills[ProhibitSkill] or Util.DummyTable
|
||||||
for _, skill in ipairs(status_skills) do
|
for _, skill in ipairs(status_skills) do
|
||||||
if skill:isProhibited(self, to, card) then
|
if skill:isProhibited(self, to, card) then
|
||||||
return true
|
return true
|
||||||
|
@ -907,8 +918,8 @@ function Player:prohibitReveal(isDeputy)
|
||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
for _, m in ipairs(table.map(MarkEnum.TempMarkSuffix, function(s)
|
for _, m in ipairs(table.map(MarkEnum.TempMarkSuffix, function(s)
|
||||||
return self:getMark(MarkEnum.RevealProhibited .. s)
|
return self:getMark(MarkEnum.RevealProhibited .. s)
|
||||||
end)) do
|
end)) do
|
||||||
if type(m) == "table" and table.contains(m, place) then
|
if type(m) == "table" and table.contains(m, place) then
|
||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
|
@ -928,9 +939,11 @@ fk.SwitchYin = 1
|
||||||
---@return number|string @ 转换技状态
|
---@return number|string @ 转换技状态
|
||||||
function Player:getSwitchSkillState(skillName, afterUse, inWord)
|
function Player:getSwitchSkillState(skillName, afterUse, inWord)
|
||||||
if afterUse then
|
if afterUse then
|
||||||
return self:getMark(MarkEnum.SwithSkillPreName .. skillName) < 1 and (inWord and "yin" or fk.SwitchYin) or (inWord and "yang" or fk.SwitchYang)
|
return self:getMark(MarkEnum.SwithSkillPreName .. skillName) < 1 and (inWord and "yin" or fk.SwitchYin) or
|
||||||
|
(inWord and "yang" or fk.SwitchYang)
|
||||||
else
|
else
|
||||||
return self:getMark(MarkEnum.SwithSkillPreName .. skillName) < 1 and (inWord and "yang" or fk.SwitchYang) or (inWord and "yin" or fk.SwitchYin)
|
return self:getMark(MarkEnum.SwithSkillPreName .. skillName) < 1 and (inWord and "yang" or fk.SwitchYang) or
|
||||||
|
(inWord and "yin" or fk.SwitchYin)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -946,12 +959,12 @@ function Player:canMoveCardInBoardTo(to, id)
|
||||||
return to:hasEmptyEquipSlot(card.sub_type)
|
return to:hasEmptyEquipSlot(card.sub_type)
|
||||||
else
|
else
|
||||||
return
|
return
|
||||||
not (
|
not (
|
||||||
table.find(to:getCardIds(Player.Judge), function(cardId)
|
table.find(to:getCardIds(Player.Judge), function(cardId)
|
||||||
return Fk:getCardById(cardId).name == card.name
|
return Fk:getCardById(cardId).name == card.name
|
||||||
end) or
|
end) or
|
||||||
table.contains(to.sealedSlots, Player.JudgeSlot)
|
table.contains(to.sealedSlots, Player.JudgeSlot)
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -588,13 +588,3 @@ function fk.CreateGameMode(spec)
|
||||||
end
|
end
|
||||||
return ret
|
return ret
|
||||||
end
|
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,7 +7,6 @@
|
||||||
|
|
||||||
---@alias null nil
|
---@alias null nil
|
||||||
---@alias bool boolean | nil
|
---@alias bool boolean | nil
|
||||||
---@alias int integer
|
|
||||||
|
|
||||||
---@class fk
|
---@class fk
|
||||||
---FreeKill's lua API
|
---FreeKill's lua API
|
||||||
|
|
|
@ -0,0 +1,45 @@
|
||||||
|
拓展包ai文件写法:
|
||||||
|
按照包文件夹名+_ai,例如standard_cards是standard_cards_ai.lua,直接放在init.lua同位置下。
|
||||||
|
|
||||||
|
分辨敌我:
|
||||||
|
设置反值内值,同时设置技能和牌的身份值;例如杀的值为100,主公或跳身份的忠被杀时,来源反值+100,当一名角色反值大于50时,将识别为反贼;当跳反者杀反贼(或跳忠者杀主忠)时,他内值就+100,内值大于50的将被识别为内奸;当然,标记身份是以最高值开始标记身份,例如场上两名角色为400和300的最高反值,但是反贼只剩下一个,那么就只将400的标记为反贼,其余身份也是如此。优先针对虚弱,相同虚弱再优先仇恨。
|
||||||
|
|
||||||
|
要让身份值加给角色,就需要在使用技能或牌时接入ai接口,所以我在触发时机函数的最后加了接口,用于获取此时触发时机的数据,例如1号位杀2号位,此时触发了指定目标时机,就可以通过接口接入ai,然后让来源反值+100。但是N神不想这样增加接口,但我还不了解怎么通过其他方式同步获取使用技能或牌的数据......
|
||||||
|
|
||||||
|
空闲点ai出牌:
|
||||||
|
先定义阶段技或卡牌的优先度,然后获取角色所有可用的阶段技和卡牌,按照优先度进行排序,再进行for列表逐一检测是否可使用,可使用则检测是否有使用函数,有则执行使用函数;在使用函数里进行定义self.use_id和给self.use_tos添加角色id;然后系统检测有self.use_id则输出使用(如果是使用卡则self.use_id=卡id,阶段技是self.use_id=子卡表{}),同时可以给self.use_tos添加角色id做为牌或技能的目标,会同步输出。
|
||||||
|
|
||||||
|
请求ai使用牌:
|
||||||
|
包含请求无懈,请求桃,借刀请求杀等
|
||||||
|
请求无懈有正无懈和反无懈,系统根据生效锦囊牌名定位请求代码(之后可以增加来源和目标技能名来修正是否使用,例如要通过伤害锦囊卖血时,可以通过技能名阻止友方使用无懈),同时请求代码会带有正反无懈的参数,根据这个参数进行分类讨论,请求代码需定义self.use_id为将要使用的无懈id,当然也可以定义self.use_id=true,这样就会根据卡牌优先度选择第一个无懈使用。因为要区分正反无懈,我在room里的请求无懈时增加了fk.askNullification和fk.askNullificationData,前者用来区分正反无懈,后者记录要无懈的锦囊数据,因为直接获取使用数据,可能会是上个无懈的数据而不是最初锦囊的数据,当然我并不知道其他的识别正反无懈和获取源锦囊生效数据的方法,只能是手动增加记录的这个样子来实现.....
|
||||||
|
|
||||||
|
请求桃默认给友军使用,但是可以通过来源和目标的技能来修正来源是否要使用桃
|
||||||
|
|
||||||
|
剩下的请求牌根据提示信息名进行决策,兜底决策是套用空闲点出牌代码,所以说请求使用牌和空闲点使用牌的的函数是相通的,如果请求使用牌没有定义函数,就会调用空闲点使用牌的函数来兜底。
|
||||||
|
|
||||||
|
技能转化牌:
|
||||||
|
急救桃、倾国闪等
|
||||||
|
也是通过技能名定位转化代码,也需要定义self.use_id为技能子卡表{}就行,如果没有子卡就定义空表{},如果不定义就表示不使用转化技。
|
||||||
|
|
||||||
|
请求ai打出牌:
|
||||||
|
杀响应决斗南蛮等
|
||||||
|
根据牌名或技能名来分别决策,同时优先以技能名决策(用于卖血技),兜底决策是默认不响应,因为有部分是技能请求响应,例如鬼才改判打出。响应牌依旧是将要响应的卡的id定义为self.use_id(懒得再定义个新参数)
|
||||||
|
|
||||||
|
请求ai弃置牌:
|
||||||
|
给每个牌名定义保留值,然后排序并优先弃置低保留值的牌。
|
||||||
|
|
||||||
|
请求ai发动技能:
|
||||||
|
直接按照技能名分别决策。
|
||||||
|
技能请求选择角色是给self.use_tos添加目标id,例如突袭
|
||||||
|
既请求选择角色又请求选择牌就再增加定义self.use_id为技能子卡表{}
|
||||||
|
|
||||||
|
请求ai选择角色区域牌:
|
||||||
|
按照提示信息名分别决策,同时设置兜底决策,对敌军优先选择其重要的牌。
|
||||||
|
依旧是定义self.use_id为技能子卡表{},不论选择一张牌还是多张牌,都是将牌id添加到表然后定义给self.use_id。
|
||||||
|
|
||||||
|
请求ai选择选项:
|
||||||
|
按照提示信息名分别决策,同时兜底决策是随机选择。
|
||||||
|
这个就直接返回需要选择的选项就行。
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -4,24 +4,3 @@ AI = require "server.ai.ai"
|
||||||
TrustAI = require "server.ai.trust_ai"
|
TrustAI = require "server.ai.trust_ai"
|
||||||
RandomAI = require "server.ai.random_ai"
|
RandomAI = require "server.ai.random_ai"
|
||||||
SmartAI = require "server.ai.smart_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
|
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
-- SPDX-License-Identifier: GPL-3.0-or-later
|
-- SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
---@class RandomAI: AI
|
---@class RandomAI: AI
|
||||||
local RandomAI = AI:subclass("RandomAI")
|
local RandomAI = AI:subclass("RandomAI")
|
||||||
|
|
||||||
|
@ -7,234 +6,273 @@ local RandomAI = AI:subclass("RandomAI")
|
||||||
---@param skill ActiveSkill
|
---@param skill ActiveSkill
|
||||||
---@param card Card | nil
|
---@param card Card | nil
|
||||||
local function useActiveSkill(self, skill, card)
|
local function useActiveSkill(self, skill, card)
|
||||||
local room = self.room
|
local room = self.room
|
||||||
local player = self.player
|
local player = self.player
|
||||||
|
|
||||||
local filter_func = skill.cardFilter
|
local filter_func = skill.cardFilter
|
||||||
if card then
|
if card then
|
||||||
filter_func = function() return false end
|
filter_func = function()
|
||||||
end
|
return false
|
||||||
|
|
||||||
if self.command == "PlayCard" and (not skill:canUse(player, card) or player:prohibitUse(card)) then
|
|
||||||
return ""
|
|
||||||
end
|
|
||||||
|
|
||||||
local max_try_times = 100
|
|
||||||
local selected_targets = {}
|
|
||||||
local selected_cards = {}
|
|
||||||
local min = skill:getMinTargetNum()
|
|
||||||
local max = skill:getMaxTargetNum(player, card)
|
|
||||||
local min_card = skill:getMinCardNum()
|
|
||||||
local max_card = skill:getMaxCardNum()
|
|
||||||
for _ = 0, max_try_times do
|
|
||||||
if skill:feasible(selected_targets, selected_cards, self.player, card) then break end
|
|
||||||
local avail_targets = table.filter(self.room:getAlivePlayers(), function(p)
|
|
||||||
local ret = skill:targetFilter(p.id, selected_targets, selected_cards, card or Fk:cloneCard'zixing')
|
|
||||||
if ret and card then
|
|
||||||
if player:prohibitUse(card) then
|
|
||||||
ret = false
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
return ret
|
|
||||||
end)
|
|
||||||
avail_targets = table.map(avail_targets, function(p) return p.id end)
|
|
||||||
local avail_cards = table.filter(player:getCardIds{ Player.Hand, Player.Equip }, function(id)
|
|
||||||
return filter_func(skill, id, selected_cards, selected_targets)
|
|
||||||
end)
|
|
||||||
|
|
||||||
if #avail_targets == 0 and #avail_cards == 0 then break end
|
if self.command == "PlayCard" and (not skill:canUse(player, card) or player:prohibitUse(card)) then
|
||||||
table.insertIfNeed(selected_targets, table.random(avail_targets))
|
return ""
|
||||||
table.insertIfNeed(selected_cards, table.random(avail_cards))
|
end
|
||||||
end
|
|
||||||
if skill:feasible(selected_targets, selected_cards, self.player, card) then
|
local max_try_times = 100
|
||||||
local ret = json.encode{
|
local selected_targets = {}
|
||||||
card = card and card.id or json.encode{
|
local selected_cards = {}
|
||||||
skill = skill.name,
|
local min = skill:getMinTargetNum()
|
||||||
subcards = selected_cards,
|
local max = skill:getMaxTargetNum(player, card)
|
||||||
},
|
local min_card = skill:getMinCardNum()
|
||||||
targets = selected_targets,
|
local max_card = skill:getMaxCardNum()
|
||||||
}
|
for _ = 0, max_try_times do
|
||||||
-- print(ret)
|
if skill:feasible(selected_targets, selected_cards, self.player, card) then
|
||||||
return ret
|
break
|
||||||
end
|
end
|
||||||
return ""
|
local avail_targets = table.filter(self.room:getAlivePlayers(), function(p)
|
||||||
|
local ret = skill:targetFilter(p.id, selected_targets, selected_cards, card or Fk:cloneCard 'zixing')
|
||||||
|
if ret and card then
|
||||||
|
if player:prohibitUse(card) then
|
||||||
|
ret = false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return ret
|
||||||
|
end)
|
||||||
|
avail_targets = table.map(avail_targets, function(p)
|
||||||
|
return p.id
|
||||||
|
end)
|
||||||
|
local avail_cards = table.filter(player:getCardIds{Player.Hand, Player.Equip}, function(id)
|
||||||
|
return filter_func(skill, id, selected_cards, selected_targets)
|
||||||
|
end)
|
||||||
|
|
||||||
|
if #avail_targets == 0 and #avail_cards == 0 then
|
||||||
|
break
|
||||||
|
end
|
||||||
|
table.insertIfNeed(selected_targets, table.random(avail_targets))
|
||||||
|
table.insertIfNeed(selected_cards, table.random(avail_cards))
|
||||||
|
end
|
||||||
|
if skill:feasible(selected_targets, selected_cards, self.player, card) then
|
||||||
|
local ret = json.encode {
|
||||||
|
card = card and card.id or json.encode {
|
||||||
|
skill = skill.name,
|
||||||
|
subcards = selected_cards
|
||||||
|
},
|
||||||
|
targets = selected_targets
|
||||||
|
}
|
||||||
|
-- print(ret)
|
||||||
|
return ret
|
||||||
|
end
|
||||||
|
return ""
|
||||||
end
|
end
|
||||||
|
|
||||||
---@param self RandomAI
|
---@param self RandomAI
|
||||||
---@param skill ViewAsSkill
|
---@param skill ViewAsSkill
|
||||||
local function useVSSkill(self, skill, pattern, cancelable, extra_data)
|
local function useVSSkill(self, skill, pattern, cancelable, extra_data)
|
||||||
local player = self.player
|
local player = self.player
|
||||||
local room = self.room
|
local room = self.room
|
||||||
local precondition
|
local precondition
|
||||||
|
|
||||||
if self.command == "PlayCard" then
|
if self.command == "PlayCard" then
|
||||||
precondition = skill:enabledAtPlay(player)
|
precondition = skill:enabledAtPlay(player)
|
||||||
if not precondition then return nil end
|
if not precondition then
|
||||||
local exp = Exppattern:Parse(skill.pattern)
|
return nil
|
||||||
local cnames = {}
|
end
|
||||||
for _, m in ipairs(exp.matchers) do
|
local exp = Exppattern:Parse(skill.pattern)
|
||||||
if m.name then table.insertTable(cnames, m.name) end
|
local cnames = {}
|
||||||
|
for _, m in ipairs(exp.matchers) do
|
||||||
|
if m.name then
|
||||||
|
table.insertTable(cnames, m.name)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
for _, n in ipairs(cnames) do
|
||||||
|
local c = Fk:cloneCard(n)
|
||||||
|
precondition = c.skill:canUse(Self, c)
|
||||||
|
if precondition then
|
||||||
|
break
|
||||||
|
end
|
||||||
|
end
|
||||||
|
else
|
||||||
|
precondition = skill:enabledAtResponse(player)
|
||||||
|
if not precondition then
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
local exp = Exppattern:Parse(pattern)
|
||||||
|
precondition = exp:matchExp(skill.pattern)
|
||||||
end
|
end
|
||||||
for _, n in ipairs(cnames) do
|
|
||||||
local c = Fk:cloneCard(n)
|
if (not precondition) or math.random() < 0.2 then
|
||||||
precondition = c.skill:canUse(Self, c)
|
return nil
|
||||||
if precondition then break end
|
|
||||||
end
|
end
|
||||||
else
|
|
||||||
precondition = skill:enabledAtResponse(player)
|
|
||||||
if not precondition then return nil end
|
|
||||||
local exp = Exppattern:Parse(pattern)
|
|
||||||
precondition = exp:matchExp(skill.pattern)
|
|
||||||
end
|
|
||||||
|
|
||||||
if (not precondition) or math.random() < 0.2 then return nil end
|
local selected_cards = {}
|
||||||
|
local max_try_time = 100
|
||||||
|
|
||||||
local selected_cards = {}
|
for _ = 0, max_try_time do
|
||||||
local max_try_time = 100
|
local avail_cards = table.filter(player:getCardIds{Player.Hand, Player.Equip}, function(id)
|
||||||
|
return skill:cardFilter(id, selected_cards)
|
||||||
for _ = 0, max_try_time do
|
end)
|
||||||
local avail_cards = table.filter(player:getCardIds{ Player.Hand, Player.Equip }, function(id)
|
if #avail_cards == 0 then
|
||||||
return skill:cardFilter(id, selected_cards)
|
break
|
||||||
end)
|
end
|
||||||
if #avail_cards == 0 then break end
|
table.insert(selected_cards, table.random(avail_cards))
|
||||||
table.insert(selected_cards, table.random(avail_cards))
|
if skill:viewAs(selected_cards) then
|
||||||
if skill:viewAs(selected_cards) then
|
return {
|
||||||
return {
|
skill = skill.name,
|
||||||
skill = skill.name,
|
subcards = selected_cards
|
||||||
subcards = selected_cards,
|
}
|
||||||
}
|
end
|
||||||
end
|
end
|
||||||
end
|
return nil
|
||||||
return nil
|
|
||||||
end
|
end
|
||||||
|
|
||||||
local random_cb = {}
|
local random_cb = {}
|
||||||
|
|
||||||
random_cb.AskForUseActiveSkill = function(self, jsonData)
|
random_cb.AskForUseActiveSkill = function(self, jsonData)
|
||||||
local data = json.decode(jsonData)
|
local data = json.decode(jsonData)
|
||||||
local skill = Fk.skills[data[1]]
|
local skill = Fk.skills[data[1]]
|
||||||
local cancelable = data[3]
|
local cancelable = data[3]
|
||||||
if cancelable and math.random() < 0.25 then return "" end
|
if cancelable and math.random() < 0.25 then
|
||||||
local extra_data = json.decode(data[4])
|
return ""
|
||||||
for k, v in pairs(extra_data) do
|
end
|
||||||
skill[k] = v
|
local extra_data = json.decode(data[4])
|
||||||
end
|
for k, v in pairs(extra_data) do
|
||||||
return useActiveSkill(self, skill)
|
skill[k] = v
|
||||||
|
end
|
||||||
|
return useActiveSkill(self, skill)
|
||||||
end
|
end
|
||||||
|
|
||||||
random_cb.AskForSkillInvoke = function(self, jsonData)
|
random_cb.AskForSkillInvoke = function(self, jsonData)
|
||||||
return table.random{"1", ""}
|
return table.random {"1", ""}
|
||||||
end
|
end
|
||||||
|
|
||||||
random_cb.AskForUseCard = function(self, jsonData)
|
random_cb.AskForUseCard = function(self, jsonData)
|
||||||
local player = self.player
|
local player = self.player
|
||||||
local data = json.decode(jsonData)
|
local data = json.decode(jsonData)
|
||||||
local card_name = data[1]
|
local card_name = data[1]
|
||||||
local pattern = data[2] or card_name
|
local pattern = data[2] or card_name
|
||||||
local cancelable = data[4] or true
|
local cancelable = data[4] or true
|
||||||
local exp = Exppattern:Parse(pattern)
|
local exp = Exppattern:Parse(pattern)
|
||||||
|
|
||||||
local avail_cards = table.filter(player:getCardIds("he"), function(id)
|
local avail_cards = table.filter(player:getCardIds("he"), function(id)
|
||||||
return exp:match(Fk:getCardById(id)) and not player:prohibitUse(Fk:getCardById(id))
|
return exp:match(Fk:getCardById(id)) and not player:prohibitUse(Fk:getCardById(id))
|
||||||
end)
|
end)
|
||||||
if #avail_cards > 0 then
|
if #avail_cards > 0 then
|
||||||
if math.random() < 0.25 then return "" end
|
if math.random() < 0.25 then
|
||||||
for _, card in ipairs(avail_cards) do
|
return ""
|
||||||
local skill = Fk:getCardById(card).skill
|
end
|
||||||
local max_try_times = 100
|
avail_cards = table.map(avail_cards, function(id)
|
||||||
local selected_targets = {}
|
return Fk:getCardById(id)
|
||||||
local min = skill:getMinTargetNum()
|
|
||||||
local max = skill:getMaxTargetNum(player, card)
|
|
||||||
local min_card = skill:getMinCardNum()
|
|
||||||
local max_card = skill:getMaxCardNum()
|
|
||||||
for _ = 0, max_try_times do
|
|
||||||
if skill:feasible(selected_targets, {card}, self.player, card) then break end
|
|
||||||
local avail_targets = table.filter(self.room:getAlivePlayers(), function(p)
|
|
||||||
local ret = skill:targetFilter(p.id, selected_targets, {card}, card or Fk:cloneCard'zixing')
|
|
||||||
return ret
|
|
||||||
end)
|
end)
|
||||||
avail_targets = table.map(avail_targets, function(p) return p.id end)
|
for _, card in ipairs(avail_cards) do
|
||||||
|
local skill = card.skill
|
||||||
|
local max_try_times = 100
|
||||||
|
local selected_targets = {}
|
||||||
|
local min = skill:getMinTargetNum()
|
||||||
|
local max = skill:getMaxTargetNum(player, card)
|
||||||
|
local min_card = skill:getMinCardNum()
|
||||||
|
local max_card = skill:getMaxCardNum()
|
||||||
|
for _ = 0, max_try_times do
|
||||||
|
if skill:feasible(selected_targets, {card.id}, self.player, card) then
|
||||||
|
break
|
||||||
|
end
|
||||||
|
local avail_targets = table.filter(self.room:getAlivePlayers(), function(p)
|
||||||
|
local ret = skill:targetFilter(p.id, selected_targets, {card}, card or Fk:cloneCard 'zixing')
|
||||||
|
return ret
|
||||||
|
end)
|
||||||
|
avail_targets = table.map(avail_targets, function(p)
|
||||||
|
return p.id
|
||||||
|
end)
|
||||||
|
|
||||||
if #avail_targets == 0 and #avail_cards == 0 then break end
|
if #avail_targets + #avail_cards < 1 then
|
||||||
table.insertIfNeed(selected_targets, table.random(avail_targets))
|
break
|
||||||
table.insertIfNeed({card}, table.random(avail_cards))
|
end
|
||||||
end
|
table.insertIfNeed(selected_targets, table.random(avail_targets))
|
||||||
if skill:feasible(selected_targets, {card}, self.player, card) then
|
end
|
||||||
return json.encode{
|
if skill:feasible(selected_targets, {card.id}, self.player, card) then
|
||||||
card = table.random(avail_cards),
|
return json.encode {
|
||||||
targets = selected_targets,
|
card = table.random(avail_cards),
|
||||||
}
|
targets = selected_targets
|
||||||
end
|
}
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
return ""
|
||||||
return ""
|
|
||||||
end
|
end
|
||||||
|
|
||||||
---@param self RandomAI
|
---@param self RandomAI
|
||||||
random_cb.AskForResponseCard = function(self, jsonData)
|
random_cb.AskForResponseCard = function(self, jsonData)
|
||||||
local data = json.decode(jsonData)
|
local data = json.decode(jsonData)
|
||||||
local pattern = data[2]
|
local pattern = data[2]
|
||||||
local cancelable = true
|
local cancelable = true
|
||||||
local exp = Exppattern:Parse(pattern)
|
local exp = Exppattern:Parse(pattern)
|
||||||
local avail_cards = table.filter(self.player:getCardIds{ Player.Hand, Player.Equip }, function(id)
|
local avail_cards = table.filter(self.player:getCardIds{Player.Hand, Player.Equip}, function(id)
|
||||||
return exp:match(Fk:getCardById(id))
|
return exp:match(Fk:getCardById(id))
|
||||||
end)
|
end)
|
||||||
if #avail_cards > 0 then return json.encode{
|
if #avail_cards > 0 then
|
||||||
card = table.random(avail_cards),
|
return json.encode {
|
||||||
targets = {},
|
card = table.random(avail_cards),
|
||||||
} end
|
targets = {}
|
||||||
-- TODO: vs skill
|
}
|
||||||
return ""
|
end
|
||||||
|
-- TODO: vs skill
|
||||||
|
return ""
|
||||||
end
|
end
|
||||||
|
|
||||||
---@param self RandomAI
|
---@param self RandomAI
|
||||||
random_cb.PlayCard = function(self, jsonData)
|
random_cb.PlayCard = function(self, jsonData)
|
||||||
local cards = table.map(self.player:getCardIds(Player.Hand),
|
local cards = table.map(self.player:getCardIds(Player.Hand), function(id)
|
||||||
function(id) return Fk:getCardById(id) end)
|
return Fk:getCardById(id)
|
||||||
local actives = table.filter(self.player:getAllSkills(), function(s)
|
end)
|
||||||
return s:isInstanceOf(ActiveSkill)
|
local actives = table.filter(self.player:getAllSkills(), function(s)
|
||||||
end)
|
return s:isInstanceOf(ActiveSkill)
|
||||||
local vss = table.filter(self.player:getAllSkills(), function(s)
|
end)
|
||||||
return s:isInstanceOf(ViewAsSkill)
|
local vss = table.filter(self.player:getAllSkills(), function(s)
|
||||||
end)
|
return s:isInstanceOf(ViewAsSkill)
|
||||||
table.insertTable(cards, actives)
|
end)
|
||||||
table.insertTable(cards, vss)
|
table.insertTable(cards, actives)
|
||||||
|
table.insertTable(cards, vss)
|
||||||
|
|
||||||
while #cards > 0 do
|
while #cards > 0 do
|
||||||
local sth = table.random(cards)
|
local sth = table.random(cards)
|
||||||
if sth:isInstanceOf(Card) then
|
if sth:isInstanceOf(Card) then
|
||||||
local card = sth
|
local card = sth
|
||||||
local skill = card.skill ---@type ActiveSkill
|
local skill = card.skill ---@type ActiveSkill
|
||||||
if math.random() > 0.15 then
|
if math.random() > 0.15 then
|
||||||
local ret = useActiveSkill(self, skill, card)
|
local ret = useActiveSkill(self, skill, card)
|
||||||
if ret ~= "" then return ret end
|
if ret ~= "" then
|
||||||
table.removeOne(cards, card)
|
return ret
|
||||||
else
|
end
|
||||||
table.removeOne(cards, card)
|
table.removeOne(cards, card)
|
||||||
end
|
else
|
||||||
elseif sth:isInstanceOf(ActiveSkill) then
|
table.removeOne(cards, card)
|
||||||
local active = sth
|
end
|
||||||
if math.random() > 0.30 then
|
elseif sth:isInstanceOf(ActiveSkill) then
|
||||||
local ret = useActiveSkill(self, active, nil)
|
local active = sth
|
||||||
if ret ~= "" then return ret end
|
if math.random() > 0.30 then
|
||||||
end
|
local ret = useActiveSkill(self, active, nil)
|
||||||
table.removeOne(cards, active)
|
if ret ~= "" then
|
||||||
else
|
return ret
|
||||||
local vs = sth
|
end
|
||||||
if math.random() > 0.20 then
|
end
|
||||||
local ret = useVSSkill(self, vs)
|
table.removeOne(cards, active)
|
||||||
-- TODO: handle vs result
|
else
|
||||||
end
|
local vs = sth
|
||||||
table.removeOne(cards, vs)
|
if math.random() > 0.20 then
|
||||||
|
local ret = useVSSkill(self, vs)
|
||||||
|
-- TODO: handle vs result
|
||||||
|
end
|
||||||
|
table.removeOne(cards, vs)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
|
||||||
|
|
||||||
return ""
|
return ""
|
||||||
end
|
end
|
||||||
|
|
||||||
function RandomAI:initialize(player)
|
function RandomAI:initialize(player)
|
||||||
AI.initialize(self, player)
|
AI.initialize(self, player)
|
||||||
self.cb_table = random_cb
|
self.cb_table = random_cb
|
||||||
end
|
end
|
||||||
|
|
||||||
return RandomAI
|
return RandomAI
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -1,7 +1,5 @@
|
||||||
-- SPDX-License-Identifier: GPL-3.0-or-later
|
-- SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
-- Trust AI
|
-- Trust AI
|
||||||
|
|
||||||
---@class TrustAI: AI
|
---@class TrustAI: AI
|
||||||
local TrustAI = AI:subclass("TrustAI")
|
local TrustAI = AI:subclass("TrustAI")
|
||||||
|
|
||||||
|
@ -10,6 +8,328 @@ local trust_cb = {}
|
||||||
function TrustAI:initialize(player)
|
function TrustAI:initialize(player)
|
||||||
AI.initialize(self, player)
|
AI.initialize(self, player)
|
||||||
self.cb_table = trust_cb
|
self.cb_table = trust_cb
|
||||||
|
<<<<<<< HEAD
|
||||||
|
=======
|
||||||
|
self.player = player
|
||||||
|
self.room = RoomInstance or ClientInstance
|
||||||
|
|
||||||
|
fk.ai_role[player.id] = "neutral"
|
||||||
|
fk.roleValue[player.id] = {
|
||||||
|
lord = 0,
|
||||||
|
loyalist = 0,
|
||||||
|
rebel = 0,
|
||||||
|
renegade = 0
|
||||||
|
}
|
||||||
|
self:updatePlayers()
|
||||||
|
end
|
||||||
|
|
||||||
|
function TrustAI:isRolePredictable()
|
||||||
|
return self.room.settings.gameMode ~= "aaa_role_mode"
|
||||||
|
end
|
||||||
|
|
||||||
|
local function aliveRoles(room)
|
||||||
|
fk.alive_roles = {
|
||||||
|
lord = 0,
|
||||||
|
loyalist = 0,
|
||||||
|
rebel = 0,
|
||||||
|
renegade = 0
|
||||||
|
}
|
||||||
|
for _, ap in ipairs(room:getAllPlayers(false)) do
|
||||||
|
fk.alive_roles[ap.role] = 0
|
||||||
|
end
|
||||||
|
for _, ap in ipairs(room:getAlivePlayers(false)) do
|
||||||
|
fk.alive_roles[ap.role] = fk.alive_roles[ap.role] + 1
|
||||||
|
end
|
||||||
|
return fk.alive_roles
|
||||||
|
end
|
||||||
|
|
||||||
|
function TrustAI:objectiveLevel(to)
|
||||||
|
if self.player.id == to.id then
|
||||||
|
return -2
|
||||||
|
elseif #self.room:getAlivePlayers(false) < 3 then
|
||||||
|
return 5
|
||||||
|
end
|
||||||
|
local ars = aliveRoles(self.room)
|
||||||
|
if self:isRolePredictable() then
|
||||||
|
fk.ai_role[self.player.id] = self.role
|
||||||
|
fk.roleValue[self.player.id][self.role] = 666
|
||||||
|
if self.role == "renegade" then
|
||||||
|
fk.explicit_renegade = true
|
||||||
|
end
|
||||||
|
for _, p in ipairs(self.room:getAlivePlayers()) do
|
||||||
|
if
|
||||||
|
p.role == self.role or p.role == "lord" and self.role == "loyalist" or
|
||||||
|
p.role == "loyalist" and self.role == "lord"
|
||||||
|
then
|
||||||
|
table.insert(self.friends, p)
|
||||||
|
if p.id ~= self.player.id then
|
||||||
|
table.insert(self.friends_noself, p)
|
||||||
|
end
|
||||||
|
else
|
||||||
|
table.insert(self.enemies, p)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
elseif self.role == "renegade" then
|
||||||
|
if to.role == "lord" then
|
||||||
|
return -1
|
||||||
|
elseif ars.rebel < 1 then
|
||||||
|
return 4
|
||||||
|
elseif fk.ai_role[to.id] == "loyalist" then
|
||||||
|
return ars.lord + ars.loyalist - ars.rebel
|
||||||
|
elseif fk.ai_role[to.id] == "rebel" then
|
||||||
|
local r = ars.rebel - ars.lord + ars.loyalist
|
||||||
|
if r >= 0 then
|
||||||
|
return 3
|
||||||
|
else
|
||||||
|
return r
|
||||||
|
end
|
||||||
|
end
|
||||||
|
elseif self.role == "lord" or self.role == "loyalist" then
|
||||||
|
if fk.ai_role[to.id] == "rebel" then
|
||||||
|
return 5
|
||||||
|
elseif to.role == "lord" then
|
||||||
|
return -2
|
||||||
|
elseif ars.rebel < 1 then
|
||||||
|
if self.role == "lord" then
|
||||||
|
return fk.explicit_renegade and fk.ai_role[to.id] == "renegade" and 4 or to.hp > 1 and 2 or 0
|
||||||
|
elseif fk.explicit_renegade then
|
||||||
|
return fk.ai_role[to.id] == "renegade" and 4 or -1
|
||||||
|
else
|
||||||
|
return 3
|
||||||
|
end
|
||||||
|
elseif fk.ai_role[to.id] == "loyalist" then
|
||||||
|
return -2
|
||||||
|
elseif fk.ai_role[to.id] == "renegade" then
|
||||||
|
local r = ars.lord + ars.loyalist - ars.rebel
|
||||||
|
if r <= 0 then
|
||||||
|
return r
|
||||||
|
else
|
||||||
|
return 3
|
||||||
|
end
|
||||||
|
end
|
||||||
|
elseif self.role == "rebel" then
|
||||||
|
if to.role == "lord" then
|
||||||
|
return 5
|
||||||
|
elseif fk.ai_role[to.id] == "loyalist" then
|
||||||
|
return 4
|
||||||
|
elseif fk.ai_role[to.id] == "rebel" then
|
||||||
|
return -2
|
||||||
|
elseif fk.ai_role[to.id] == "renegade" then
|
||||||
|
local r = ars.rebel - ars.lord + ars.loyalist
|
||||||
|
if r > 0 then
|
||||||
|
return 1
|
||||||
|
else
|
||||||
|
return r
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return 0
|
||||||
|
end
|
||||||
|
|
||||||
|
function TrustAI:updatePlayers(update)
|
||||||
|
self.role = self.player.role
|
||||||
|
local neutrality = {}
|
||||||
|
self.enemies = {}
|
||||||
|
self.friends = {}
|
||||||
|
self.friends_noself = {}
|
||||||
|
|
||||||
|
local aps = self.room:getAlivePlayers()
|
||||||
|
local function compare_func(a, b)
|
||||||
|
local v1 = fk.roleValue[a.id].rebel
|
||||||
|
local v2 = fk.roleValue[b.id].rebel
|
||||||
|
if v1 == v2 then
|
||||||
|
v1 = fk.roleValue[a.id].renegade
|
||||||
|
v2 = fk.roleValue[b.id].renegade
|
||||||
|
end
|
||||||
|
return v1 > v2
|
||||||
|
end
|
||||||
|
table.sort(aps, compare_func)
|
||||||
|
fk.explicit_renegade = false
|
||||||
|
local ars = aliveRoles(self.room)
|
||||||
|
local rebel, renegade, loyalist = 0, 0, 0
|
||||||
|
for _, ap in ipairs(aps) do
|
||||||
|
if ap.role == "lord" then
|
||||||
|
fk.ai_role[ap.id] = "loyalist"
|
||||||
|
elseif fk.roleValue[ap.id].rebel > 50 and ars.rebel > rebel then
|
||||||
|
rebel = rebel + 1
|
||||||
|
fk.ai_role[ap.id] = "rebel"
|
||||||
|
elseif fk.roleValue[ap.id].renegade > 50 and ars.renegade > renegade then
|
||||||
|
renegade = renegade + 1
|
||||||
|
fk.ai_role[ap.id] = "renegade"
|
||||||
|
fk.explicit_renegade = fk.roleValue[ap.id].renegade > 100
|
||||||
|
elseif fk.roleValue[ap.id].rebel < -50 and ars.loyalist > loyalist then
|
||||||
|
loyalist = loyalist + 1
|
||||||
|
fk.ai_role[ap.id] = "loyalist"
|
||||||
|
else
|
||||||
|
fk.ai_role[ap.id] = "neutral"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
for n, p in ipairs(self.room:getAlivePlayers(false)) do
|
||||||
|
n = self:objectiveLevel(p)
|
||||||
|
if n < 0 then
|
||||||
|
table.insert(self.friends, p)
|
||||||
|
if p.id ~= self.player.id then
|
||||||
|
table.insert(self.friends_noself, p)
|
||||||
|
end
|
||||||
|
elseif n > 0 then
|
||||||
|
table.insert(self.enemies, p)
|
||||||
|
else
|
||||||
|
table.insert(neutrality, p)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
self:assignValue()
|
||||||
|
--[[
|
||||||
|
if self.enemies<1 and #neutrality>0
|
||||||
|
and#self.toUse<3 and self:getOverflow()>0
|
||||||
|
then
|
||||||
|
function compare_func(a,b)
|
||||||
|
return sgs.getDefense(a)<sgs.getDefense(b)
|
||||||
|
end
|
||||||
|
table.sort(neutrality,compare_func)
|
||||||
|
table.insert(self.enemies,neutrality[1])
|
||||||
|
end-]]
|
||||||
|
end
|
||||||
|
|
||||||
|
local function updateIntention(player, to, intention)
|
||||||
|
if player.id == to.id then
|
||||||
|
return
|
||||||
|
elseif player.role == "lord" then
|
||||||
|
fk.roleValue[to.id].rebel = fk.roleValue[to.id].rebel + intention * (200 - fk.roleValue[to.id].rebel) / 200
|
||||||
|
else
|
||||||
|
if to.role == "lord" or fk.ai_role[to.id] == "loyalist" then
|
||||||
|
fk.roleValue[player.id].rebel = fk.roleValue[player.id].rebel +
|
||||||
|
intention * (200 - fk.roleValue[player.id].rebel) / 200
|
||||||
|
elseif fk.ai_role[to.id] == "rebel" then
|
||||||
|
fk.roleValue[player.id].rebel = fk.roleValue[player.id].rebel -
|
||||||
|
intention * (fk.roleValue[player.id].rebel + 200) / 200
|
||||||
|
end
|
||||||
|
if fk.roleValue[player.id].rebel < 0 and intention > 0 or fk.roleValue[player.id].rebel > 0 and intention < 0 then
|
||||||
|
fk.roleValue[player.id].renegade = fk.roleValue[player.id].renegade +
|
||||||
|
intention * (100 - fk.roleValue[player.id].renegade) / 200
|
||||||
|
end
|
||||||
|
local aps = player.room:getAlivePlayers()
|
||||||
|
local function compare_func(a, b)
|
||||||
|
local v1 = fk.roleValue[a.id].rebel
|
||||||
|
local v2 = fk.roleValue[b.id].rebel
|
||||||
|
if v1 == v2 then
|
||||||
|
v1 = fk.roleValue[a.id].renegade
|
||||||
|
v2 = fk.roleValue[b.id].renegade
|
||||||
|
end
|
||||||
|
return v1 > v2
|
||||||
|
end
|
||||||
|
table.sort(aps, compare_func)
|
||||||
|
fk.explicit_renegade = false
|
||||||
|
local ars = aliveRoles(player.room)
|
||||||
|
local rebel, renegade, loyalist = 0, 0, 0
|
||||||
|
for _, ap in ipairs(aps) do
|
||||||
|
if ap.role == "lord" then
|
||||||
|
fk.ai_role[ap.id] = "loyalist"
|
||||||
|
elseif fk.roleValue[ap.id].rebel > 50 and ars.rebel > rebel then
|
||||||
|
rebel = rebel + 1
|
||||||
|
fk.ai_role[ap.id] = "rebel"
|
||||||
|
elseif fk.roleValue[ap.id].renegade > 50 and ars.renegade > renegade then
|
||||||
|
renegade = renegade + 1
|
||||||
|
fk.ai_role[ap.id] = "renegade"
|
||||||
|
fk.explicit_renegade = fk.roleValue[ap.id].renegade > 100
|
||||||
|
elseif fk.roleValue[ap.id].rebel < -50 and ars.loyalist > loyalist then
|
||||||
|
loyalist = loyalist + 1
|
||||||
|
fk.ai_role[ap.id] = "loyalist"
|
||||||
|
else
|
||||||
|
fk.ai_role[ap.id] = "neutral"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
fk.qWarning(
|
||||||
|
player.general ..
|
||||||
|
" " ..
|
||||||
|
intention ..
|
||||||
|
" " ..
|
||||||
|
fk.ai_role[player.id] ..
|
||||||
|
" rebelValue:" .. fk.roleValue[player.id].rebel .. " renegadeValue:" .. fk.roleValue[player.id].renegade
|
||||||
|
) --]]
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function TrustAI:filterEvent(event, player, data)
|
||||||
|
if event == fk.TargetSpecified then
|
||||||
|
local callback = fk.ai_card[data.card.name]
|
||||||
|
callback = callback and callback.intention
|
||||||
|
if type(callback) == "function" then
|
||||||
|
for _, p in ipairs(TargetGroup:getRealTargets(data.tos)) do
|
||||||
|
p = self.room:getPlayerById(p)
|
||||||
|
local intention = callback(p.ai, data.card, self.room:getPlayerById(data.from))
|
||||||
|
if type(intention) == "number" then
|
||||||
|
updateIntention(self.room:getPlayerById(data.from), p, intention)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
elseif type(callback) == "number" then
|
||||||
|
for _, p in ipairs(TargetGroup:getRealTargets(data.tos)) do
|
||||||
|
p = self.room:getPlayerById(p)
|
||||||
|
updateIntention(self.room:getPlayerById(data.from), p, callback)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
elseif event == fk.StartJudge then
|
||||||
|
fk.trick_judge[data.reason] = data.pattern
|
||||||
|
elseif event == fk.AfterCardsMove then
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function TrustAI:isWeak(player, getAP)
|
||||||
|
player = player or self.player
|
||||||
|
if type(player) == "number" then
|
||||||
|
player = self.room:getPlayerById(player)
|
||||||
|
end
|
||||||
|
return player.hp < 2 or player.hp <= 2 and #player:getCardIds("&h") <= 2
|
||||||
|
end
|
||||||
|
|
||||||
|
function TrustAI:isFriend(pid, tid)
|
||||||
|
if tid then
|
||||||
|
local bt = self:isFriend(pid)
|
||||||
|
return bt ~= nil and bt == self:isFriend(tid)
|
||||||
|
end
|
||||||
|
if type(pid) == "number" then
|
||||||
|
pid = self.room:getPlayerById(pid)
|
||||||
|
end
|
||||||
|
local ve = self:objectiveLevel(pid)
|
||||||
|
if ve < 0 then
|
||||||
|
return true
|
||||||
|
elseif ve > 0 then
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function TrustAI:isEnemie(pid, tid)
|
||||||
|
if tid then
|
||||||
|
local bt = self:isFriend(pid)
|
||||||
|
return bt ~= nil and bt ~= self:isFriend(tid)
|
||||||
|
end
|
||||||
|
if type(pid) == "number" then
|
||||||
|
pid = self.room:getPlayerById(pid)
|
||||||
|
end
|
||||||
|
local ve = self:objectiveLevel(pid)
|
||||||
|
if ve > 0 then
|
||||||
|
return true
|
||||||
|
elseif ve < 0 then
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function TrustAI:eventData(game_event)
|
||||||
|
local event = self.room.logic:getCurrentEvent():findParent(GameEvent[game_event], true)
|
||||||
|
return event and event.data[1]
|
||||||
|
end
|
||||||
|
|
||||||
|
for _, n in ipairs(FileIO.ls("packages")) do
|
||||||
|
if FileIO.isDir("packages/" .. n) and FileIO.exists("packages/" .. n .. "/" .. n .. "_ai.lua") then
|
||||||
|
dofile("packages/" .. n .. "/" .. n .. "_ai.lua")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
-- 加载两次拓展是为了能够引用,例如属性杀的使用直接套入普通杀的使用
|
||||||
|
for _, n in ipairs(FileIO.ls("packages")) do
|
||||||
|
if FileIO.isDir("packages/" .. n) and FileIO.exists("packages/" .. n .. "/" .. n .. "_ai.lua") then
|
||||||
|
dofile("packages/" .. n .. "/" .. n .. "_ai.lua")
|
||||||
|
end
|
||||||
|
>>>>>>> 79d3213cc0aa996c9072ae5696e387a9bda210d8
|
||||||
end
|
end
|
||||||
|
|
||||||
return TrustAI
|
return TrustAI
|
||||||
|
|
|
@ -1,15 +1,18 @@
|
||||||
-- SPDX-License-Identifier: GPL-3.0-or-later
|
-- SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
local playCardEmotionAndSound = function(room, player, card)
|
local playCardEmotionAndSound = function(room, player, card)
|
||||||
if card.type ~= Card.TypeEquip then
|
if card.type ~= Card.TypeEquip then
|
||||||
local anim_path = "./packages/" .. card.package.extensionName .. "/image/anim/" .. card.name
|
local anim_path = "./packages/" .. card.package.extensionName .. "/image/anim/" .. card.name
|
||||||
if not FileIO.exists(anim_path) then
|
if not FileIO.exists(anim_path) then
|
||||||
for _, dir in ipairs(FileIO.ls("./packages/")) do
|
for _, dir in ipairs(FileIO.ls("./packages/")) do
|
||||||
anim_path = "./packages/" .. dir .. "/image/anim/" .. card.name
|
anim_path = "./packages/" .. dir .. "/image/anim/" .. card.name
|
||||||
if FileIO.exists(anim_path) then break end
|
if FileIO.exists(anim_path) then
|
||||||
|
break
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
if FileIO.exists(anim_path) then room:setEmotion(player, anim_path) end
|
if FileIO.exists(anim_path) then
|
||||||
|
room:setEmotion(player, anim_path)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
local soundName
|
local soundName
|
||||||
|
@ -25,13 +28,15 @@ local playCardEmotionAndSound = function(room, player, card)
|
||||||
|
|
||||||
soundName = "./audio/card/common/" .. subTypeStr
|
soundName = "./audio/card/common/" .. subTypeStr
|
||||||
else
|
else
|
||||||
soundName = "./packages/" .. card.package.extensionName .. "/audio/card/"
|
soundName = "./packages/" .. card.package.extensionName .. "/audio/card/" ..
|
||||||
.. (player.gender == General.Male and "male/" or "female/") .. card.name
|
(player.gender == General.Male and "male/" or "female/") .. card.name
|
||||||
if not FileIO.exists(soundName .. ".mp3") then
|
if not FileIO.exists(soundName .. ".mp3") then
|
||||||
for _, dir in ipairs(FileIO.ls("./packages/")) do
|
for _, dir in ipairs(FileIO.ls("./packages/")) do
|
||||||
soundName = "./packages/" .. dir .. "/audio/card/"
|
soundName = "./packages/" .. dir .. "/audio/card/" ..
|
||||||
.. (player.gender == General.Male and "male/" or "female/") .. card.name
|
(player.gender == General.Male and "male/" or "female/") .. card.name
|
||||||
if FileIO.exists(soundName .. ".mp3") then break end
|
if FileIO.exists(soundName .. ".mp3") then
|
||||||
|
break
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -49,17 +54,19 @@ local sendCardEmotionAndLog = function(room, cardUseEvent)
|
||||||
local card = _card
|
local card = _card
|
||||||
---[[
|
---[[
|
||||||
if not _card:isVirtual() then
|
if not _card:isVirtual() then
|
||||||
local temp = { card = _card }
|
local temp = {
|
||||||
|
card = _card
|
||||||
|
}
|
||||||
Fk:filterCard(_card.id, room:getPlayerById(from), temp)
|
Fk:filterCard(_card.id, room:getPlayerById(from), temp)
|
||||||
card = temp.card
|
card = temp.card
|
||||||
end
|
end
|
||||||
cardUseEvent.card = card
|
cardUseEvent.card = card
|
||||||
--]]
|
-- ]]
|
||||||
|
|
||||||
playCardEmotionAndSound(room, room:getPlayerById(from), card)
|
playCardEmotionAndSound(room, room:getPlayerById(from), card)
|
||||||
room:doAnimate("Indicate", {
|
room:doAnimate("Indicate", {
|
||||||
from = from,
|
from = from,
|
||||||
to = cardUseEvent.tos or Util.DummyTable,
|
to = cardUseEvent.tos or Util.DummyTable
|
||||||
})
|
})
|
||||||
|
|
||||||
local useCardIds = card:isVirtual() and card.subcards or { card.id }
|
local useCardIds = card:isVirtual() and card.subcards or { card.id }
|
||||||
|
@ -71,23 +78,23 @@ local sendCardEmotionAndLog = function(room, cardUseEvent)
|
||||||
|
|
||||||
if card:isVirtual() or (card ~= _card) then
|
if card:isVirtual() or (card ~= _card) then
|
||||||
if #useCardIds == 0 then
|
if #useCardIds == 0 then
|
||||||
room:sendLog{
|
room:sendLog {
|
||||||
type = "#UseV0CardToTargets",
|
type = "#UseV0CardToTargets",
|
||||||
from = from,
|
from = from,
|
||||||
to = to,
|
to = to,
|
||||||
arg = card:toLogString(),
|
arg = card:toLogString()
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
room:sendLog{
|
room:sendLog {
|
||||||
type = "#UseVCardToTargets",
|
type = "#UseVCardToTargets",
|
||||||
from = from,
|
from = from,
|
||||||
to = to,
|
to = to,
|
||||||
card = useCardIds,
|
card = useCardIds,
|
||||||
arg = card:toLogString(),
|
arg = card:toLogString()
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
room:sendLog{
|
room:sendLog {
|
||||||
type = "#UseCardToTargets",
|
type = "#UseCardToTargets",
|
||||||
from = from,
|
from = from,
|
||||||
to = to,
|
to = to,
|
||||||
|
@ -97,74 +104,78 @@ local sendCardEmotionAndLog = function(room, cardUseEvent)
|
||||||
|
|
||||||
for _, t in ipairs(cardUseEvent.tos) do
|
for _, t in ipairs(cardUseEvent.tos) do
|
||||||
if t[2] then
|
if t[2] then
|
||||||
local temp = {table.unpack(t)}
|
local temp = { table.unpack(t) }
|
||||||
table.remove(temp, 1)
|
table.remove(temp, 1)
|
||||||
room:sendLog{
|
room:sendLog {
|
||||||
type = "#CardUseCollaborator",
|
type = "#CardUseCollaborator",
|
||||||
from = t[1],
|
from = t[1],
|
||||||
to = temp,
|
to = temp,
|
||||||
arg = card.name,
|
arg = card.name
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
elseif cardUseEvent.toCard then
|
elseif cardUseEvent.toCard then
|
||||||
if card:isVirtual() or (card ~= _card) then
|
if card:isVirtual() or (card ~= _card) then
|
||||||
if #useCardIds == 0 then
|
if #useCardIds == 0 then
|
||||||
room:sendLog{
|
room:sendLog {
|
||||||
type = "#UseV0CardToCard",
|
type = "#UseV0CardToCard",
|
||||||
from = from,
|
from = from,
|
||||||
arg = cardUseEvent.toCard.name,
|
arg = cardUseEvent.toCard.name,
|
||||||
arg2 = card:toLogString(),
|
arg2 = card:toLogString()
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
room:sendLog{
|
room:sendLog {
|
||||||
type = "#UseVCardToCard",
|
type = "#UseVCardToCard",
|
||||||
from = from,
|
from = from,
|
||||||
card = useCardIds,
|
card = useCardIds,
|
||||||
arg = cardUseEvent.toCard.name,
|
arg = cardUseEvent.toCard.name,
|
||||||
arg2 = card:toLogString(),
|
arg2 = card:toLogString()
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
room:sendLog{
|
room:sendLog {
|
||||||
type = "#UseCardToCard",
|
type = "#UseCardToCard",
|
||||||
from = from,
|
from = from,
|
||||||
card = useCardIds,
|
card = useCardIds,
|
||||||
arg = cardUseEvent.toCard.name,
|
arg = cardUseEvent.toCard.name
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
if card:isVirtual() or (card ~= _card) then
|
if card:isVirtual() or (card ~= _card) then
|
||||||
if #useCardIds == 0 then
|
if #useCardIds == 0 then
|
||||||
room:sendLog{
|
room:sendLog {
|
||||||
type = "#UseV0Card",
|
type = "#UseV0Card",
|
||||||
from = from,
|
from = from,
|
||||||
arg = card:toLogString(),
|
arg = card:toLogString()
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
room:sendLog{
|
room:sendLog {
|
||||||
type = "#UseVCard",
|
type = "#UseVCard",
|
||||||
from = from,
|
from = from,
|
||||||
card = useCardIds,
|
card = useCardIds,
|
||||||
arg = card:toLogString(),
|
arg = card:toLogString()
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
room:sendLog{
|
room:sendLog {
|
||||||
type = "#UseCard",
|
type = "#UseCard",
|
||||||
from = from,
|
from = from,
|
||||||
card = useCardIds,
|
card = useCardIds
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
if #useCardIds == 0 then return end
|
if #useCardIds == 0 then
|
||||||
|
return
|
||||||
|
end
|
||||||
if cardUseEvent.tos and #cardUseEvent.tos > 0 and #cardUseEvent.tos <= 2 then
|
if cardUseEvent.tos and #cardUseEvent.tos > 0 and #cardUseEvent.tos <= 2 then
|
||||||
local tos = table.map(cardUseEvent.tos, function(e) return e[1] end)
|
local tos = table.map(cardUseEvent.tos, function(e)
|
||||||
|
return e[1]
|
||||||
|
end)
|
||||||
room:sendFootnote(useCardIds, {
|
room:sendFootnote(useCardIds, {
|
||||||
type = "##UseCardTo",
|
type = "##UseCardTo",
|
||||||
from = from,
|
from = from,
|
||||||
to = tos,
|
to = tos
|
||||||
})
|
})
|
||||||
if card:isVirtual() then
|
if card:isVirtual() then
|
||||||
room:sendCardVirtName(useCardIds, card.name)
|
room:sendCardVirtName(useCardIds, card.name)
|
||||||
|
@ -172,7 +183,7 @@ local sendCardEmotionAndLog = function(room, cardUseEvent)
|
||||||
else
|
else
|
||||||
room:sendFootnote(useCardIds, {
|
room:sendFootnote(useCardIds, {
|
||||||
type = "##UseCard",
|
type = "##UseCard",
|
||||||
from = from,
|
from = from
|
||||||
})
|
})
|
||||||
if card:isVirtual() then
|
if card:isVirtual() then
|
||||||
room:sendCardVirtName(useCardIds, card.name)
|
room:sendCardVirtName(useCardIds, card.name)
|
||||||
|
@ -185,16 +196,18 @@ GameEvent.functions[GameEvent.UseCard] = function(self)
|
||||||
local room = self.room
|
local room = self.room
|
||||||
local logic = room.logic
|
local logic = room.logic
|
||||||
|
|
||||||
room:moveCardTo(cardUseEvent.card, Card.Processing, nil, fk.ReasonUse)
|
|
||||||
|
|
||||||
if cardUseEvent.card.skill then
|
if cardUseEvent.card.skill then
|
||||||
cardUseEvent.card.skill:onUse(room, cardUseEvent)
|
cardUseEvent.card.skill:onUse(room, cardUseEvent)
|
||||||
end
|
end
|
||||||
|
|
||||||
if logic:trigger(fk.PreCardUse, room:getPlayerById(cardUseEvent.from), cardUseEvent) then
|
if logic:trigger(fk.PreCardUse, room:getPlayerById(cardUseEvent.from), cardUseEvent) then
|
||||||
|
cardUseEvent.breakEvent = true
|
||||||
|
self.data = { cardUseEvent }
|
||||||
logic:breakEvent()
|
logic:breakEvent()
|
||||||
end
|
end
|
||||||
|
|
||||||
|
room:moveCardTo(cardUseEvent.card, Card.Processing, nil, fk.ReasonUse)
|
||||||
|
|
||||||
sendCardEmotionAndLog(room, cardUseEvent)
|
sendCardEmotionAndLog(room, cardUseEvent)
|
||||||
|
|
||||||
if not cardUseEvent.extraUse then
|
if not cardUseEvent.extraUse then
|
||||||
|
@ -205,7 +218,6 @@ GameEvent.functions[GameEvent.UseCard] = function(self)
|
||||||
cardUseEvent.responseToEvent.cardsResponded = cardUseEvent.responseToEvent.cardsResponded or {}
|
cardUseEvent.responseToEvent.cardsResponded = cardUseEvent.responseToEvent.cardsResponded or {}
|
||||||
table.insertIfNeed(cardUseEvent.responseToEvent.cardsResponded, cardUseEvent.card)
|
table.insertIfNeed(cardUseEvent.responseToEvent.cardsResponded, cardUseEvent.card)
|
||||||
end
|
end
|
||||||
|
|
||||||
for _, event in ipairs({ fk.AfterCardUseDeclared, fk.AfterCardTargetDeclared, fk.CardUsing }) do
|
for _, event in ipairs({ fk.AfterCardUseDeclared, fk.AfterCardTargetDeclared, fk.CardUsing }) do
|
||||||
if not cardUseEvent.toCard and #TargetGroup:getRealTargets(cardUseEvent.tos) == 0 then
|
if not cardUseEvent.toCard and #TargetGroup:getRealTargets(cardUseEvent.tos) == 0 then
|
||||||
break
|
break
|
||||||
|
@ -229,7 +241,7 @@ GameEvent.cleaners[GameEvent.UseCard] = function(self)
|
||||||
room:moveCards({
|
room:moveCards({
|
||||||
ids = leftRealCardIds,
|
ids = leftRealCardIds,
|
||||||
toArea = Card.DiscardPile,
|
toArea = Card.DiscardPile,
|
||||||
moveReason = fk.ReasonUse,
|
moveReason = fk.ReasonUse
|
||||||
})
|
})
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -238,50 +250,54 @@ GameEvent.functions[GameEvent.RespondCard] = function(self)
|
||||||
local cardResponseEvent = table.unpack(self.data)
|
local cardResponseEvent = table.unpack(self.data)
|
||||||
local room = self.room
|
local room = self.room
|
||||||
local logic = room.logic
|
local logic = room.logic
|
||||||
|
|
||||||
|
if logic:trigger(fk.PreCardRespond, room:getPlayerById(cardResponseEvent.from), cardResponseEvent) then
|
||||||
|
cardResponseEvent.breakEvent = true
|
||||||
|
self.data = { cardResponseEvent }
|
||||||
|
logic:breakEvent()
|
||||||
|
end
|
||||||
|
|
||||||
local from = cardResponseEvent.customFrom or cardResponseEvent.from
|
local from = cardResponseEvent.customFrom or cardResponseEvent.from
|
||||||
local card = cardResponseEvent.card
|
local card = cardResponseEvent.card
|
||||||
local cardIds = room:getSubcardsByRule(card)
|
local cardIds = room:getSubcardsByRule(card)
|
||||||
|
|
||||||
if card:isVirtual() then
|
if card:isVirtual() then
|
||||||
if #cardIds == 0 then
|
if #cardIds == 0 then
|
||||||
room:sendLog{
|
room:sendLog {
|
||||||
type = "#ResponsePlayV0Card",
|
type = "#ResponsePlayV0Card",
|
||||||
from = from,
|
from = from,
|
||||||
arg = card:toLogString(),
|
arg = card:toLogString()
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
room:sendLog{
|
room:sendLog {
|
||||||
type = "#ResponsePlayVCard",
|
type = "#ResponsePlayVCard",
|
||||||
from = from,
|
from = from,
|
||||||
card = cardIds,
|
card = cardIds,
|
||||||
arg = card:toLogString(),
|
arg = card:toLogString()
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
room:sendLog{
|
room:sendLog {
|
||||||
type = "#ResponsePlayCard",
|
type = "#ResponsePlayCard",
|
||||||
from = from,
|
from = from,
|
||||||
card = cardIds,
|
card = cardIds
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
room:moveCardTo(card, Card.Processing, nil, fk.ReasonResonpse)
|
room:moveCardTo(card, Card.Processing, nil, fk.ReasonResonpse)
|
||||||
if #cardIds > 0 then
|
if #cardIds > 0 then
|
||||||
room:sendFootnote(cardIds, {
|
room:sendFootnote(cardIds, {
|
||||||
type = "##ResponsePlayCard",
|
type = "##ResponsePlayCard",
|
||||||
from = from,
|
from = from
|
||||||
})
|
})
|
||||||
if card:isVirtual() then
|
if card:isVirtual() then
|
||||||
room:sendCardVirtName(cardIds, card.name)
|
room:sendCardVirtName(cardIds, card.name)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
if cardResponseEvent.retrial ~= true then
|
||||||
if logic:trigger(fk.PreCardRespond, room:getPlayerById(cardResponseEvent.from), cardResponseEvent) then
|
playCardEmotionAndSound(room, room:getPlayerById(from), card)
|
||||||
logic:breakEvent()
|
|
||||||
end
|
end
|
||||||
|
|
||||||
playCardEmotionAndSound(room, room:getPlayerById(from), card)
|
|
||||||
|
|
||||||
logic:trigger(fk.CardResponding, room:getPlayerById(cardResponseEvent.from), cardResponseEvent)
|
logic:trigger(fk.CardResponding, room:getPlayerById(cardResponseEvent.from), cardResponseEvent)
|
||||||
|
self.data = { cardResponseEvent }
|
||||||
end
|
end
|
||||||
|
|
||||||
GameEvent.cleaners[GameEvent.RespondCard] = function(self)
|
GameEvent.cleaners[GameEvent.RespondCard] = function(self)
|
||||||
|
@ -295,7 +311,7 @@ GameEvent.cleaners[GameEvent.RespondCard] = function(self)
|
||||||
room:moveCards({
|
room:moveCards({
|
||||||
ids = realCardIds,
|
ids = realCardIds,
|
||||||
toArea = Card.DiscardPile,
|
toArea = Card.DiscardPile,
|
||||||
moveReason = fk.ReasonResonpse,
|
moveReason = fk.ReasonResonpse
|
||||||
})
|
})
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -315,13 +331,9 @@ GameEvent.functions[GameEvent.CardEffect] = function(self)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
if
|
if not cardEffectEvent.toCard and
|
||||||
not cardEffectEvent.toCard and
|
(not (room:getPlayerById(cardEffectEvent.to):isAlive() and cardEffectEvent.to) or
|
||||||
(
|
#room:deadPlayerFilter(TargetGroup:getRealTargets(cardEffectEvent.tos)) == 0) then
|
||||||
not (room:getPlayerById(cardEffectEvent.to):isAlive() and cardEffectEvent.to)
|
|
||||||
or #room:deadPlayerFilter(TargetGroup:getRealTargets(cardEffectEvent.tos)) == 0
|
|
||||||
)
|
|
||||||
then
|
|
||||||
logic:breakEvent()
|
logic:breakEvent()
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -73,13 +73,6 @@ function GameEvent:addExitFunc(f)
|
||||||
table.insert(self.extra_exit_funcs, f)
|
table.insert(self.extra_exit_funcs, f)
|
||||||
end
|
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)
|
function GameEvent:findParent(eventType, includeSelf)
|
||||||
if includeSelf and self.event == eventType then return self end
|
if includeSelf and self.event == eventType then return self end
|
||||||
local e = self.parent
|
local e = self.parent
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
-- SPDX-License-Identifier: GPL-3.0-or-later
|
-- SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
---@class GameLogic: Object
|
---@class GameLogic: Object
|
||||||
---@field public room Room
|
---@field public room Room
|
||||||
---@field public skill_table table<Event, TriggerSkill[]>
|
---@field public skill_table table<Event, TriggerSkill[]>
|
||||||
|
@ -14,421 +13,424 @@
|
||||||
local GameLogic = class("GameLogic")
|
local GameLogic = class("GameLogic")
|
||||||
|
|
||||||
function GameLogic:initialize(room)
|
function GameLogic:initialize(room)
|
||||||
self.room = room
|
self.room = room
|
||||||
self.skill_table = {} -- TriggerEvent --> TriggerSkill[]
|
self.skill_table = {} -- TriggerEvent --> TriggerSkill[]
|
||||||
self.skill_priority_table = {}
|
self.skill_priority_table = {}
|
||||||
self.refresh_skill_table = {}
|
self.refresh_skill_table = {}
|
||||||
self.skills = {} -- skillName[]
|
self.skills = {} -- skillName[]
|
||||||
self.game_event_stack = Stack:new()
|
self.game_event_stack = Stack:new()
|
||||||
self.all_game_events = {}
|
self.all_game_events = {}
|
||||||
self.event_recorder = {}
|
self.event_recorder = {}
|
||||||
self.current_event_id = 0
|
self.current_event_id = 0
|
||||||
|
|
||||||
self.role_table = {
|
self.role_table = {{"lord"}, {"lord", "rebel"}, {"lord", "rebel", "renegade"},
|
||||||
{ "lord" },
|
{"lord", "loyalist", "rebel", "renegade"}, {"lord", "loyalist", "rebel", "rebel", "renegade"},
|
||||||
{ "lord", "rebel" },
|
{"lord", "loyalist", "rebel", "rebel", "rebel", "renegade"},
|
||||||
{ "lord", "rebel", "renegade" },
|
{"lord", "loyalist", "loyalist", "rebel", "rebel", "rebel", "renegade"},
|
||||||
{ "lord", "loyalist", "rebel", "renegade" },
|
{"lord", "loyalist", "loyalist", "rebel", "rebel", "rebel", "rebel", "renegade"}}
|
||||||
{ "lord", "loyalist", "rebel", "rebel", "renegade" },
|
|
||||||
{ "lord", "loyalist", "rebel", "rebel", "rebel", "renegade" },
|
|
||||||
{ "lord", "loyalist", "loyalist", "rebel", "rebel", "rebel", "renegade" },
|
|
||||||
{ "lord", "loyalist", "loyalist", "rebel", "rebel", "rebel", "rebel", "renegade" },
|
|
||||||
}
|
|
||||||
end
|
end
|
||||||
|
|
||||||
function GameLogic:run()
|
function GameLogic:run()
|
||||||
-- default logic
|
-- default logic
|
||||||
local room = self.room
|
local room = self.room
|
||||||
table.shuffle(self.room.players)
|
table.shuffle(self.room.players)
|
||||||
self:assignRoles()
|
self:assignRoles()
|
||||||
self.room.game_started = true
|
self.room.game_started = true
|
||||||
room:doBroadcastNotify("StartGame", "")
|
room:doBroadcastNotify("StartGame", "")
|
||||||
room:adjustSeats()
|
room:adjustSeats()
|
||||||
|
|
||||||
self:chooseGenerals()
|
self:chooseGenerals()
|
||||||
|
|
||||||
self:buildPlayerCircle()
|
self:buildPlayerCircle()
|
||||||
self:broadcastGeneral()
|
self:broadcastGeneral()
|
||||||
self:prepareDrawPile()
|
self:prepareDrawPile()
|
||||||
self:attachSkillToPlayers()
|
self:attachSkillToPlayers()
|
||||||
self:prepareForStart()
|
self:prepareForStart()
|
||||||
|
|
||||||
self:action()
|
self:action()
|
||||||
end
|
end
|
||||||
|
|
||||||
local function execGameEvent(type, ...)
|
local function execGameEvent(type, ...)
|
||||||
local event = GameEvent:new(type, ...)
|
local event = GameEvent:new(type, ...)
|
||||||
local _, ret = event:exec()
|
local _, ret = event:exec()
|
||||||
return ret
|
return ret
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
function GameLogic:assignRoles()
|
function GameLogic:assignRoles()
|
||||||
local room = self.room
|
local room = self.room
|
||||||
local n = #room.players
|
local n = #room.players
|
||||||
local roles = self.role_table[n]
|
local roles = self.role_table[n]
|
||||||
table.shuffle(roles)
|
table.shuffle(roles)
|
||||||
|
|
||||||
for i = 1, n do
|
for i = 1, n do
|
||||||
local p = room.players[i]
|
local p = room.players[i]
|
||||||
p.role = roles[i]
|
p.role = roles[i]
|
||||||
if p.role == "lord" then
|
if p.role == "lord" then
|
||||||
p.role_shown = true
|
p.role_shown = true
|
||||||
room:broadcastProperty(p, "role")
|
room:broadcastProperty(p, "role")
|
||||||
else
|
else
|
||||||
room:notifyProperty(p, p, "role")
|
room:notifyProperty(p, p, "role")
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
function GameLogic:chooseGenerals()
|
function GameLogic:chooseGenerals()
|
||||||
local room = self.room
|
local room = self.room
|
||||||
local generalNum = room.settings.generalNum
|
local generalNum = room.settings.generalNum
|
||||||
local n = room.settings.enableDeputy and 2 or 1
|
local n = room.settings.enableDeputy and 2 or 1
|
||||||
local lord = room:getLord()
|
local lord = room:getLord()
|
||||||
local lord_generals = {}
|
local lord_generals = {}
|
||||||
|
|
||||||
if lord ~= nil then
|
if lord ~= nil then
|
||||||
room.current = lord
|
room.current = lord
|
||||||
local generals = {}
|
local generals = {}
|
||||||
local lordlist = {}
|
local lordlist = {}
|
||||||
local lordpools = {}
|
local lordpools = {}
|
||||||
if room.settings.gameMode == "aaa_role_mode" then
|
if room.settings.gameMode == "aaa_role_mode" then
|
||||||
for _, general in pairs(Fk:getAllGenerals()) do
|
for _, general in pairs(Fk:getAllGenerals()) do
|
||||||
if (not general.hidden and not general.total_hidden) and
|
if (not general.hidden and not general.total_hidden) and table.find(general.skills, function(s)
|
||||||
table.find(general.skills, function(s)
|
return s.lordSkill
|
||||||
return s.lordSkill
|
end) and not table.find(lordlist, function(g)
|
||||||
end) and
|
return g.trueName == general.trueName
|
||||||
not table.find(lordlist, function(g)
|
end) then
|
||||||
return g.trueName == general.trueName
|
table.insert(lordlist, general)
|
||||||
end) then
|
end
|
||||||
table.insert(lordlist, general)
|
end
|
||||||
|
lordlist = table.random(lordlist, 3) or {}
|
||||||
end
|
end
|
||||||
end
|
table.insertTable(generals, Fk:getGeneralsRandomly(generalNum, Fk:getAllGenerals(), nil, function(g)
|
||||||
lordlist = table.random(lordlist, 3) or {}
|
return table.contains(table.map(lordlist, function(g)
|
||||||
end
|
return g.trueName
|
||||||
table.insertTable(generals, Fk:getGeneralsRandomly(generalNum, Fk:getAllGenerals(), nil, function(g)
|
end), g.trueName)
|
||||||
return table.contains(table.map(lordlist, function(g) return g.trueName end), g.trueName)
|
end))
|
||||||
end))
|
for i = 1, #generals do
|
||||||
for i = 1, #generals do
|
generals[i] = generals[i].name
|
||||||
generals[i] = generals[i].name
|
end
|
||||||
end
|
lordpools = table.simpleClone(generals)
|
||||||
lordpools = table.simpleClone(generals)
|
table.insertTable(lordpools, table.map(lordlist, function(g)
|
||||||
table.insertTable(lordpools, table.map(lordlist, function(g) return g.name end))
|
return g.name
|
||||||
lord_generals = room:askForGeneral(lord, lordpools, n)
|
end))
|
||||||
local lord_general, deputy
|
lord_generals = room:askForGeneral(lord, lordpools, n)
|
||||||
if type(lord_generals) == "table" then
|
local lord_general, deputy
|
||||||
deputy = lord_generals[2]
|
if type(lord_generals) == "table" then
|
||||||
lord_general = lord_generals[1]
|
deputy = lord_generals[2]
|
||||||
else
|
lord_general = lord_generals[1]
|
||||||
lord_general = lord_generals
|
else
|
||||||
lord_generals = {lord_general}
|
lord_general = lord_generals
|
||||||
|
lord_generals = {lord_general}
|
||||||
|
end
|
||||||
|
|
||||||
|
room:setPlayerGeneral(lord, lord_general, true)
|
||||||
|
room:askForChooseKingdom({lord})
|
||||||
|
room:broadcastProperty(lord, "general")
|
||||||
|
room:broadcastProperty(lord, "kingdom")
|
||||||
|
room:setDeputyGeneral(lord, deputy)
|
||||||
|
room:broadcastProperty(lord, "deputyGeneral")
|
||||||
end
|
end
|
||||||
|
|
||||||
room:setPlayerGeneral(lord, lord_general, true)
|
local nonlord = room:getOtherPlayers(lord, true)
|
||||||
room:askForChooseKingdom({lord})
|
local generals = Fk:getGeneralsRandomly(#nonlord * generalNum, nil, lord_generals)
|
||||||
room:broadcastProperty(lord, "general")
|
table.shuffle(generals)
|
||||||
room:broadcastProperty(lord, "kingdom")
|
for _, p in ipairs(nonlord) do
|
||||||
room:setDeputyGeneral(lord, deputy)
|
local arg = {}
|
||||||
room:broadcastProperty(lord, "deputyGeneral")
|
for i = 1, generalNum do
|
||||||
end
|
table.insert(arg, table.remove(generals, 1).name)
|
||||||
|
end
|
||||||
local nonlord = room:getOtherPlayers(lord, true)
|
p.request_data = json.encode {arg, n}
|
||||||
local generals = Fk:getGeneralsRandomly(#nonlord * generalNum, nil, lord_generals)
|
p.default_reply = table.random(arg, n)
|
||||||
table.shuffle(generals)
|
|
||||||
for _, p in ipairs(nonlord) do
|
|
||||||
local arg = {}
|
|
||||||
for i = 1, generalNum do
|
|
||||||
table.insert(arg, table.remove(generals, 1).name)
|
|
||||||
end
|
end
|
||||||
p.request_data = json.encode{ arg, n }
|
|
||||||
p.default_reply = table.random(arg, n)
|
|
||||||
end
|
|
||||||
|
|
||||||
room:notifyMoveFocus(nonlord, "AskForGeneral")
|
room:notifyMoveFocus(nonlord, "AskForGeneral")
|
||||||
room:doBroadcastRequest("AskForGeneral", nonlord)
|
room:doBroadcastRequest("AskForGeneral", nonlord)
|
||||||
|
|
||||||
for _, p in ipairs(nonlord) do
|
for _, p in ipairs(nonlord) do
|
||||||
if p.general == "" and p.reply_ready then
|
if p.general == "" and p.reply_ready then
|
||||||
local generals = json.decode(p.client_reply)
|
local generals = json.decode(p.client_reply)
|
||||||
local general = generals[1]
|
local general = generals[1]
|
||||||
local deputy = generals[2]
|
local deputy = generals[2]
|
||||||
room:setPlayerGeneral(p, general, true, true)
|
room:setPlayerGeneral(p, general, true, true)
|
||||||
room:setDeputyGeneral(p, deputy)
|
room:setDeputyGeneral(p, deputy)
|
||||||
else
|
else
|
||||||
room:setPlayerGeneral(p, p.default_reply[1], true, true)
|
room:setPlayerGeneral(p, p.default_reply[1], true, true)
|
||||||
room:setDeputyGeneral(p, p.default_reply[2])
|
room:setDeputyGeneral(p, p.default_reply[2])
|
||||||
|
end
|
||||||
|
p.default_reply = ""
|
||||||
end
|
end
|
||||||
p.default_reply = ""
|
|
||||||
end
|
|
||||||
|
|
||||||
room:askForChooseKingdom(nonlord)
|
room:askForChooseKingdom(nonlord)
|
||||||
end
|
end
|
||||||
|
|
||||||
function GameLogic:buildPlayerCircle()
|
function GameLogic:buildPlayerCircle()
|
||||||
local room = self.room
|
local room = self.room
|
||||||
local players = room.players
|
local players = room.players
|
||||||
room.alive_players = {table.unpack(players)}
|
room.alive_players = {table.unpack(players)}
|
||||||
for i = 1, #players - 1 do
|
for i = 1, #players - 1 do
|
||||||
players[i].next = players[i + 1]
|
players[i].next = players[i + 1]
|
||||||
end
|
end
|
||||||
players[#players].next = players[1]
|
players[#players].next = players[1]
|
||||||
end
|
end
|
||||||
|
|
||||||
function GameLogic:broadcastGeneral()
|
function GameLogic:broadcastGeneral()
|
||||||
local room = self.room
|
local room = self.room
|
||||||
local players = room.players
|
local players = room.players
|
||||||
|
|
||||||
for _, p in ipairs(players) do
|
for _, p in ipairs(players) do
|
||||||
assert(p.general ~= "")
|
assert(p.general ~= "")
|
||||||
local general = Fk.generals[p.general]
|
local general = Fk.generals[p.general]
|
||||||
local deputy = Fk.generals[p.deputyGeneral]
|
local deputy = Fk.generals[p.deputyGeneral]
|
||||||
p.maxHp = p:getGeneralMaxHp()
|
p.maxHp = p:getGeneralMaxHp()
|
||||||
p.hp = deputy and math.floor((deputy.hp + general.hp) / 2) or general.hp
|
p.hp = deputy and math.floor((deputy.hp + general.hp) / 2) or general.hp
|
||||||
p.shield = math.min(general.shield + (deputy and deputy.shield or 0), 5)
|
p.shield = math.min(general.shield + (deputy and deputy.shield or 0), 5)
|
||||||
-- TODO: setup AI here
|
-- TODO: setup AI here
|
||||||
|
|
||||||
if p.role ~= "lord" then
|
if p.role ~= "lord" then
|
||||||
room:broadcastProperty(p, "general")
|
room:broadcastProperty(p, "general")
|
||||||
room:broadcastProperty(p, "kingdom")
|
room:broadcastProperty(p, "kingdom")
|
||||||
room:broadcastProperty(p, "deputyGeneral")
|
room:broadcastProperty(p, "deputyGeneral")
|
||||||
elseif #players >= 5 then
|
elseif #players >= 5 then
|
||||||
p.maxHp = p.maxHp + 1
|
p.maxHp = p.maxHp + 1
|
||||||
p.hp = p.hp + 1
|
p.hp = p.hp + 1
|
||||||
|
end
|
||||||
|
room:broadcastProperty(p, "maxHp")
|
||||||
|
room:broadcastProperty(p, "hp")
|
||||||
|
room:broadcastProperty(p, "shield")
|
||||||
end
|
end
|
||||||
room:broadcastProperty(p, "maxHp")
|
|
||||||
room:broadcastProperty(p, "hp")
|
|
||||||
room:broadcastProperty(p, "shield")
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
function GameLogic:prepareDrawPile()
|
function GameLogic:prepareDrawPile()
|
||||||
local room = self.room
|
local room = self.room
|
||||||
local allCardIds = Fk:getAllCardIds()
|
local allCardIds = Fk:getAllCardIds()
|
||||||
|
|
||||||
for i = #allCardIds, 1, -1 do
|
for i = #allCardIds, 1, -1 do
|
||||||
if Fk:getCardById(allCardIds[i]).is_derived then
|
if Fk:getCardById(allCardIds[i]).is_derived then
|
||||||
local id = allCardIds[i]
|
local id = allCardIds[i]
|
||||||
table.removeOne(allCardIds, id)
|
table.removeOne(allCardIds, id)
|
||||||
table.insert(room.void, id)
|
table.insert(room.void, id)
|
||||||
room:setCardArea(id, Card.Void, nil)
|
room:setCardArea(id, Card.Void, nil)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
|
||||||
|
|
||||||
table.shuffle(allCardIds)
|
table.shuffle(allCardIds)
|
||||||
room.draw_pile = allCardIds
|
room.draw_pile = allCardIds
|
||||||
for _, id in ipairs(room.draw_pile) do
|
for _, id in ipairs(room.draw_pile) do
|
||||||
self.room:setCardArea(id, Card.DrawPile, nil)
|
self.room:setCardArea(id, Card.DrawPile, nil)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
function GameLogic:attachSkillToPlayers()
|
function GameLogic:attachSkillToPlayers()
|
||||||
local room = self.room
|
local room = self.room
|
||||||
local players = room.players
|
local players = room.players
|
||||||
|
|
||||||
local addRoleModSkills = function(player, skillName)
|
local addRoleModSkills = function(player, skillName)
|
||||||
local skill = Fk.skills[skillName]
|
local skill = Fk.skills[skillName]
|
||||||
if skill.lordSkill and (player.role ~= "lord" or #room.players < 5) then
|
if skill.lordSkill and (player.role ~= "lord" or #room.players < 5) then
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
if #skill.attachedKingdom > 0 and not table.contains(skill.attachedKingdom, player.kingdom) then
|
if #skill.attachedKingdom > 0 and not table.contains(skill.attachedKingdom, player.kingdom) then
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
room:handleAddLoseSkills(player, skillName, nil, false)
|
room:handleAddLoseSkills(player, skillName, nil, false)
|
||||||
end
|
|
||||||
for _, p in ipairs(room.alive_players) do
|
|
||||||
local skills = Fk.generals[p.general].skills
|
|
||||||
for _, s in ipairs(skills) do
|
|
||||||
addRoleModSkills(p, s.name)
|
|
||||||
end
|
|
||||||
for _, sname in ipairs(Fk.generals[p.general].other_skills) do
|
|
||||||
addRoleModSkills(p, sname)
|
|
||||||
end
|
end
|
||||||
|
for _, p in ipairs(room.alive_players) do
|
||||||
|
local skills = Fk.generals[p.general].skills
|
||||||
|
for _, s in ipairs(skills) do
|
||||||
|
addRoleModSkills(p, s.name)
|
||||||
|
end
|
||||||
|
for _, sname in ipairs(Fk.generals[p.general].other_skills) do
|
||||||
|
addRoleModSkills(p, sname)
|
||||||
|
end
|
||||||
|
|
||||||
local deputy = Fk.generals[p.deputyGeneral]
|
local deputy = Fk.generals[p.deputyGeneral]
|
||||||
if deputy then
|
if deputy then
|
||||||
skills = deputy.skills
|
skills = deputy.skills
|
||||||
for _, s in ipairs(skills) do
|
for _, s in ipairs(skills) do
|
||||||
addRoleModSkills(p, s.name)
|
addRoleModSkills(p, s.name)
|
||||||
end
|
end
|
||||||
for _, sname in ipairs(deputy.other_skills) do
|
for _, sname in ipairs(deputy.other_skills) do
|
||||||
addRoleModSkills(p, sname)
|
addRoleModSkills(p, sname)
|
||||||
end
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
function GameLogic:prepareForStart()
|
function GameLogic:prepareForStart()
|
||||||
local room = self.room
|
local room = self.room
|
||||||
local players = room.players
|
local players = room.players
|
||||||
|
|
||||||
self:addTriggerSkill(GameRule)
|
self:addTriggerSkill(GameRule)
|
||||||
for _, trig in ipairs(Fk.global_trigger) do
|
for _, trig in ipairs(Fk.global_trigger) do
|
||||||
self:addTriggerSkill(trig)
|
self:addTriggerSkill(trig)
|
||||||
end
|
end
|
||||||
|
|
||||||
self.room:sendLog{ type = "$GameStart" }
|
self.room:sendLog{
|
||||||
|
type = "$GameStart"
|
||||||
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
function GameLogic:action()
|
function GameLogic:action()
|
||||||
self:trigger(fk.GamePrepared)
|
self:trigger(fk.GamePrepared)
|
||||||
local room = self.room
|
local room = self.room
|
||||||
|
|
||||||
execGameEvent(GameEvent.DrawInitial)
|
execGameEvent(GameEvent.DrawInitial)
|
||||||
|
|
||||||
while true do
|
while true do
|
||||||
execGameEvent(GameEvent.Round)
|
execGameEvent(GameEvent.Round)
|
||||||
if room.game_finished then break end
|
if room.game_finished then
|
||||||
end
|
break
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
---@param skill TriggerSkill
|
---@param skill TriggerSkill
|
||||||
function GameLogic:addTriggerSkill(skill)
|
function GameLogic:addTriggerSkill(skill)
|
||||||
if skill == nil or table.contains(self.skills, skill.name) then
|
if skill == nil or table.contains(self.skills, skill.name) then
|
||||||
return
|
return
|
||||||
end
|
|
||||||
|
|
||||||
table.insert(self.skills, skill.name)
|
|
||||||
|
|
||||||
for _, event in ipairs(skill.refresh_events) do
|
|
||||||
if self.refresh_skill_table[event] == nil then
|
|
||||||
self.refresh_skill_table[event] = {}
|
|
||||||
end
|
|
||||||
table.insert(self.refresh_skill_table[event], skill)
|
|
||||||
end
|
|
||||||
|
|
||||||
for _, event in ipairs(skill.events) do
|
|
||||||
if self.skill_table[event] == nil then
|
|
||||||
self.skill_table[event] = {}
|
|
||||||
end
|
|
||||||
table.insert(self.skill_table[event], skill)
|
|
||||||
|
|
||||||
if self.skill_priority_table[event] == nil then
|
|
||||||
self.skill_priority_table[event] = {}
|
|
||||||
end
|
end
|
||||||
|
|
||||||
local priority_tab = self.skill_priority_table[event]
|
table.insert(self.skills, skill.name)
|
||||||
local prio = skill.priority_table[event]
|
|
||||||
if not table.contains(priority_tab, prio) then
|
for _, event in ipairs(skill.refresh_events) do
|
||||||
for i, v in ipairs(priority_tab) do
|
if self.refresh_skill_table[event] == nil then
|
||||||
if v < prio then
|
self.refresh_skill_table[event] = {}
|
||||||
table.insert(priority_tab, i, prio)
|
|
||||||
break
|
|
||||||
end
|
end
|
||||||
end
|
table.insert(self.refresh_skill_table[event], skill)
|
||||||
|
|
||||||
if not table.contains(priority_tab, prio) then
|
|
||||||
table.insert(priority_tab, prio)
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
if not table.contains(self.skill_priority_table[event],
|
for _, event in ipairs(skill.events) do
|
||||||
skill.priority_table[event]) then
|
if self.skill_table[event] == nil then
|
||||||
|
self.skill_table[event] = {}
|
||||||
|
end
|
||||||
|
table.insert(self.skill_table[event], skill)
|
||||||
|
|
||||||
table.insert(self.skill_priority_table[event],
|
if self.skill_priority_table[event] == nil then
|
||||||
skill.priority_table[event])
|
self.skill_priority_table[event] = {}
|
||||||
end
|
end
|
||||||
end
|
|
||||||
|
|
||||||
if skill.visible then
|
local priority_tab = self.skill_priority_table[event]
|
||||||
if (Fk.related_skills[skill.name] == nil) then return end
|
local prio = skill.priority_table[event]
|
||||||
for _, s in ipairs(Fk.related_skills[skill.name]) do
|
if not table.contains(priority_tab, prio) then
|
||||||
if (s.class == TriggerSkill) then
|
for i, v in ipairs(priority_tab) do
|
||||||
self:addTriggerSkill(s)
|
if v < prio then
|
||||||
end
|
table.insert(priority_tab, i, prio)
|
||||||
|
break
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if not table.contains(priority_tab, prio) then
|
||||||
|
table.insert(priority_tab, prio)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if not table.contains(self.skill_priority_table[event], skill.priority_table[event]) then
|
||||||
|
|
||||||
|
table.insert(self.skill_priority_table[event], skill.priority_table[event])
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if skill.visible then
|
||||||
|
if (Fk.related_skills[skill.name] == nil) then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
for _, s in ipairs(Fk.related_skills[skill.name]) do
|
||||||
|
if (s.class == TriggerSkill) then
|
||||||
|
self:addTriggerSkill(s)
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
---@param event Event
|
---@param event Event
|
||||||
---@param target ServerPlayer|nil
|
---@param target ServerPlayer|nil
|
||||||
---@param data any|nil
|
---@param data any|nil
|
||||||
function GameLogic:trigger(event, target, data, refresh_only)
|
function GameLogic:trigger(event, target, data, refresh_only)
|
||||||
local room = self.room
|
local room = self.room
|
||||||
local broken = false
|
local broken = false
|
||||||
local skills = self.skill_table[event] or {}
|
local skills = self.skill_table[event] or {}
|
||||||
local skills_to_refresh = self.refresh_skill_table[event] or Util.DummyTable
|
local skills_to_refresh = self.refresh_skill_table[event] or Util.DummyTable
|
||||||
local _target = room.current -- for iteration
|
local _target = room.current -- for iteration
|
||||||
local player = _target
|
local player = _target
|
||||||
|
if #skills_to_refresh > 0 then
|
||||||
if #skills_to_refresh > 0 then repeat do
|
repeat
|
||||||
-- refresh skills. This should not be broken
|
do
|
||||||
for _, skill in ipairs(skills_to_refresh) do
|
-- refresh skills. This should not be broken
|
||||||
if skill:canRefresh(event, target, player, data) then
|
for _, skill in ipairs(skills_to_refresh) do
|
||||||
skill:refresh(event, target, player, data)
|
if skill:canRefresh(event, target, player, data) then
|
||||||
end
|
skill:refresh(event, target, player, data)
|
||||||
end
|
end
|
||||||
player = player.next
|
end
|
||||||
end until player == _target end
|
player = player.next
|
||||||
|
end
|
||||||
if #skills == 0 or refresh_only then return end
|
until player == _target
|
||||||
|
|
||||||
local prio_tab = self.skill_priority_table[event]
|
|
||||||
local prev_prio = math.huge
|
|
||||||
|
|
||||||
for _, prio in ipairs(prio_tab) do
|
|
||||||
if broken then break end
|
|
||||||
if prio >= prev_prio then
|
|
||||||
-- continue
|
|
||||||
goto trigger_loop_continue
|
|
||||||
end
|
end
|
||||||
|
|
||||||
repeat do
|
if #skills == 0 or refresh_only then
|
||||||
local invoked_skills = {}
|
return
|
||||||
local filter_func = function(skill)
|
end
|
||||||
return skill.priority_table[event] == prio and
|
|
||||||
not table.contains(invoked_skills, skill) and
|
|
||||||
skill:triggerable(event, target, player, data)
|
|
||||||
end
|
|
||||||
|
|
||||||
local skill_names = table.map(table.filter(skills, filter_func), Util.NameMapper)
|
local prio_tab = self.skill_priority_table[event]
|
||||||
|
local prev_prio = math.huge
|
||||||
|
|
||||||
while #skill_names > 0 do
|
for _, prio in ipairs(prio_tab) do
|
||||||
local skill_name = prio <= 0 and table.random(skill_names) or
|
if broken then
|
||||||
room:askForChoice(player, skill_names, "trigger", "#choose-trigger")
|
break
|
||||||
|
end
|
||||||
|
if prio >= prev_prio then
|
||||||
|
-- continue
|
||||||
|
goto trigger_loop_continue
|
||||||
|
end
|
||||||
|
|
||||||
local skill = skill_name == "game_rule" and GameRule
|
repeat
|
||||||
or Fk.skills[skill_name]
|
do
|
||||||
|
local triggerables = table.filter(skills, function(skill)
|
||||||
|
return skill.priority_table[event] == prio and skill:triggerable(event, target, player, data)
|
||||||
|
end)
|
||||||
|
|
||||||
table.insert(invoked_skills, skill)
|
local skill_names = table.map(triggerables, function(skill)
|
||||||
broken = skill:trigger(event, target, player, data)
|
return skill.name
|
||||||
skill_names = table.map(table.filter(skills, filter_func), Util.NameMapper)
|
end)
|
||||||
|
|
||||||
broken = broken or (event == fk.AskForPeaches
|
while #skill_names > 0 do
|
||||||
and room:getPlayerById(data.who).hp > 0)
|
local skill_name = prio <= 0 and table.random(skill_names) or
|
||||||
|
room:askForChoice(player, skill_names, "trigger", "#choose-trigger")
|
||||||
|
|
||||||
if broken then break end
|
local skill = skill_name == "game_rule" and GameRule or Fk.skills[skill_name]
|
||||||
end
|
|
||||||
|
|
||||||
if broken then break end
|
local len = #skills
|
||||||
|
broken = skill:trigger(event, target, player, data)
|
||||||
|
|
||||||
player = player.next
|
table.insertTable(skill_names,
|
||||||
end until player == _target
|
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))
|
||||||
|
|
||||||
prev_prio = prio
|
broken = broken or (event == fk.AskForPeaches and room:getPlayerById(data.who).hp > 0)
|
||||||
::trigger_loop_continue::
|
|
||||||
end
|
|
||||||
|
|
||||||
return broken
|
if broken then
|
||||||
|
break
|
||||||
|
end
|
||||||
|
table.removeOne(skill_names, skill_name)
|
||||||
|
end
|
||||||
|
if broken then
|
||||||
|
break
|
||||||
|
end
|
||||||
|
player = player.next
|
||||||
|
end
|
||||||
|
until player == _target
|
||||||
|
|
||||||
|
prev_prio = prio
|
||||||
|
::trigger_loop_continue::
|
||||||
|
end
|
||||||
|
_target.ai:filterEvent(event, target, data)
|
||||||
|
return broken
|
||||||
end
|
end
|
||||||
|
|
||||||
---@return GameEvent
|
---@return GameEvent
|
||||||
function GameLogic:getCurrentEvent()
|
function GameLogic:getCurrentEvent()
|
||||||
return self.game_event_stack.t[self.game_event_stack.p]
|
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
|
end
|
||||||
|
|
||||||
-- 在指定历史范围中找至多n个符合条件的事件
|
-- 在指定历史范围中找至多n个符合条件的事件
|
||||||
|
@ -438,85 +440,90 @@ end
|
||||||
---@param scope integer @ 查询历史范围,只能是当前阶段/回合/轮次
|
---@param scope integer @ 查询历史范围,只能是当前阶段/回合/轮次
|
||||||
---@return GameEvent[] @ 找到的符合条件的所有事件,最多n个但不保证有n个
|
---@return GameEvent[] @ 找到的符合条件的所有事件,最多n个但不保证有n个
|
||||||
function GameLogic:getEventsOfScope(eventType, n, func, scope)
|
function GameLogic:getEventsOfScope(eventType, n, func, scope)
|
||||||
scope = scope or Player.HistoryTurn
|
scope = scope or Player.HistoryTurn
|
||||||
local event = self:getCurrentEvent()
|
local event = self:getCurrentEvent()
|
||||||
local start_event ---@type GameEvent
|
local start_event ---@type GameEvent
|
||||||
if scope == Player.HistoryGame then
|
if scope == Player.HistoryGame then
|
||||||
start_event = self.all_game_events[1]
|
start_event = self.all_game_events[1]
|
||||||
elseif scope == Player.HistoryRound then
|
elseif scope == Player.HistoryRound then
|
||||||
start_event = event:findParent(GameEvent.Round, true)
|
start_event = event:findParent(GameEvent.Round, true)
|
||||||
elseif scope == Player.HistoryTurn then
|
elseif scope == Player.HistoryTurn then
|
||||||
start_event = event:findParent(GameEvent.Turn, true)
|
start_event = event:findParent(GameEvent.Turn, true)
|
||||||
elseif scope == Player.HistoryPhase then
|
elseif scope == Player.HistoryPhase then
|
||||||
start_event = event:findParent(GameEvent.Phase, true)
|
start_event = event:findParent(GameEvent.Phase, true)
|
||||||
end
|
end
|
||||||
|
|
||||||
return start_event:searchEvents(eventType, n, func)
|
return start_event:searchEvents(eventType, n, func)
|
||||||
end
|
end
|
||||||
|
|
||||||
function GameLogic:dumpEventStack(detailed)
|
function GameLogic:dumpEventStack(detailed)
|
||||||
local top = self:getCurrentEvent()
|
local top = self:getCurrentEvent()
|
||||||
local i = self.game_event_stack.p
|
local i = self.game_event_stack.p
|
||||||
local inspect = p
|
local inspect = p
|
||||||
if not top then return end
|
if not top then
|
||||||
|
return
|
||||||
print("===== Start of event stack dump =====")
|
|
||||||
if not detailed then print("") end
|
|
||||||
|
|
||||||
repeat
|
|
||||||
local printable_data
|
|
||||||
if type(top.data) ~= "table" then
|
|
||||||
printable_data = top.data
|
|
||||||
else
|
|
||||||
printable_data = table.cloneWithoutClass(top.data)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
print("===== Start of event stack dump =====")
|
||||||
if not detailed then
|
if not detailed then
|
||||||
print("Stack level #" .. i .. ": " .. tostring(top))
|
print("")
|
||||||
else
|
|
||||||
print("\nStack level #" .. i .. ":")
|
|
||||||
inspect{
|
|
||||||
eventId = GameEvent:translate(top.event),
|
|
||||||
data = printable_data or "nil",
|
|
||||||
}
|
|
||||||
end
|
end
|
||||||
|
|
||||||
top = top.parent
|
repeat
|
||||||
i = i - 1
|
local printable_data
|
||||||
until not top
|
if type(top.data) ~= "table" then
|
||||||
|
printable_data = top.data
|
||||||
|
else
|
||||||
|
printable_data = table.cloneWithoutClass(top.data)
|
||||||
|
end
|
||||||
|
|
||||||
print("\n===== End of event stack dump =====")
|
if not detailed then
|
||||||
|
print("Stack level #" .. i .. ": " .. tostring(top))
|
||||||
|
else
|
||||||
|
print("\nStack level #" .. i .. ":")
|
||||||
|
inspect {
|
||||||
|
eventId = GameEvent:translate(top.event),
|
||||||
|
data = printable_data or "nil"
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
top = top.parent
|
||||||
|
i = i - 1
|
||||||
|
until not top
|
||||||
|
|
||||||
|
print("\n===== End of event stack dump =====")
|
||||||
end
|
end
|
||||||
|
|
||||||
function GameLogic:dumpAllEvents(from, to)
|
function GameLogic:dumpAllEvents(from, to)
|
||||||
from = from or 1
|
from = from or 1
|
||||||
to = to or #self.all_game_events
|
to = to or #self.all_game_events
|
||||||
assert(from <= to)
|
assert(from <= to)
|
||||||
|
|
||||||
local indent = 0
|
local indent = 0
|
||||||
local tab = " "
|
local tab = " "
|
||||||
for i = from, to, 1 do
|
for i = from, to, 1 do
|
||||||
local v = self.all_game_events[i]
|
local v = self.all_game_events[i]
|
||||||
if type(v) ~= "table" then
|
if type(v) ~= "table" then
|
||||||
indent = math.max(indent - 1, 0)
|
indent = math.max(indent - 1, 0)
|
||||||
-- v = "End"
|
-- v = "End"
|
||||||
-- print(tab:rep(indent) .. string.format("#%d: %s", i, v))
|
-- print(tab:rep(indent) .. string.format("#%d: %s", i, v))
|
||||||
else
|
else
|
||||||
print(tab:rep(indent) .. string.format("%s", tostring(v)))
|
print(tab:rep(indent) .. string.format("%s", tostring(v)))
|
||||||
if v.id ~= v.end_id then
|
if v.id ~= v.end_id then
|
||||||
indent = indent + 1
|
indent = indent + 1
|
||||||
end
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
function GameLogic:breakEvent(ret)
|
function GameLogic:breakEvent(ret)
|
||||||
coroutine.yield("__breakEvent", ret)
|
self.room.breakEvent = true
|
||||||
|
coroutine.yield("__breakEvent", ret)
|
||||||
end
|
end
|
||||||
|
|
||||||
function GameLogic:breakTurn()
|
function GameLogic:breakTurn()
|
||||||
local event = self:getCurrentEvent():findParent(GameEvent.Turn)
|
local event = self:getCurrentEvent():findParent(GameEvent.Turn)
|
||||||
event:shutdown()
|
event:shutdown()
|
||||||
end
|
end
|
||||||
|
|
||||||
return GameLogic
|
return GameLogic
|
||||||
|
|
1405
lua/server/room.lua
1405
lua/server/room.lua
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,46 @@
|
||||||
|
diff a/lua/server/room.lua b/lua/server/room.lua (rejected hunks)
|
||||||
|
@@ -1482,41 +1482,18 @@
|
||||||
|
local result = self:doRequest(chooser, command, json.encode(data))
|
||||||
|
|
||||||
|
if result == "" then
|
||||||
|
- local areas = {}
|
||||||
|
- local handcards
|
||||||
|
+ 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)
|
||||||
|
+ handcards = target:getCardIds(flag)
|
||||||
|
else
|
||||||
|
- handcards = {}
|
||||||
|
for _, t in ipairs(flag.card_data) do
|
||||||
|
table.insertTable(handcards, t[2])
|
||||||
|
end
|
||||||
|
end
|
||||||
|
- if #handcards == 0 then
|
||||||
|
- return
|
||||||
|
- end
|
||||||
|
- result = handcards[math.random(1, #handcards)]
|
||||||
|
+ result = handcards[math.random(1, #handcards)] or -1
|
||||||
|
else
|
||||||
|
result = tonumber(result)
|
||||||
|
end
|
||||||
|
-
|
||||||
|
- if result == -1 then
|
||||||
|
- local handcards = target:getCardIds(Player.Hand)
|
||||||
|
- if #handcards == 0 then
|
||||||
|
- return
|
||||||
|
- end
|
||||||
|
- result = table.random(handcards)
|
||||||
|
- end
|
||||||
|
-
|
||||||
|
return result
|
||||||
|
end
|
||||||
|
|
|
@ -24,8 +24,8 @@ local ServerPlayer = Player:subclass("ServerPlayer")
|
||||||
|
|
||||||
function ServerPlayer:initialize(_self)
|
function ServerPlayer:initialize(_self)
|
||||||
Player.initialize(self)
|
Player.initialize(self)
|
||||||
self.serverplayer = _self -- 控制者
|
self.serverplayer = _self -- 控制者
|
||||||
self._splayer = _self -- 真正在玩的玩家
|
self._splayer = _self -- 真正在玩的玩家
|
||||||
self._observers = { _self } -- "旁观"中的玩家,然而不包括真正的旁观者
|
self._observers = { _self } -- "旁观"中的玩家,然而不包括真正的旁观者
|
||||||
self.id = _self:getId()
|
self.id = _self:getId()
|
||||||
self.room = nil
|
self.room = nil
|
||||||
|
@ -45,7 +45,7 @@ function ServerPlayer:initialize(_self)
|
||||||
self._prelighted_skills = {}
|
self._prelighted_skills = {}
|
||||||
|
|
||||||
self._timewaste_count = 0
|
self._timewaste_count = 0
|
||||||
self.ai = RandomAI:new(self)
|
self.ai = SmartAI:new(self)
|
||||||
end
|
end
|
||||||
|
|
||||||
---@param command string
|
---@param command string
|
||||||
|
@ -284,25 +284,25 @@ function ServerPlayer:marshal(player, observe)
|
||||||
end
|
end
|
||||||
|
|
||||||
for k, v in pairs(self.mark) do
|
for k, v in pairs(self.mark) do
|
||||||
player:doNotify("SetPlayerMark", json.encode{self.id, k, v})
|
player:doNotify("SetPlayerMark", json.encode { self.id, k, v })
|
||||||
end
|
end
|
||||||
|
|
||||||
for _, s in ipairs(self.player_skills) do
|
for _, s in ipairs(self.player_skills) do
|
||||||
player:doNotify("AddSkill", json.encode{self.id, s.name})
|
player:doNotify("AddSkill", json.encode { self.id, s.name })
|
||||||
end
|
end
|
||||||
|
|
||||||
for k, v in pairs(self.cardUsedHistory) do
|
for k, v in pairs(self.cardUsedHistory) do
|
||||||
if v[1] > 0 then
|
if v[1] > 0 then
|
||||||
player:doNotify("AddCardUseHistory", json.encode{k, v[1]})
|
player:doNotify("AddCardUseHistory", json.encode { k, v[1] })
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
for k, v in pairs(self.skillUsedHistory) do
|
for k, v in pairs(self.skillUsedHistory) do
|
||||||
if v[4] > 0 then
|
if v[4] > 0 then
|
||||||
player:doNotify("SetSkillUseHistory", json.encode{self.id, k, v[1], 1})
|
player:doNotify("SetSkillUseHistory", json.encode { self.id, k, v[1], 1 })
|
||||||
player:doNotify("SetSkillUseHistory", json.encode{self.id, k, v[2], 2})
|
player:doNotify("SetSkillUseHistory", json.encode { self.id, k, v[2], 2 })
|
||||||
player:doNotify("SetSkillUseHistory", json.encode{self.id, k, v[3], 3})
|
player:doNotify("SetSkillUseHistory", json.encode { self.id, k, v[3], 3 })
|
||||||
player:doNotify("SetSkillUseHistory", json.encode{self.id, k, v[4], 4})
|
player:doNotify("SetSkillUseHistory", json.encode { self.id, k, v[4], 4 })
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -319,13 +319,13 @@ function ServerPlayer:reconnect()
|
||||||
local room = self.room
|
local room = self.room
|
||||||
self.serverplayer:setState(fk.Player_Online)
|
self.serverplayer:setState(fk.Player_Online)
|
||||||
|
|
||||||
self:doNotify("Setup", json.encode{
|
self:doNotify("Setup", json.encode {
|
||||||
self.id,
|
self.id,
|
||||||
self._splayer:getScreenName(),
|
self._splayer:getScreenName(),
|
||||||
self._splayer:getAvatar(),
|
self._splayer:getAvatar(),
|
||||||
})
|
})
|
||||||
self:doNotify("EnterLobby", "")
|
self:doNotify("EnterLobby", "")
|
||||||
self:doNotify("EnterRoom", json.encode{
|
self:doNotify("EnterRoom", json.encode {
|
||||||
#room.players, room.timeout, room.settings,
|
#room.players, room.timeout, room.settings,
|
||||||
})
|
})
|
||||||
self:doNotify("StartGame", "")
|
self:doNotify("StartGame", "")
|
||||||
|
@ -333,7 +333,7 @@ function ServerPlayer:reconnect()
|
||||||
|
|
||||||
-- send player data
|
-- send player data
|
||||||
for _, p in ipairs(room:getOtherPlayers(self, false, true)) do
|
for _, p in ipairs(room:getOtherPlayers(self, false, true)) do
|
||||||
self:doNotify("AddPlayer", json.encode{
|
self:doNotify("AddPlayer", json.encode {
|
||||||
p.id,
|
p.id,
|
||||||
p._splayer:getScreenName(),
|
p._splayer:getScreenName(),
|
||||||
p._splayer:getAvatar(),
|
p._splayer:getAvatar(),
|
||||||
|
@ -350,13 +350,13 @@ function ServerPlayer:reconnect()
|
||||||
for i = -2, -math.huge, -1 do
|
for i = -2, -math.huge, -1 do
|
||||||
local c = Fk.printed_cards[i]
|
local c = Fk.printed_cards[i]
|
||||||
if not c then break end
|
if not c then break end
|
||||||
self:doNotify("PrintCard", json.encode{ c.name, c.suit, c.number })
|
self:doNotify("PrintCard", json.encode { c.name, c.suit, c.number })
|
||||||
end
|
end
|
||||||
|
|
||||||
-- send card marks
|
-- send card marks
|
||||||
for id, marks in pairs(room.card_marks) do
|
for id, marks in pairs(room.card_marks) do
|
||||||
for k, v in pairs(marks) do
|
for k, v in pairs(marks) do
|
||||||
self:doNotify("SetCardMark", json.encode{ id, k, v })
|
self:doNotify("SetCardMark", json.encode { id, k, v })
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -371,9 +371,9 @@ function ServerPlayer:reconnect()
|
||||||
|
|
||||||
-- send fake skills
|
-- send fake skills
|
||||||
for _, s in ipairs(self._manually_fake_skills) do
|
for _, s in ipairs(self._manually_fake_skills) do
|
||||||
self:doNotify("AddSkill", json.encode{ self.id, s.name, true })
|
self:doNotify("AddSkill", json.encode { self.id, s.name, true })
|
||||||
if table.contains(self.prelighted_skills, s) then
|
if table.contains(self.prelighted_skills, s) then
|
||||||
self:doNotify("PrelightSkill", json.encode{ s.name, true })
|
self:doNotify("PrelightSkill", json.encode { s.name, true })
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -392,7 +392,7 @@ function ServerPlayer:turnOver()
|
||||||
self.faceup = not self.faceup
|
self.faceup = not self.faceup
|
||||||
self.room:broadcastProperty(self, "faceup")
|
self.room:broadcastProperty(self, "faceup")
|
||||||
|
|
||||||
self.room:sendLog{
|
self.room:sendLog {
|
||||||
type = "#TurnOver",
|
type = "#TurnOver",
|
||||||
from = self.id,
|
from = self.id,
|
||||||
arg = self.faceup and "face_up" or "face_down",
|
arg = self.faceup and "face_up" or "face_down",
|
||||||
|
@ -408,12 +408,12 @@ function ServerPlayer:showCards(cards)
|
||||||
end
|
end
|
||||||
|
|
||||||
local room = self.room
|
local room = self.room
|
||||||
room:sendLog{
|
room:sendLog {
|
||||||
type = "#ShowCard",
|
type = "#ShowCard",
|
||||||
from = self.id,
|
from = self.id,
|
||||||
card = cards,
|
card = cards,
|
||||||
}
|
}
|
||||||
room:doBroadcastNotify("ShowCard", json.encode{
|
room:doBroadcastNotify("ShowCard", json.encode {
|
||||||
from = self.id,
|
from = self.id,
|
||||||
cards = cards,
|
cards = cards,
|
||||||
})
|
})
|
||||||
|
@ -469,7 +469,7 @@ function ServerPlayer:gainAnExtraPhase(phase, delay)
|
||||||
local logic = room.logic
|
local logic = room.logic
|
||||||
local turn = logic:getCurrentEvent():findParent(GameEvent.Phase, true)
|
local turn = logic:getCurrentEvent():findParent(GameEvent.Phase, true)
|
||||||
if turn then
|
if turn then
|
||||||
turn:prependExitFunc(function() self:gainAnExtraPhase(phase, false) end)
|
turn:addExitFunc(function() self:gainAnExtraPhase(phase, false) end)
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -478,12 +478,13 @@ function ServerPlayer:gainAnExtraPhase(phase, delay)
|
||||||
self.phase = phase
|
self.phase = phase
|
||||||
room:broadcastProperty(self, "phase")
|
room:broadcastProperty(self, "phase")
|
||||||
|
|
||||||
room:sendLog{
|
room:sendLog {
|
||||||
type = "#GainAnExtraPhase",
|
type = "#GainAnExtraPhase",
|
||||||
from = self.id,
|
from = self.id,
|
||||||
arg = phase_name_table[phase],
|
arg = phase_name_table[phase],
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
GameEvent(GameEvent.Phase, self, self.phase):exec()
|
GameEvent(GameEvent.Phase, self, self.phase):exec()
|
||||||
|
|
||||||
self.phase = current
|
self.phase = current
|
||||||
|
@ -552,7 +553,7 @@ function ServerPlayer:play(phase_table)
|
||||||
if (not skip) or (cancel_skip) then
|
if (not skip) or (cancel_skip) then
|
||||||
GameEvent(GameEvent.Phase, self, self.phase):exec()
|
GameEvent(GameEvent.Phase, self, self.phase):exec()
|
||||||
else
|
else
|
||||||
room:sendLog{
|
room:sendLog {
|
||||||
type = "#PhaseSkipped",
|
type = "#PhaseSkipped",
|
||||||
from = self.id,
|
from = self.id,
|
||||||
arg = phase_name_table[self.phase],
|
arg = phase_name_table[self.phase],
|
||||||
|
@ -564,11 +565,11 @@ end
|
||||||
---@param phase Phase
|
---@param phase Phase
|
||||||
function ServerPlayer:skip(phase)
|
function ServerPlayer:skip(phase)
|
||||||
if not table.contains({
|
if not table.contains({
|
||||||
Player.Judge,
|
Player.Judge,
|
||||||
Player.Draw,
|
Player.Draw,
|
||||||
Player.Play,
|
Player.Play,
|
||||||
Player.Discard
|
Player.Discard
|
||||||
}, phase) then
|
}, phase) then
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
self.skipped_phases[phase] = true
|
self.skipped_phases[phase] = true
|
||||||
|
@ -579,7 +580,6 @@ function ServerPlayer:skip(phase)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
--- 当进行到出牌阶段空闲点时,结束出牌阶段。
|
|
||||||
function ServerPlayer:endPlayPhase()
|
function ServerPlayer:endPlayPhase()
|
||||||
self._play_phase_end = true
|
self._play_phase_end = true
|
||||||
-- TODO: send log
|
-- TODO: send log
|
||||||
|
@ -592,44 +592,22 @@ function ServerPlayer:gainAnExtraTurn(delay)
|
||||||
local logic = room.logic
|
local logic = room.logic
|
||||||
local turn = logic:getCurrentEvent():findParent(GameEvent.Turn, true)
|
local turn = logic:getCurrentEvent():findParent(GameEvent.Turn, true)
|
||||||
if turn then
|
if turn then
|
||||||
turn:prependExitFunc(function() self:gainAnExtraTurn(false) end)
|
turn:addExitFunc(function() self:gainAnExtraTurn(false) end)
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
room:sendLog{
|
room:sendLog {
|
||||||
type = "#GainAnExtraTurn",
|
type = "#GainAnExtraTurn",
|
||||||
from = self.id
|
from = self.id
|
||||||
}
|
}
|
||||||
|
|
||||||
local current = room.current
|
local current = room.current
|
||||||
room.current = self
|
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()
|
GameEvent(GameEvent.Turn, self):exec()
|
||||||
|
|
||||||
table.remove(ex_tag)
|
|
||||||
|
|
||||||
room.current = current
|
room.current = current
|
||||||
end
|
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)
|
function ServerPlayer:drawCards(num, skillName, fromPlace)
|
||||||
return self.room:drawCards(self, num, skillName, fromPlace)
|
return self.room:drawCards(self, num, skillName, fromPlace)
|
||||||
end
|
end
|
||||||
|
@ -686,7 +664,7 @@ end
|
||||||
|
|
||||||
function ServerPlayer:addVirtualEquip(card)
|
function ServerPlayer:addVirtualEquip(card)
|
||||||
Player.addVirtualEquip(self, card)
|
Player.addVirtualEquip(self, card)
|
||||||
self.room:doBroadcastNotify("AddVirtualEquip", json.encode{
|
self.room:doBroadcastNotify("AddVirtualEquip", json.encode {
|
||||||
player = self.id,
|
player = self.id,
|
||||||
name = card.name,
|
name = card.name,
|
||||||
subcards = card.subcards,
|
subcards = card.subcards,
|
||||||
|
@ -695,7 +673,7 @@ end
|
||||||
|
|
||||||
function ServerPlayer:removeVirtualEquip(cid)
|
function ServerPlayer:removeVirtualEquip(cid)
|
||||||
local ret = Player.removeVirtualEquip(self, cid)
|
local ret = Player.removeVirtualEquip(self, cid)
|
||||||
self.room:doBroadcastNotify("RemoveVirtualEquip", json.encode{
|
self.room:doBroadcastNotify("RemoveVirtualEquip", json.encode {
|
||||||
player = self.id,
|
player = self.id,
|
||||||
id = cid,
|
id = cid,
|
||||||
})
|
})
|
||||||
|
@ -704,22 +682,22 @@ end
|
||||||
|
|
||||||
function ServerPlayer:addCardUseHistory(cardName, num)
|
function ServerPlayer:addCardUseHistory(cardName, num)
|
||||||
Player.addCardUseHistory(self, cardName, num)
|
Player.addCardUseHistory(self, cardName, num)
|
||||||
self:doNotify("AddCardUseHistory", json.encode{cardName, num})
|
self:doNotify("AddCardUseHistory", json.encode { cardName, num })
|
||||||
end
|
end
|
||||||
|
|
||||||
function ServerPlayer:setCardUseHistory(cardName, num, scope)
|
function ServerPlayer:setCardUseHistory(cardName, num, scope)
|
||||||
Player.setCardUseHistory(self, cardName, num, scope)
|
Player.setCardUseHistory(self, cardName, num, scope)
|
||||||
self:doNotify("SetCardUseHistory", json.encode{cardName, num, scope})
|
self:doNotify("SetCardUseHistory", json.encode { cardName, num, scope })
|
||||||
end
|
end
|
||||||
|
|
||||||
function ServerPlayer:addSkillUseHistory(cardName, num)
|
function ServerPlayer:addSkillUseHistory(cardName, num)
|
||||||
Player.addSkillUseHistory(self, cardName, num)
|
Player.addSkillUseHistory(self, cardName, num)
|
||||||
self.room:doBroadcastNotify("AddSkillUseHistory", json.encode{self.id, cardName, num})
|
self.room:doBroadcastNotify("AddSkillUseHistory", json.encode { self.id, cardName, num })
|
||||||
end
|
end
|
||||||
|
|
||||||
function ServerPlayer:setSkillUseHistory(cardName, num, scope)
|
function ServerPlayer:setSkillUseHistory(cardName, num, scope)
|
||||||
Player.setSkillUseHistory(self, cardName, num, scope)
|
Player.setSkillUseHistory(self, cardName, num, scope)
|
||||||
self.room:doBroadcastNotify("SetSkillUseHistory", json.encode{self.id, cardName, num, scope})
|
self.room:doBroadcastNotify("SetSkillUseHistory", json.encode { self.id, cardName, num, scope })
|
||||||
end
|
end
|
||||||
|
|
||||||
---@param chained boolean
|
---@param chained boolean
|
||||||
|
@ -732,7 +710,7 @@ function ServerPlayer:setChainState(chained)
|
||||||
self.chained = chained
|
self.chained = chained
|
||||||
|
|
||||||
room:broadcastProperty(self, "chained")
|
room:broadcastProperty(self, "chained")
|
||||||
room:sendLog{
|
room:sendLog {
|
||||||
type = "#ChainStateChange",
|
type = "#ChainStateChange",
|
||||||
from = self.id,
|
from = self.id,
|
||||||
arg = self.chained and "chained" or "un-chained"
|
arg = self.chained and "chained" or "un-chained"
|
||||||
|
@ -743,7 +721,7 @@ function ServerPlayer:setChainState(chained)
|
||||||
end
|
end
|
||||||
|
|
||||||
function ServerPlayer:reset()
|
function ServerPlayer:reset()
|
||||||
self.room:sendLog{
|
self.room:sendLog {
|
||||||
type = "#ChainStateChange",
|
type = "#ChainStateChange",
|
||||||
from = self.id,
|
from = self.id,
|
||||||
arg = "reset-general"
|
arg = "reset-general"
|
||||||
|
@ -792,12 +770,12 @@ function ServerPlayer:addFakeSkill(skill)
|
||||||
table.insert(self._fake_skills, skill)
|
table.insert(self._fake_skills, skill)
|
||||||
for _, s in ipairs(skill.related_skills) do
|
for _, s in ipairs(skill.related_skills) do
|
||||||
-- if s.main_skill == skill then -- TODO: need more detailed
|
-- if s.main_skill == skill then -- TODO: need more detailed
|
||||||
table.insert(self._fake_skills, s)
|
table.insert(self._fake_skills, s)
|
||||||
-- end
|
-- end
|
||||||
end
|
end
|
||||||
|
|
||||||
-- TODO
|
-- TODO
|
||||||
self:doNotify("AddSkill", json.encode{ self.id, skill.name, true })
|
self:doNotify("AddSkill", json.encode { self.id, skill.name, true })
|
||||||
end
|
end
|
||||||
|
|
||||||
---@param skill Skill
|
---@param skill Skill
|
||||||
|
@ -816,7 +794,7 @@ function ServerPlayer:loseFakeSkill(skill)
|
||||||
end
|
end
|
||||||
|
|
||||||
-- TODO
|
-- TODO
|
||||||
self:doNotify("LoseSkill", json.encode{ self.id, skill.name, true })
|
self:doNotify("LoseSkill", json.encode { self.id, skill.name, true })
|
||||||
end
|
end
|
||||||
|
|
||||||
function ServerPlayer:isFakeSkill(skill)
|
function ServerPlayer:isFakeSkill(skill)
|
||||||
|
@ -852,7 +830,7 @@ function ServerPlayer:prelightSkill(skill, isPrelight)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
self:doNotify("PrelightSkill", json.encode{ skill.name, isPrelight })
|
self:doNotify("PrelightSkill", json.encode { skill.name, isPrelight })
|
||||||
end
|
end
|
||||||
|
|
||||||
---@param isDeputy bool
|
---@param isDeputy bool
|
||||||
|
@ -876,7 +854,8 @@ function ServerPlayer:revealGeneral(isDeputy, no_trigger)
|
||||||
|
|
||||||
local ret = true
|
local ret = true
|
||||||
if not ((isDeputy and self.general ~= "anjiang") or (not isDeputy and self.deputyGeneral ~= "anjiang")) then
|
if not ((isDeputy and self.general ~= "anjiang") or (not isDeputy and self.deputyGeneral ~= "anjiang")) then
|
||||||
local other = Fk.generals[self:getMark(isDeputy and "__heg_general" or "__heg_deputy")] or Fk.generals["blank_shibing"]
|
local other = Fk.generals[self:getMark(isDeputy and "__heg_general" or "__heg_deputy")] or
|
||||||
|
Fk.generals["blank_shibing"]
|
||||||
for _, sname in ipairs(other:getSkillNameList()) do
|
for _, sname in ipairs(other:getSkillNameList()) do
|
||||||
local s = Fk.skills[sname]
|
local s = Fk.skills[sname]
|
||||||
if s.frequency == Skill.Compulsory and s.relate_to_place ~= (isDeputy and "m" or "d") then
|
if s.frequency == Skill.Compulsory and s.relate_to_place ~= (isDeputy and "m" or "d") then
|
||||||
|
@ -895,9 +874,9 @@ function ServerPlayer:revealGeneral(isDeputy, no_trigger)
|
||||||
local kingdom = general.kingdom
|
local kingdom = general.kingdom
|
||||||
self.kingdom = kingdom
|
self.kingdom = kingdom
|
||||||
if oldKingdom == "unknown" and #table.filter(room:getOtherPlayers(self, false, true),
|
if oldKingdom == "unknown" and #table.filter(room:getOtherPlayers(self, false, true),
|
||||||
function(p)
|
function(p)
|
||||||
return p.kingdom == kingdom
|
return p.kingdom == kingdom
|
||||||
end) >= #room.players // 2 then
|
end) >= #room.players // 2 then
|
||||||
self.kingdom = "wild"
|
self.kingdom = "wild"
|
||||||
end
|
end
|
||||||
room:broadcastProperty(self, "kingdom")
|
room:broadcastProperty(self, "kingdom")
|
||||||
|
@ -909,7 +888,7 @@ function ServerPlayer:revealGeneral(isDeputy, no_trigger)
|
||||||
self.gender = general.gender
|
self.gender = general.gender
|
||||||
end
|
end
|
||||||
|
|
||||||
room:sendLog{
|
room:sendLog {
|
||||||
type = "#RevealGeneral",
|
type = "#RevealGeneral",
|
||||||
from = self.id,
|
from = self.id,
|
||||||
arg = isDeputy and "deputyGeneral" or "mainGeneral",
|
arg = isDeputy and "deputyGeneral" or "mainGeneral",
|
||||||
|
@ -927,7 +906,7 @@ function ServerPlayer:revealBySkillName(skill_name)
|
||||||
|
|
||||||
if main then
|
if main then
|
||||||
if table.contains(Fk.generals[self:getMark("__heg_general")]
|
if table.contains(Fk.generals[self:getMark("__heg_general")]
|
||||||
:getSkillNameList(), skill_name) then
|
:getSkillNameList(), skill_name) then
|
||||||
self:revealGeneral(false)
|
self:revealGeneral(false)
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
@ -935,7 +914,7 @@ function ServerPlayer:revealBySkillName(skill_name)
|
||||||
|
|
||||||
if deputy then
|
if deputy then
|
||||||
if table.contains(Fk.generals[self:getMark("__heg_deputy")]
|
if table.contains(Fk.generals[self:getMark("__heg_deputy")]
|
||||||
:getSkillNameList(), skill_name) then
|
:getSkillNameList(), skill_name) then
|
||||||
self:revealGeneral(true)
|
self:revealGeneral(true)
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
@ -948,7 +927,7 @@ function ServerPlayer:hideGeneral(isDeputy)
|
||||||
local mark = isDeputy and "__heg_deputy" or "__heg_general"
|
local mark = isDeputy and "__heg_deputy" or "__heg_general"
|
||||||
|
|
||||||
self:setMark(mark, generalName)
|
self:setMark(mark, generalName)
|
||||||
self:doNotify("SetPlayerMark", json.encode{ self.id, mark, generalName})
|
self:doNotify("SetPlayerMark", json.encode { self.id, mark, generalName })
|
||||||
|
|
||||||
if isDeputy then
|
if isDeputy then
|
||||||
room:setDeputyGeneral(self, "anjiang")
|
room:setDeputyGeneral(self, "anjiang")
|
||||||
|
@ -983,6 +962,7 @@ function ServerPlayer:hideGeneral(isDeputy)
|
||||||
|
|
||||||
room.logic:trigger(fk.GeneralHidden, room, generalName)
|
room.logic:trigger(fk.GeneralHidden, room, generalName)
|
||||||
end
|
end
|
||||||
|
|
||||||
-- 神貂蝉
|
-- 神貂蝉
|
||||||
|
|
||||||
---@param p ServerPlayer
|
---@param p ServerPlayer
|
||||||
|
@ -1002,7 +982,7 @@ function ServerPlayer:addBuddy(other)
|
||||||
other = self.room:getPlayerById(other)
|
other = self.room:getPlayerById(other)
|
||||||
end
|
end
|
||||||
Player.addBuddy(self, other)
|
Player.addBuddy(self, other)
|
||||||
self:doNotify("AddBuddy", json.encode{ other.id, other.player_cards[Player.Hand] })
|
self:doNotify("AddBuddy", json.encode { other.id, other.player_cards[Player.Hand] })
|
||||||
end
|
end
|
||||||
|
|
||||||
function ServerPlayer:removeBuddy(other)
|
function ServerPlayer:removeBuddy(other)
|
||||||
|
@ -1013,4 +993,8 @@ function ServerPlayer:removeBuddy(other)
|
||||||
self:doNotify("RmBuddy", tostring(other.id))
|
self:doNotify("RmBuddy", tostring(other.id))
|
||||||
end
|
end
|
||||||
|
|
||||||
|
function ServerPlayer:getAI()
|
||||||
|
return self.ai
|
||||||
|
end
|
||||||
|
|
||||||
return ServerPlayer
|
return ServerPlayer
|
||||||
|
|
|
@ -1,481 +1,448 @@
|
||||||
-- SPDX-License-Identifier: GPL-3.0-or-later
|
-- SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
local extension = Package:new("maneuvering", Package.CardPack)
|
local extension = Package:new("maneuvering", Package.CardPack)
|
||||||
|
|
||||||
local slash = Fk:cloneCard("slash")
|
local slash = Fk:cloneCard("slash")
|
||||||
|
|
||||||
local thunderSlashSkill = fk.CreateActiveSkill{
|
local thunderSlashSkill = fk.CreateActiveSkill {
|
||||||
name = "thunder__slash_skill",
|
name = "thunder__slash_skill",
|
||||||
max_phase_use_time = 1,
|
max_phase_use_time = 1,
|
||||||
target_num = 1,
|
target_num = 1,
|
||||||
can_use = slash.skill.canUse,
|
can_use = slash.skill.canUse,
|
||||||
mod_target_filter = slash.skill.modTargetFilter,
|
mod_target_filter = slash.skill.modTargetFilter,
|
||||||
target_filter = slash.skill.targetFilter,
|
target_filter = slash.skill.targetFilter,
|
||||||
on_effect = function(self, room, effect)
|
on_effect = function(self, room, effect)
|
||||||
local to = effect.to
|
local to = effect.to
|
||||||
local from = effect.from
|
local from = effect.from
|
||||||
|
|
||||||
room:damage({
|
room:damage({
|
||||||
from = room:getPlayerById(from),
|
from = room:getPlayerById(from),
|
||||||
to = room:getPlayerById(to),
|
to = room:getPlayerById(to),
|
||||||
card = effect.card,
|
card = effect.card,
|
||||||
damage = 1,
|
damage = 1,
|
||||||
damageType = fk.ThunderDamage,
|
damageType = fk.ThunderDamage,
|
||||||
skillName = self.name
|
skillName = self.name
|
||||||
})
|
})
|
||||||
end
|
|
||||||
}
|
|
||||||
local thunderSlash = fk.CreateBasicCard{
|
|
||||||
name = "thunder__slash",
|
|
||||||
skill = thunderSlashSkill,
|
|
||||||
is_damage_card = true,
|
|
||||||
}
|
|
||||||
|
|
||||||
extension:addCards{
|
|
||||||
thunderSlash:clone(Card.Club, 5),
|
|
||||||
thunderSlash:clone(Card.Club, 6),
|
|
||||||
thunderSlash:clone(Card.Club, 7),
|
|
||||||
thunderSlash:clone(Card.Club, 8),
|
|
||||||
thunderSlash:clone(Card.Spade, 4),
|
|
||||||
thunderSlash:clone(Card.Spade, 5),
|
|
||||||
thunderSlash:clone(Card.Spade, 6),
|
|
||||||
thunderSlash:clone(Card.Spade, 7),
|
|
||||||
thunderSlash:clone(Card.Spade, 8),
|
|
||||||
}
|
|
||||||
|
|
||||||
local fireSlashSkill = fk.CreateActiveSkill{
|
|
||||||
name = "fire__slash_skill",
|
|
||||||
max_phase_use_time = 1,
|
|
||||||
target_num = 1,
|
|
||||||
can_use = slash.skill.canUse,
|
|
||||||
mod_target_filter = slash.skill.modTargetFilter,
|
|
||||||
target_filter = slash.skill.targetFilter,
|
|
||||||
on_effect = function(self, room, effect)
|
|
||||||
local to = effect.to
|
|
||||||
local from = effect.from
|
|
||||||
|
|
||||||
room:damage({
|
|
||||||
from = room:getPlayerById(from),
|
|
||||||
to = room:getPlayerById(to),
|
|
||||||
card = effect.card,
|
|
||||||
damage = 1,
|
|
||||||
damageType = fk.FireDamage,
|
|
||||||
skillName = self.name
|
|
||||||
})
|
|
||||||
end
|
|
||||||
}
|
|
||||||
local fireSlash = fk.CreateBasicCard{
|
|
||||||
name = "fire__slash",
|
|
||||||
skill = fireSlashSkill,
|
|
||||||
is_damage_card = true,
|
|
||||||
}
|
|
||||||
|
|
||||||
extension:addCards{
|
|
||||||
fireSlash:clone(Card.Heart, 4),
|
|
||||||
fireSlash:clone(Card.Heart, 7),
|
|
||||||
fireSlash:clone(Card.Heart, 10),
|
|
||||||
fireSlash:clone(Card.Diamond, 4),
|
|
||||||
fireSlash:clone(Card.Diamond, 5),
|
|
||||||
}
|
|
||||||
|
|
||||||
local analepticSkill = fk.CreateActiveSkill{
|
|
||||||
name = "analeptic_skill",
|
|
||||||
max_turn_use_time = 1,
|
|
||||||
mod_target_filter = function(self, to_select, selected, user, card, distance_limited)
|
|
||||||
return self:withinTimesLimit(Fk:currentRoom():getPlayerById(to_select), Player.HistoryTurn, card, "analeptic", Fk:currentRoom():getPlayerById(to_select)) and
|
|
||||||
not table.find(Fk:currentRoom().alive_players, function(p)
|
|
||||||
return p.dying
|
|
||||||
end)
|
|
||||||
end,
|
|
||||||
can_use = function(self, player, card)
|
|
||||||
return self:withinTimesLimit(player, Player.HistoryTurn, card, "analeptic", player)
|
|
||||||
end,
|
|
||||||
on_use = function(self, room, use)
|
|
||||||
if not use.tos or #TargetGroup:getRealTargets(use.tos) == 0 then
|
|
||||||
use.tos = { { use.from } }
|
|
||||||
end
|
end
|
||||||
|
}
|
||||||
if use.extra_data and use.extra_data.analepticRecover then
|
local thunderSlash = fk.CreateBasicCard {
|
||||||
use.extraUse = true
|
name = "thunder__slash",
|
||||||
end
|
skill = thunderSlashSkill,
|
||||||
end,
|
is_damage_card = true
|
||||||
on_effect = function(self, room, effect)
|
|
||||||
local to = room:getPlayerById(effect.to)
|
|
||||||
if effect.extra_data and effect.extra_data.analepticRecover then
|
|
||||||
room:recover({
|
|
||||||
who = to,
|
|
||||||
num = 1,
|
|
||||||
recoverBy = room:getPlayerById(effect.from),
|
|
||||||
card = effect.card,
|
|
||||||
})
|
|
||||||
else
|
|
||||||
to.drank = to.drank + 1
|
|
||||||
room:broadcastProperty(to, "drank")
|
|
||||||
end
|
|
||||||
end
|
|
||||||
}
|
}
|
||||||
|
|
||||||
local analepticEffect = fk.CreateTriggerSkill{
|
extension:addCards{thunderSlash:clone(Card.Club, 5), thunderSlash:clone(Card.Club, 6), thunderSlash:clone(Card.Club, 7),
|
||||||
name = "analeptic_effect",
|
thunderSlash:clone(Card.Club, 8), thunderSlash:clone(Card.Spade, 4),
|
||||||
global = true,
|
thunderSlash:clone(Card.Spade, 5), thunderSlash:clone(Card.Spade, 6),
|
||||||
priority = 0, -- game rule
|
thunderSlash:clone(Card.Spade, 7), thunderSlash:clone(Card.Spade, 8)}
|
||||||
events = { fk.PreCardUse, fk.EventPhaseStart },
|
|
||||||
can_trigger = function(self, event, target, player, data)
|
|
||||||
if target ~= player then
|
|
||||||
return false
|
|
||||||
end
|
|
||||||
|
|
||||||
if event == fk.PreCardUse then
|
local fireSlashSkill = fk.CreateActiveSkill {
|
||||||
return data.card.trueName == "slash" and player.drank > 0
|
name = "fire__slash_skill",
|
||||||
else
|
max_phase_use_time = 1,
|
||||||
return target.phase == Player.NotActive
|
target_num = 1,
|
||||||
|
can_use = slash.skill.canUse,
|
||||||
|
mod_target_filter = slash.skill.modTargetFilter,
|
||||||
|
target_filter = slash.skill.targetFilter,
|
||||||
|
on_effect = function(self, room, effect)
|
||||||
|
local to = effect.to
|
||||||
|
local from = effect.from
|
||||||
|
|
||||||
|
room:damage({
|
||||||
|
from = room:getPlayerById(from),
|
||||||
|
to = room:getPlayerById(to),
|
||||||
|
card = effect.card,
|
||||||
|
damage = 1,
|
||||||
|
damageType = fk.FireDamage,
|
||||||
|
skillName = self.name
|
||||||
|
})
|
||||||
end
|
end
|
||||||
end,
|
}
|
||||||
on_trigger = function(self, event, target, player, data)
|
local fireSlash = fk.CreateBasicCard {
|
||||||
local room = player.room
|
name = "fire__slash",
|
||||||
if event == fk.PreCardUse then
|
skill = fireSlashSkill,
|
||||||
data.additionalDamage = (data.additionalDamage or 0) + player.drank
|
is_damage_card = true
|
||||||
data.extra_data = data.extra_data or {}
|
}
|
||||||
data.extra_data.drankBuff = player.drank
|
|
||||||
player.drank = 0
|
extension:addCards{fireSlash:clone(Card.Heart, 4), fireSlash:clone(Card.Heart, 7), fireSlash:clone(Card.Heart, 10),
|
||||||
room:broadcastProperty(player, "drank")
|
fireSlash:clone(Card.Diamond, 4), fireSlash:clone(Card.Diamond, 5)}
|
||||||
else
|
|
||||||
for _, p in ipairs(room:getAlivePlayers(true)) do
|
local analepticSkill = fk.CreateActiveSkill {
|
||||||
if p.drank > 0 then
|
name = "analeptic_skill",
|
||||||
p.drank = 0
|
max_turn_use_time = 1,
|
||||||
room:broadcastProperty(p, "drank")
|
mod_target_filter = function(self, to_select, selected, user, card, distance_limited)
|
||||||
|
local to = Fk:currentRoom():getPlayerById(to_select)
|
||||||
|
local from = Fk:currentRoom():getPlayerById(user)
|
||||||
|
return self:withinTimesLimit(from, Player.HistoryTurn, card, "analeptic", to) and
|
||||||
|
not table.find(Fk:currentRoom().alive_players, function(p)
|
||||||
|
return p.dying
|
||||||
|
end) and not (card and from:isProhibited(to, card))
|
||||||
|
end,
|
||||||
|
can_use = function(self, player, card)
|
||||||
|
return self:withinTimesLimit(player, Player.HistoryTurn, card, "analeptic", player) and
|
||||||
|
not player:isProhibited(player, card)
|
||||||
|
end,
|
||||||
|
|
||||||
|
on_use = function(self, room, use)
|
||||||
|
if not use.tos or #TargetGroup:getRealTargets(use.tos) == 0 then
|
||||||
|
use.tos = {{use.from}}
|
||||||
|
end
|
||||||
|
|
||||||
|
if use.extra_data and use.extra_data.analepticRecover then
|
||||||
|
use.extraUse = true
|
||||||
|
end
|
||||||
|
end,
|
||||||
|
on_effect = function(self, room, effect)
|
||||||
|
local to = room:getPlayerById(effect.to)
|
||||||
|
if effect.extra_data and effect.extra_data.analepticRecover then
|
||||||
|
room:recover({
|
||||||
|
who = to,
|
||||||
|
num = 1,
|
||||||
|
recoverBy = room:getPlayerById(effect.from),
|
||||||
|
card = effect.card
|
||||||
|
})
|
||||||
|
else
|
||||||
|
to.drank = to.drank + 1
|
||||||
|
room:broadcastProperty(to, "drank")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
}
|
||||||
|
|
||||||
|
local analepticEffect = fk.CreateTriggerSkill {
|
||||||
|
name = "analeptic_effect",
|
||||||
|
global = true,
|
||||||
|
priority = 0, -- game rule
|
||||||
|
events = {fk.PreCardUse, fk.EventPhaseStart},
|
||||||
|
can_trigger = function(self, event, target, player, data)
|
||||||
|
if target ~= player then
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
if event == fk.PreCardUse then
|
||||||
|
return data.card.trueName == "slash" and player.drank > 0
|
||||||
|
else
|
||||||
|
return target.phase == Player.NotActive
|
||||||
|
end
|
||||||
|
end,
|
||||||
|
on_trigger = function(self, event, target, player, data)
|
||||||
|
local room = player.room
|
||||||
|
if event == fk.PreCardUse then
|
||||||
|
data.additionalDamage = (data.additionalDamage or 0) + player.drank
|
||||||
|
data.extra_data = data.extra_data or {}
|
||||||
|
data.extra_data.drankBuff = player.drank
|
||||||
|
player.drank = 0
|
||||||
|
room:broadcastProperty(player, "drank")
|
||||||
|
else
|
||||||
|
for _, p in ipairs(room:getAlivePlayers(true)) do
|
||||||
|
if p.drank > 0 then
|
||||||
|
p.drank = 0
|
||||||
|
room:broadcastProperty(p, "drank")
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end,
|
|
||||||
}
|
}
|
||||||
Fk:addSkill(analepticEffect)
|
Fk:addSkill(analepticEffect)
|
||||||
|
|
||||||
local analeptic = fk.CreateBasicCard{
|
local analeptic = fk.CreateBasicCard {
|
||||||
name = "analeptic",
|
name = "analeptic",
|
||||||
suit = Card.Spade,
|
suit = Card.Spade,
|
||||||
number = 3,
|
number = 3,
|
||||||
skill = analepticSkill,
|
skill = analepticSkill
|
||||||
}
|
}
|
||||||
|
|
||||||
extension:addCards({
|
extension:addCards({analeptic, analeptic:clone(Card.Spade, 9), analeptic:clone(Card.Club, 3),
|
||||||
analeptic,
|
analeptic:clone(Card.Club, 9), analeptic:clone(Card.Diamond, 9)})
|
||||||
analeptic:clone(Card.Spade, 9),
|
|
||||||
analeptic:clone(Card.Club, 3),
|
|
||||||
analeptic:clone(Card.Club, 9),
|
|
||||||
analeptic:clone(Card.Diamond, 9),
|
|
||||||
})
|
|
||||||
|
|
||||||
local recast = fk.CreateActiveSkill{
|
local recast = fk.CreateActiveSkill {
|
||||||
name = "recast",
|
name = "recast",
|
||||||
target_num = 0,
|
target_num = 0,
|
||||||
on_use = function(self, room, effect)
|
on_use = function(self, room, effect)
|
||||||
room:recastCard(effect.cards, room:getPlayerById(effect.from))
|
room:recastCard(effect.cards, room:getPlayerById(effect.from))
|
||||||
end
|
end
|
||||||
}
|
}
|
||||||
Fk:addSkill(recast)
|
Fk:addSkill(recast)
|
||||||
|
|
||||||
local ironChainCardSkill = fk.CreateActiveSkill{
|
local ironChainCardSkill = fk.CreateActiveSkill {
|
||||||
name = "iron_chain_skill",
|
name = "iron_chain_skill",
|
||||||
min_target_num = 1,
|
min_target_num = 1,
|
||||||
max_target_num = 2,
|
max_target_num = 2,
|
||||||
mod_target_filter = function(self, to_select, selected, user, card, distance_limited)
|
mod_target_filter = function(self, to_select, selected, user, card, distance_limited)
|
||||||
return true
|
local to = Fk:currentRoom():getPlayerById(to_select)
|
||||||
end,
|
local from = Fk:currentRoom():getPlayerById(user)
|
||||||
target_filter = function() return true end,
|
return not (card and from:isProhibited(to, card))
|
||||||
on_effect = function(self, room, cardEffectEvent)
|
end,
|
||||||
local to = room:getPlayerById(cardEffectEvent.to)
|
target_filter = function(self, to_select, selected, _, card)
|
||||||
to:setChainState(not to.chained)
|
if #selected < self:getMaxTargetNum(Self, card) then
|
||||||
end,
|
return self:modTargetFilter(to_select, selected, Self.id, card)
|
||||||
}
|
end
|
||||||
|
end,
|
||||||
local ironChain = fk.CreateTrickCard{
|
on_effect = function(self, room, cardEffectEvent)
|
||||||
name = "iron_chain",
|
local to = room:getPlayerById(cardEffectEvent.to)
|
||||||
skill = ironChainCardSkill,
|
to:setChainState(not to.chained)
|
||||||
special_skills = { "recast" },
|
|
||||||
multiple_targets = true,
|
|
||||||
}
|
|
||||||
extension:addCards{
|
|
||||||
ironChain:clone(Card.Spade, 11),
|
|
||||||
ironChain:clone(Card.Spade, 12),
|
|
||||||
ironChain:clone(Card.Club, 10),
|
|
||||||
ironChain:clone(Card.Club, 11),
|
|
||||||
ironChain:clone(Card.Club, 12),
|
|
||||||
ironChain:clone(Card.Club, 13),
|
|
||||||
}
|
|
||||||
|
|
||||||
local fireAttackSkill = fk.CreateActiveSkill{
|
|
||||||
name = "fire_attack_skill",
|
|
||||||
target_num = 1,
|
|
||||||
mod_target_filter = function(self, to_select, selected, user, card, distance_limited)
|
|
||||||
return not Fk:currentRoom():getPlayerById(to_select):isKongcheng()
|
|
||||||
end,
|
|
||||||
target_filter = function(self, to_select)
|
|
||||||
return self:modTargetFilter(to_select)
|
|
||||||
end,
|
|
||||||
on_effect = function(self, room, cardEffectEvent)
|
|
||||||
local from = room:getPlayerById(cardEffectEvent.from)
|
|
||||||
local to = room:getPlayerById(cardEffectEvent.to)
|
|
||||||
if to:isKongcheng() then return end
|
|
||||||
|
|
||||||
local showCard = room:askForCard(to, 1, 1, false, self.name, false, ".|.|.|hand", "#fire_attack-show:" .. from.id)[1]
|
|
||||||
to:showCards(showCard)
|
|
||||||
|
|
||||||
showCard = Fk:getCardById(showCard)
|
|
||||||
local cards = room:askForDiscard(from, 1, 1, false, self.name, true,
|
|
||||||
".|.|" .. showCard:getSuitString(), "#fire_attack-discard:" .. to.id .. "::" .. showCard:getSuitString())
|
|
||||||
if #cards > 0 then
|
|
||||||
room:damage({
|
|
||||||
from = from,
|
|
||||||
to = to,
|
|
||||||
card = cardEffectEvent.card,
|
|
||||||
damage = 1,
|
|
||||||
damageType = fk.FireDamage,
|
|
||||||
skillName = self.name
|
|
||||||
})
|
|
||||||
end
|
end
|
||||||
end,
|
|
||||||
}
|
|
||||||
local fireAttack = fk.CreateTrickCard{
|
|
||||||
name = "fire_attack",
|
|
||||||
skill = fireAttackSkill,
|
|
||||||
is_damage_card = true,
|
|
||||||
}
|
|
||||||
extension:addCards{
|
|
||||||
fireAttack:clone(Card.Heart, 2),
|
|
||||||
fireAttack:clone(Card.Heart, 3),
|
|
||||||
fireAttack:clone(Card.Diamond, 12),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
local supplyShortageSkill = fk.CreateActiveSkill{
|
local ironChain = fk.CreateTrickCard {
|
||||||
name = "supply_shortage_skill",
|
name = "iron_chain",
|
||||||
distance_limit = 1,
|
skill = ironChainCardSkill,
|
||||||
mod_target_filter = function(self, to_select, selected, user, card, distance_limited)
|
special_skills = {"recast"},
|
||||||
local player = Fk:currentRoom():getPlayerById(to_select)
|
multiple_targets = true
|
||||||
local from = Fk:currentRoom():getPlayerById(user)
|
}
|
||||||
return from ~= player and not (distance_limited and not self:withinDistanceLimit(from, false, card, player))
|
extension:addCards{ironChain:clone(Card.Spade, 11), ironChain:clone(Card.Spade, 12), ironChain:clone(Card.Club, 10),
|
||||||
end,
|
ironChain:clone(Card.Club, 11), ironChain:clone(Card.Club, 12), ironChain:clone(Card.Club, 13)}
|
||||||
target_filter = function(self, to_select, selected, _, card)
|
|
||||||
return #selected == 0 and self:modTargetFilter(to_select, selected, Self.id, card, true)
|
local fireAttackSkill = fk.CreateActiveSkill {
|
||||||
end,
|
name = "fire_attack_skill",
|
||||||
target_num = 1,
|
target_num = 1,
|
||||||
on_effect = function(self, room, effect)
|
mod_target_filter = function(self, to_select, selected, user, card, distance_limited)
|
||||||
local to = room:getPlayerById(effect.to)
|
local to = Fk:currentRoom():getPlayerById(to_select)
|
||||||
local judge = {
|
local from = Fk:currentRoom():getPlayerById(user)
|
||||||
who = to,
|
return not (card and from:isProhibited(to, card)) and not to:isKongcheng()
|
||||||
reason = "supply_shortage",
|
end,
|
||||||
pattern = ".|.|spade,heart,diamond",
|
target_filter = function(self, to_select, selected, _, card)
|
||||||
}
|
if #selected < self:getMaxTargetNum(Self, card) then
|
||||||
room:judge(judge)
|
return self:modTargetFilter(to_select, selected, Self.id, card)
|
||||||
local result = judge.card
|
end
|
||||||
if result.suit ~= Card.Club then
|
end,
|
||||||
to:skip(Player.Draw)
|
on_effect = function(self, room, cardEffectEvent)
|
||||||
|
local from = room:getPlayerById(cardEffectEvent.from)
|
||||||
|
local to = room:getPlayerById(cardEffectEvent.to)
|
||||||
|
if to:isKongcheng() then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
local showCard = room:askForCard(to, 1, 1, false, self.name, false, ".|.|.|hand",
|
||||||
|
"#fire_attack-show:" .. from.id)[1]
|
||||||
|
to:showCards(showCard)
|
||||||
|
|
||||||
|
showCard = Fk:getCardById(showCard)
|
||||||
|
local cards = room:askForDiscard(from, 1, 1, false, self.name, true, ".|.|" .. showCard:getSuitString(),
|
||||||
|
"#fire_attack-discard:" .. to.id .. "::" .. showCard:getSuitString())
|
||||||
|
if #cards > 0 then
|
||||||
|
room:damage({
|
||||||
|
from = from,
|
||||||
|
to = to,
|
||||||
|
card = cardEffectEvent.card,
|
||||||
|
damage = 1,
|
||||||
|
damageType = fk.FireDamage,
|
||||||
|
skillName = self.name
|
||||||
|
})
|
||||||
|
end
|
||||||
end
|
end
|
||||||
self:onNullified(room, effect)
|
|
||||||
end,
|
|
||||||
on_nullified = function(self, room, effect)
|
|
||||||
room:moveCards{
|
|
||||||
ids = room:getSubcardsByRule(effect.card, { Card.Processing }),
|
|
||||||
toArea = Card.DiscardPile,
|
|
||||||
moveReason = fk.ReasonUse
|
|
||||||
}
|
|
||||||
end,
|
|
||||||
}
|
}
|
||||||
local supplyShortage = fk.CreateDelayedTrickCard{
|
local fireAttack = fk.CreateTrickCard {
|
||||||
name = "supply_shortage",
|
name = "fire_attack",
|
||||||
skill = supplyShortageSkill,
|
skill = fireAttackSkill,
|
||||||
}
|
is_damage_card = true
|
||||||
extension:addCards{
|
|
||||||
supplyShortage:clone(Card.Spade, 10),
|
|
||||||
supplyShortage:clone(Card.Club, 4),
|
|
||||||
}
|
}
|
||||||
|
extension:addCards{fireAttack:clone(Card.Heart, 2), fireAttack:clone(Card.Heart, 3), fireAttack:clone(Card.Diamond, 12)}
|
||||||
|
|
||||||
local gudingSkill = fk.CreateTriggerSkill{
|
local supplyShortageSkill = fk.CreateActiveSkill {
|
||||||
name = "#guding_blade_skill",
|
name = "supply_shortage_skill",
|
||||||
attached_equip = "guding_blade",
|
distance_limit = 1,
|
||||||
frequency = Skill.Compulsory,
|
mod_target_filter = function(self, to_select, selected, user, card, distance_limited)
|
||||||
events = {fk.DamageCaused},
|
local player = Fk:currentRoom():getPlayerById(to_select)
|
||||||
can_trigger = function(self, event, target, player, data)
|
local from = Fk:currentRoom():getPlayerById(user)
|
||||||
return target == player and player:hasSkill(self.name) and
|
return
|
||||||
data.to:isKongcheng() and data.card and data.card.trueName == "slash" and
|
user ~= to_select and not (distance_limited and not self:withinDistanceLimit(from, false, card, player)) and
|
||||||
not data.chain
|
not (card and from:isProhibited(player, card))
|
||||||
end,
|
end,
|
||||||
on_use = function(_, _, _, _, data)
|
target_filter = function(self, to_select, selected, _, card)
|
||||||
data.damage = data.damage + 1
|
return #selected < 1 and self:modTargetFilter(to_select, selected, Self.id, card, true)
|
||||||
end,
|
end,
|
||||||
|
target_num = 1,
|
||||||
|
on_effect = function(self, room, effect)
|
||||||
|
local to = room:getPlayerById(effect.to)
|
||||||
|
local judge = {
|
||||||
|
who = to,
|
||||||
|
reason = "supply_shortage",
|
||||||
|
pattern = ".|.|club"
|
||||||
|
}
|
||||||
|
room:judge(judge)
|
||||||
|
if judge.card.suit ~= Card.Club then
|
||||||
|
to:skip(Player.Draw)
|
||||||
|
end
|
||||||
|
self:onNullified(room, effect)
|
||||||
|
end,
|
||||||
|
on_nullified = function(self, room, effect)
|
||||||
|
room:moveCards{
|
||||||
|
ids = room:getSubcardsByRule(effect.card, {Card.Processing}),
|
||||||
|
toArea = Card.DiscardPile,
|
||||||
|
moveReason = fk.ReasonUse
|
||||||
|
}
|
||||||
|
end
|
||||||
|
}
|
||||||
|
local supplyShortage = fk.CreateDelayedTrickCard {
|
||||||
|
name = "supply_shortage",
|
||||||
|
skill = supplyShortageSkill
|
||||||
|
}
|
||||||
|
extension:addCards{supplyShortage:clone(Card.Spade, 10), supplyShortage:clone(Card.Club, 4)}
|
||||||
|
|
||||||
|
local gudingSkill = fk.CreateTriggerSkill {
|
||||||
|
name = "#guding_blade_skill",
|
||||||
|
attached_equip = "guding_blade",
|
||||||
|
frequency = Skill.Compulsory,
|
||||||
|
events = {fk.DamageCaused},
|
||||||
|
can_trigger = function(self, event, target, player, data)
|
||||||
|
return target == player and player:hasSkill(self.name) and data.to:isKongcheng() and data.card and
|
||||||
|
data.card.trueName == "slash" and not data.chain
|
||||||
|
end,
|
||||||
|
on_use = function(_, _, _, _, data)
|
||||||
|
data.damage = data.damage + 1
|
||||||
|
end
|
||||||
}
|
}
|
||||||
Fk:addSkill(gudingSkill)
|
Fk:addSkill(gudingSkill)
|
||||||
local gudingBlade = fk.CreateWeapon{
|
local gudingBlade = fk.CreateWeapon {
|
||||||
name = "guding_blade",
|
name = "guding_blade",
|
||||||
suit = Card.Spade,
|
suit = Card.Spade,
|
||||||
number = 1,
|
number = 1,
|
||||||
attack_range = 2,
|
attack_range = 2,
|
||||||
equip_skill = gudingSkill,
|
equip_skill = gudingSkill
|
||||||
}
|
}
|
||||||
|
|
||||||
extension:addCard(gudingBlade)
|
extension:addCard(gudingBlade)
|
||||||
|
|
||||||
local fanSkill = fk.CreateTriggerSkill{
|
local fanSkill = fk.CreateTriggerSkill {
|
||||||
name = "#fan_skill",
|
name = "#fan_skill",
|
||||||
attached_equip = "fan",
|
attached_equip = "fan",
|
||||||
events = { fk.AfterCardUseDeclared },
|
events = {fk.AfterCardUseDeclared},
|
||||||
can_trigger = function(self, event, target, player, data)
|
can_trigger = function(self, event, target, player, data)
|
||||||
return target == player and player:hasSkill(self.name) and data.card.name == "slash"
|
return target == player and player:hasSkill(self.name) and data.card.name == "slash"
|
||||||
end,
|
end,
|
||||||
on_use = function(_, _, _, _, data)
|
on_use = function(_, _, _, _, data)
|
||||||
local card = Fk:cloneCard("fire__slash")
|
local card = Fk:cloneCard("fire__slash")
|
||||||
card.skillName = "fan"
|
card.skillName = "fan"
|
||||||
card:addSubcard(data.card)
|
card:addSubcard(data.card)
|
||||||
data.card = card
|
data.card = card
|
||||||
end,
|
end
|
||||||
}
|
}
|
||||||
Fk:addSkill(fanSkill)
|
Fk:addSkill(fanSkill)
|
||||||
local fan = fk.CreateWeapon{
|
local fan = fk.CreateWeapon {
|
||||||
name = "fan",
|
name = "fan",
|
||||||
suit = Card.Diamond,
|
suit = Card.Diamond,
|
||||||
number = 1,
|
number = 1,
|
||||||
attack_range = 4,
|
attack_range = 4,
|
||||||
equip_skill = fanSkill,
|
equip_skill = fanSkill
|
||||||
}
|
}
|
||||||
|
|
||||||
extension:addCard(fan)
|
extension:addCard(fan)
|
||||||
|
|
||||||
local vineSkill = fk.CreateTriggerSkill{
|
local vineSkill = fk.CreateTriggerSkill {
|
||||||
name = "#vine_skill",
|
name = "#vine_skill",
|
||||||
attached_equip = "vine",
|
attached_equip = "vine",
|
||||||
mute = true,
|
mute = true,
|
||||||
frequency = Skill.Compulsory,
|
frequency = Skill.Compulsory,
|
||||||
|
|
||||||
events = {fk.PreCardEffect, fk.DamageInflicted},
|
events = {fk.PreCardEffect, fk.DamageInflicted},
|
||||||
can_trigger = function(self, event, target, player, data)
|
can_trigger = function(self, event, target, player, data)
|
||||||
if event == fk.DamageInflicted then
|
if event == fk.DamageInflicted then
|
||||||
return target == player and player:hasSkill(self.name) and
|
return target == player and player:hasSkill(self.name) and data.damageType == fk.FireDamage
|
||||||
data.damageType == fk.FireDamage
|
end
|
||||||
|
local effect = data ---@type CardEffectEvent
|
||||||
|
return player.id == effect.to and player:hasSkill(self.name) and
|
||||||
|
(effect.card.name == "slash" or effect.card.name == "savage_assault" or effect.card.name ==
|
||||||
|
"archery_attack")
|
||||||
|
end,
|
||||||
|
on_use = function(self, event, target, player, data)
|
||||||
|
local room = player.room
|
||||||
|
if event == fk.DamageInflicted then
|
||||||
|
room:broadcastPlaySound("./packages/maneuvering/audio/card/vineburn")
|
||||||
|
room:setEmotion(player, "./packages/maneuvering/image/anim/vineburn")
|
||||||
|
data.damage = data.damage + 1
|
||||||
|
else
|
||||||
|
room:broadcastPlaySound("./packages/maneuvering/audio/card/vine")
|
||||||
|
room:setEmotion(player, "./packages/maneuvering/image/anim/vine")
|
||||||
|
return true
|
||||||
|
end
|
||||||
end
|
end
|
||||||
local effect = data ---@type CardEffectEvent
|
|
||||||
return player.id == effect.to and player:hasSkill(self.name) and
|
|
||||||
(effect.card.name == "slash" or effect.card.name == "savage_assault" or
|
|
||||||
effect.card.name == "archery_attack")
|
|
||||||
end,
|
|
||||||
on_use = function(self, event, target, player, data)
|
|
||||||
local room = player.room
|
|
||||||
if event == fk.DamageInflicted then
|
|
||||||
room:broadcastPlaySound("./packages/maneuvering/audio/card/vineburn")
|
|
||||||
room:setEmotion(player, "./packages/maneuvering/image/anim/vineburn")
|
|
||||||
data.damage = data.damage + 1
|
|
||||||
else
|
|
||||||
room:broadcastPlaySound("./packages/maneuvering/audio/card/vine")
|
|
||||||
room:setEmotion(player, "./packages/maneuvering/image/anim/vine")
|
|
||||||
return true
|
|
||||||
end
|
|
||||||
end,
|
|
||||||
}
|
}
|
||||||
Fk:addSkill(vineSkill)
|
Fk:addSkill(vineSkill)
|
||||||
local vine = fk.CreateArmor{
|
local vine = fk.CreateArmor {
|
||||||
name = "vine",
|
name = "vine",
|
||||||
equip_skill = vineSkill,
|
equip_skill = vineSkill
|
||||||
}
|
|
||||||
extension:addCards{
|
|
||||||
vine:clone(Card.Spade, 2),
|
|
||||||
vine:clone(Card.Club, 2),
|
|
||||||
}
|
}
|
||||||
|
extension:addCards{vine:clone(Card.Spade, 2), vine:clone(Card.Club, 2)}
|
||||||
|
|
||||||
local silverLionSkill = fk.CreateTriggerSkill{
|
local silverLionSkill = fk.CreateTriggerSkill {
|
||||||
name = "#silver_lion_skill",
|
name = "#silver_lion_skill",
|
||||||
attached_equip = "silver_lion",
|
attached_equip = "silver_lion",
|
||||||
frequency = Skill.Compulsory,
|
frequency = Skill.Compulsory,
|
||||||
events = {fk.DamageInflicted},
|
events = {fk.DamageInflicted},
|
||||||
can_trigger = function(self, event, target, player, data)
|
can_trigger = function(self, event, target, player, data)
|
||||||
return target == player and player:hasSkill(self.name) and data.damage > 1
|
return target == player and player:hasSkill(self.name) and data.damage > 1
|
||||||
end,
|
end,
|
||||||
on_use = function(_, _, _, _, data)
|
on_use = function(_, _, _, _, data)
|
||||||
data.damage = 1
|
data.damage = 1
|
||||||
end,
|
end
|
||||||
}
|
}
|
||||||
Fk:addSkill(silverLionSkill)
|
Fk:addSkill(silverLionSkill)
|
||||||
local silverLion = fk.CreateArmor{
|
local silverLion = fk.CreateArmor {
|
||||||
name = "silver_lion",
|
name = "silver_lion",
|
||||||
suit = Card.Club,
|
suit = Card.Club,
|
||||||
number = 1,
|
number = 1,
|
||||||
equip_skill = silverLionSkill,
|
equip_skill = silverLionSkill,
|
||||||
on_uninstall = function(self, room, player)
|
on_uninstall = function(self, room, player)
|
||||||
Armor.onUninstall(self, room, player)
|
Armor.onUninstall(self, room, player)
|
||||||
if player:isAlive() and player:isWounded() and self.equip_skill:isEffectable(player) then
|
if player:isAlive() and player:isWounded() and self.equip_skill:isEffectable(player) then
|
||||||
room:broadcastPlaySound("./packages/maneuvering/audio/card/silver_lion")
|
room:broadcastPlaySound("./packages/maneuvering/audio/card/silver_lion")
|
||||||
room:setEmotion(player, "./packages/maneuvering/image/anim/silver_lion")
|
room:setEmotion(player, "./packages/maneuvering/image/anim/silver_lion")
|
||||||
room:recover{
|
room:recover{
|
||||||
who = player,
|
who = player,
|
||||||
num = 1,
|
num = 1,
|
||||||
skillName = self.name
|
skillName = self.name
|
||||||
}
|
}
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end,
|
|
||||||
}
|
}
|
||||||
extension:addCard(silverLion)
|
extension:addCard(silverLion)
|
||||||
|
|
||||||
local huaLiu = fk.CreateDefensiveRide{
|
local huaLiu = fk.CreateDefensiveRide {
|
||||||
name = "hualiu",
|
name = "hualiu",
|
||||||
suit = Card.Diamond,
|
suit = Card.Diamond,
|
||||||
number = 13,
|
number = 13
|
||||||
}
|
}
|
||||||
|
|
||||||
extension:addCards({
|
extension:addCards({huaLiu})
|
||||||
huaLiu,
|
|
||||||
})
|
|
||||||
|
|
||||||
extension:addCards{
|
extension:addCards{Fk:cloneCard("jink", Card.Heart, 8), Fk:cloneCard("jink", Card.Heart, 9),
|
||||||
Fk:cloneCard("jink", Card.Heart, 8),
|
Fk:cloneCard("jink", Card.Heart, 11), Fk:cloneCard("jink", Card.Heart, 12),
|
||||||
Fk:cloneCard("jink", Card.Heart, 9),
|
Fk:cloneCard("jink", Card.Diamond, 6), Fk:cloneCard("jink", Card.Diamond, 7),
|
||||||
Fk:cloneCard("jink", Card.Heart, 11),
|
Fk:cloneCard("jink", Card.Diamond, 8), Fk:cloneCard("jink", Card.Diamond, 10),
|
||||||
Fk:cloneCard("jink", Card.Heart, 12),
|
Fk:cloneCard("jink", Card.Diamond, 11), Fk:cloneCard("peach", Card.Heart, 5),
|
||||||
Fk:cloneCard("jink", Card.Diamond, 6),
|
Fk:cloneCard("peach", Card.Heart, 6), Fk:cloneCard("peach", Card.Diamond, 2),
|
||||||
Fk:cloneCard("jink", Card.Diamond, 7),
|
Fk:cloneCard("peach", Card.Diamond, 3), Fk:cloneCard("nullification", Card.Heart, 1),
|
||||||
Fk:cloneCard("jink", Card.Diamond, 8),
|
Fk:cloneCard("nullification", Card.Heart, 13), Fk:cloneCard("nullification", Card.Spade, 13)}
|
||||||
Fk:cloneCard("jink", Card.Diamond, 10),
|
|
||||||
Fk:cloneCard("jink", Card.Diamond, 11),
|
|
||||||
|
|
||||||
Fk:cloneCard("peach", Card.Heart, 5),
|
|
||||||
Fk:cloneCard("peach", Card.Heart, 6),
|
|
||||||
Fk:cloneCard("peach", Card.Diamond, 2),
|
|
||||||
Fk:cloneCard("peach", Card.Diamond, 3),
|
|
||||||
|
|
||||||
Fk:cloneCard("nullification", Card.Heart, 1),
|
|
||||||
Fk:cloneCard("nullification", Card.Heart, 13),
|
|
||||||
Fk:cloneCard("nullification", Card.Spade, 13),
|
|
||||||
}
|
|
||||||
|
|
||||||
Fk:loadTranslationTable{
|
Fk:loadTranslationTable{
|
||||||
["maneuvering"] = "军争",
|
["maneuvering"] = "军争",
|
||||||
|
|
||||||
["thunder__slash"] = "雷杀",
|
["thunder__slash"] = "雷杀",
|
||||||
[":thunder__slash"] = "基本牌<br /><b>时机</b>:出牌阶段<br /><b>目标</b>:攻击范围内的一名角色<br /><b>效果</b>:对目标角色造成1点雷电伤害。",
|
[":thunder__slash"] = "基本牌<br /><b>时机</b>:出牌阶段<br /><b>目标</b>:攻击范围内的一名角色<br /><b>效果</b>:对目标角色造成1点雷电伤害。",
|
||||||
["fire__slash"] = "火杀",
|
["fire__slash"] = "火杀",
|
||||||
[":fire__slash"] = "基本牌<br /><b>时机</b>:出牌阶段<br /><b>目标</b>:攻击范围内的一名角色<br /><b>效果</b>:对目标角色造成1点火焰伤害。",
|
[":fire__slash"] = "基本牌<br /><b>时机</b>:出牌阶段<br /><b>目标</b>:攻击范围内的一名角色<br /><b>效果</b>:对目标角色造成1点火焰伤害。",
|
||||||
["analeptic"] = "酒",
|
["analeptic"] = "酒",
|
||||||
[":analeptic"] = "基本牌<br /><b>时机</b>:出牌阶段/你处于濒死状态时<br /><b>目标</b>:你<br /><b>效果</b>:目标角色本回合使用的下一张【杀】将要造成的伤害+1/目标角色回复1点体力。",
|
[":analeptic"] = "基本牌<br /><b>时机</b>:出牌阶段/你处于濒死状态时<br /><b>目标</b>:你<br /><b>效果</b>:目标角色本回合使用的下一张【杀】将要造成的伤害+1/目标角色回复1点体力。",
|
||||||
["iron_chain"] = "铁锁连环",
|
["iron_chain"] = "铁锁连环",
|
||||||
[":iron_chain"] = "锦囊牌<br /><b>时机</b>:出牌阶段<br /><b>目标</b>:一至两名角色<br /><b>效果</b>:横置或重置目标角色的武将牌。",
|
[":iron_chain"] = "锦囊牌<br /><b>时机</b>:出牌阶段<br /><b>目标</b>:一至两名角色<br /><b>效果</b>:横置或重置目标角色的武将牌。",
|
||||||
["_normal_use"] = "正常使用",
|
["_normal_use"] = "正常使用",
|
||||||
["recast"] = "重铸",
|
["recast"] = "重铸",
|
||||||
[":recast"] = "你可以将此牌置入弃牌堆,然后摸一张牌。",
|
[":recast"] = "你可以将此牌置入弃牌堆,然后摸一张牌。",
|
||||||
["fire_attack"] = "火攻",
|
["fire_attack"] = "火攻",
|
||||||
["fire_attack_skill"] = "火攻",
|
["fire_attack_skill"] = "火攻",
|
||||||
[":fire_attack"] = "锦囊牌<br /><b>时机</b>:出牌阶段<br /><b>目标</b>:一名有手牌的角色<br /><b>效果</b>:目标角色展示一张手牌,然后你可以弃置一张与所展示牌花色相同的手牌令其受到1点火焰伤害。",
|
[":fire_attack"] = "锦囊牌<br /><b>时机</b>:出牌阶段<br /><b>目标</b>:一名有手牌的角色<br /><b>效果</b>:目标角色展示一张手牌,然后你可以弃置一张与所展示牌花色相同的手牌令其受到1点火焰伤害。",
|
||||||
["#fire_attack-show"] = "%src 对你使用了火攻,请展示一张手牌",
|
["#fire_attack-show"] = "%src 对你使用了火攻,请展示一张手牌",
|
||||||
["#fire_attack-discard"] = "你可弃置一张 %arg 手牌,对 %src 造成1点火属性伤害",
|
["#fire_attack-discard"] = "你可弃置一张 %arg 手牌,对 %src 造成1点火属性伤害",
|
||||||
["supply_shortage"] = "兵粮寸断",
|
["supply_shortage"] = "兵粮寸断",
|
||||||
[":supply_shortage"] = "延时锦囊牌<br /><b>时机</b>:出牌阶段<br /><b>目标</b>:距离1的一名其他角色<br /><b>效果</b>:将此牌置于目标角色判定区内。其判定阶段进行判定:若结果不为梅花,其跳过摸牌阶段。然后将【兵粮寸断】置入弃牌堆。",
|
[":supply_shortage"] = "延时锦囊牌<br /><b>时机</b>:出牌阶段<br /><b>目标</b>:距离1的一名其他角色<br /><b>效果</b>:将此牌置于目标角色判定区内。其判定阶段进行判定:若结果不为梅花,其跳过摸牌阶段。然后将【兵粮寸断】置入弃牌堆。",
|
||||||
["guding_blade"] = "古锭刀",
|
["guding_blade"] = "古锭刀",
|
||||||
[":guding_blade"] = "装备牌·武器<br /><b>攻击范围</b>:2<br /><b>武器技能</b>:锁定技。每当你使用【杀】对目标角色造成伤害时,若该角色没有手牌,此伤害+1。",
|
[":guding_blade"] = "装备牌·武器<br /><b>攻击范围</b>:2<br /><b>武器技能</b>:锁定技。每当你使用【杀】对目标角色造成伤害时,若该角色没有手牌,此伤害+1。",
|
||||||
["fan"] = "朱雀羽扇",
|
["fan"] = "朱雀羽扇",
|
||||||
[":fan"] = "装备牌·武器<br /><b>攻击范围</b>:4<br /><b>武器技能</b>:你可以将一张普通【杀】当火【杀】使用。",
|
[":fan"] = "装备牌·武器<br /><b>攻击范围</b>:4<br /><b>武器技能</b>:你可以将一张普通【杀】当火【杀】使用。",
|
||||||
["#fan_skill"] = "朱雀羽扇",
|
["#fan_skill"] = "朱雀羽扇",
|
||||||
["vine"] = "藤甲",
|
["vine"] = "藤甲",
|
||||||
[":vine"] = "装备牌·防具<br /><b>防具技能</b>:锁定技。【南蛮入侵】、【万箭齐发】和普通【杀】对你无效。每当你受到火焰伤害时,此伤害+1。",
|
[":vine"] = "装备牌·防具<br /><b>防具技能</b>:锁定技。【南蛮入侵】、【万箭齐发】和普通【杀】对你无效。每当你受到火焰伤害时,此伤害+1。",
|
||||||
["silver_lion"] = "白银狮子",
|
["silver_lion"] = "白银狮子",
|
||||||
[":silver_lion"] = "装备牌·防具<br /><b>防具技能</b>:锁定技。每当你受到伤害时,若此伤害大于1点,防止多余的伤害。每当你失去装备区里的【白银狮子】后,你回复1点体力。",
|
[":silver_lion"] = "装备牌·防具<br /><b>防具技能</b>:锁定技。每当你受到伤害时,若此伤害大于1点,防止多余的伤害。每当你失去装备区里的【白银狮子】后,你回复1点体力。",
|
||||||
["hualiu"] = "骅骝",
|
["hualiu"] = "骅骝",
|
||||||
[":hualiu"] = "装备牌·坐骑<br /><b>坐骑技能</b>:其他角色与你的距离+1。",
|
[":hualiu"] = "装备牌·坐骑<br /><b>坐骑技能</b>:其他角色与你的距离+1。"
|
||||||
}
|
}
|
||||||
|
|
||||||
return extension
|
return extension
|
||||||
|
|
|
@ -0,0 +1,178 @@
|
||||||
|
fk.ai_card.thunder__slash = fk.ai_card.slash
|
||||||
|
fk.ai_use_play.thunder__slash = fk.ai_use_play.slash
|
||||||
|
fk.ai_card.fire__slash = fk.ai_card.slash
|
||||||
|
fk.ai_use_play.fire__slash = fk.ai_use_play.slash
|
||||||
|
fk.ai_card.analeptic = {
|
||||||
|
intention = 60, -- 身份值
|
||||||
|
value = 5, -- 卡牌价值
|
||||||
|
priority = 3 -- 使用优先值
|
||||||
|
}
|
||||||
|
|
||||||
|
fk.ai_use_play.analeptic = function(self, card)
|
||||||
|
local cards = table.map(self.player:getCardIds("&he"), function(id)
|
||||||
|
return Fk:getCardById(id)
|
||||||
|
end)
|
||||||
|
self:sortValue(cards)
|
||||||
|
for _, sth in ipairs(self:getActives("slash")) do
|
||||||
|
local slash = nil
|
||||||
|
if sth:isInstanceOf(Card) then
|
||||||
|
if sth.skill:canUse(self.player, sth) and not self.player:prohibitUse(sth) then
|
||||||
|
slash = sth
|
||||||
|
end
|
||||||
|
else
|
||||||
|
local selected = {}
|
||||||
|
for _, c in ipairs(cards) do
|
||||||
|
if sth:cardFilter(c.id, selected) then
|
||||||
|
table.insert(selected, c.id)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
local tc = sth:viewAs(selected)
|
||||||
|
if tc and tc:matchPattern("slash") and tc.skill:canUse(self.player, tc) and not self.player:prohibitUse(tc) then
|
||||||
|
slash = tc
|
||||||
|
end
|
||||||
|
end
|
||||||
|
if slash then
|
||||||
|
fk.ai_use_play.slash(self, slash)
|
||||||
|
if self.use_id then
|
||||||
|
self.use_id = card.id
|
||||||
|
self.use_tos = {}
|
||||||
|
break
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
fk.ai_card.iron_chain = {
|
||||||
|
intention = function(self, card, from)
|
||||||
|
if self.player.chained then
|
||||||
|
return -80
|
||||||
|
end
|
||||||
|
return 80
|
||||||
|
end, -- 身份值
|
||||||
|
value = 2, -- 卡牌价值
|
||||||
|
priority = 3 -- 使用优先值
|
||||||
|
}
|
||||||
|
|
||||||
|
fk.ai_use_play.iron_chain = function(self, card)
|
||||||
|
for _, p in ipairs(self.friends) do
|
||||||
|
if card.skill:targetFilter(p.id, self.use_tos, {}, card) and p.chained then
|
||||||
|
table.insert(self.use_tos, p.id)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
self:sort(self.enemies)
|
||||||
|
for _, p in ipairs(self.enemies) do
|
||||||
|
if card.skill:targetFilter(p.id, self.use_tos, {}, card) and not p.chained then
|
||||||
|
table.insert(self.use_tos, p.id)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
if #self.use_tos < 2 then
|
||||||
|
self.use_tos = {}
|
||||||
|
else
|
||||||
|
self.use_id = card.id
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
fk.ai_use_play.recast = function(self, card)
|
||||||
|
if self.command == "PlayCard" then
|
||||||
|
self.use_id = card.id
|
||||||
|
self.special_skill = "recast"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
fk.ai_card.fire_attack = {
|
||||||
|
intention = 90, -- 身份值
|
||||||
|
value = 3, -- 卡牌价值
|
||||||
|
priority = 4 -- 使用优先值
|
||||||
|
}
|
||||||
|
|
||||||
|
fk.ai_use_play.fire_attack = function(self, card)
|
||||||
|
self:sort(self.enemies)
|
||||||
|
for _, p in ipairs(self.enemies) do
|
||||||
|
if card.skill:targetFilter(p.id, self.use_tos, {}, card) and #self.player:getCardIds("h") > 2 then
|
||||||
|
self.use_id = card.id
|
||||||
|
table.insert(self.use_tos, p.id)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
fk.ai_dis_card.fire_attack_skill = function(self, min_num, num, include_equip, cancelable, pattern, prompt)
|
||||||
|
local use = self:eventData("UseCard")
|
||||||
|
for _, p in ipairs(TargetGroup:getRealTargets(use.tos)) do
|
||||||
|
if self:isEnemie(p) then
|
||||||
|
local cards = table.map(self.player:getCardIds("h"), function(id)
|
||||||
|
return Fk:getCardById(id)
|
||||||
|
end)
|
||||||
|
local exp = Exppattern:Parse(pattern)
|
||||||
|
cards = table.filter(cards, function(c)
|
||||||
|
return exp:match(c)
|
||||||
|
end)
|
||||||
|
if #cards > 0 then
|
||||||
|
self:sortValue(cards)
|
||||||
|
return { cards[1].id }
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
fk.ai_nullification.fire_attack = function(self, card, to, from, positive)
|
||||||
|
if positive then
|
||||||
|
if self:isFriend(to) and #to:getCardIds("h") > 0 and #from:getCardIds("h") > 0 then
|
||||||
|
if #self.avail_cards > 1 or self:isWeak(to) or to.id == self.player.id then
|
||||||
|
self.use_id = self.avail_cards[1]
|
||||||
|
end
|
||||||
|
end
|
||||||
|
else
|
||||||
|
if self:isEnemie(to) and #to:getCardIds("h") > 0 and #from:getCardIds("h") > 1 then
|
||||||
|
if #self.avail_cards > 1 or self:isWeak(to) then
|
||||||
|
self.use_id = self.avail_cards[1]
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
fk.ai_card.fire_attack = {
|
||||||
|
intention = 120, -- 身份值
|
||||||
|
value = 2, -- 卡牌价值
|
||||||
|
priority = 2 -- 使用优先值
|
||||||
|
}
|
||||||
|
|
||||||
|
fk.ai_use_play.supply_shortage = function(self, card)
|
||||||
|
self:sort(self.enemies)
|
||||||
|
for _, p in ipairs(self.enemies) do
|
||||||
|
if card.skill:targetFilter(p.id, self.use_tos, {}, card) and not p.chained then
|
||||||
|
self.use_id = card.id
|
||||||
|
table.insert(self.use_tos, p.id)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
fk.ai_nullification.supply_shortage = function(self, card, to, from, positive)
|
||||||
|
if positive then
|
||||||
|
if self:isFriend(to) then
|
||||||
|
if #self.avail_cards > 1 or self:isWeak(to) or to.id == self.player.id then
|
||||||
|
self.use_id = self.avail_cards[1]
|
||||||
|
end
|
||||||
|
end
|
||||||
|
else
|
||||||
|
if self:isEnemie(to) then
|
||||||
|
if #self.avail_cards > 1 or self:isWeak(to) then
|
||||||
|
self.use_id = self.avail_cards[1]
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
fk.ai_card.supply_shortage = {
|
||||||
|
intention = 130, -- 身份值
|
||||||
|
value = 2, -- 卡牌价值
|
||||||
|
priority = 1 -- 使用优先值
|
||||||
|
}
|
||||||
|
|
||||||
|
fk.ai_skill_invoke["#fan_skill"] = function(self)
|
||||||
|
local use = self:eventData("UseCard")
|
||||||
|
for _, p in ipairs(TargetGroup:getRealTargets(use.tos)) do
|
||||||
|
if not self:isFriend(p) then
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -10,7 +10,7 @@ local function rewardAndPunish(killer, victim)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
GameRule = fk.CreateTriggerSkill{
|
GameRule = fk.CreateTriggerSkill {
|
||||||
name = "game_rule",
|
name = "game_rule",
|
||||||
events = {
|
events = {
|
||||||
fk.GamePrepared,
|
fk.GamePrepared,
|
||||||
|
@ -37,71 +37,96 @@ GameRule = fk.CreateTriggerSkill{
|
||||||
end
|
end
|
||||||
|
|
||||||
switch(event, {
|
switch(event, {
|
||||||
[fk.AskForPeaches] = function()
|
[fk.AskForPeaches] = function()
|
||||||
local dyingPlayer = room:getPlayerById(data.who)
|
local dyingPlayer = room:getPlayerById(data.who)
|
||||||
while dyingPlayer.hp < 1 do
|
while dyingPlayer.hp < 1 do
|
||||||
local cardNames = {"peach"}
|
local cardNames = { "peach" }
|
||||||
local prompt = "#AskForPeaches:" .. dyingPlayer.id .. "::" .. tostring(1 - dyingPlayer.hp)
|
local prompt = "#AskForPeaches:" .. data.who .. "::" .. tostring(1 - dyingPlayer.hp)
|
||||||
if player == dyingPlayer then
|
if player == dyingPlayer then
|
||||||
table.insert(cardNames, "analeptic")
|
table.insert(cardNames, "analeptic")
|
||||||
prompt = "#AskForPeachesSelf:::" .. tostring(1 - dyingPlayer.hp)
|
prompt = "#AskForPeachesSelf:::" .. tostring(1 - dyingPlayer.hp)
|
||||||
end
|
end
|
||||||
|
|
||||||
cardNames = table.filter(cardNames, function (cardName)
|
cardNames = table.filter(cardNames, function(cardName)
|
||||||
local cardCloned = Fk:cloneCard(cardName)
|
local cardCloned = Fk:cloneCard(cardName)
|
||||||
return not (player:prohibitUse(cardCloned) or player:isProhibited(dyingPlayer, cardCloned))
|
return not (player:prohibitUse(cardCloned) or player:isProhibited(dyingPlayer, cardCloned))
|
||||||
end)
|
end)
|
||||||
if #cardNames == 0 then return end
|
if #cardNames < 1 then return end
|
||||||
|
room:notifyMoveFocus(player, "peach")
|
||||||
local peach_use = room:askForUseCard(player, "peach", table.concat(cardNames, ",") , prompt)
|
local useData = {
|
||||||
if not peach_use then break end
|
user = player,
|
||||||
peach_use.tos = { {dyingPlayer.id} }
|
cardName = "peach",
|
||||||
if peach_use.card.trueName == "analeptic" then
|
pattern = table.concat(cardNames, ","),
|
||||||
peach_use.extra_data = peach_use.extra_data or {}
|
extraData = Util.DummyTable
|
||||||
peach_use.extra_data.analepticRecover = true
|
}
|
||||||
|
room.logic:trigger(fk.AskForCardUse, player, useData)
|
||||||
|
if type(useData.result) == "table" then
|
||||||
|
useData = useData.result
|
||||||
|
useData.tos = { { data.who } }
|
||||||
|
if useData.card.trueName == "analeptic" then
|
||||||
|
useData.extra_data = useData.extra_data or {}
|
||||||
|
useData.extra_data.analepticRecover = true
|
||||||
|
end
|
||||||
|
room:useCard(useData)
|
||||||
|
end
|
||||||
|
useData = { "peach", table.concat(cardNames, ","), prompt, true, Util.DummyTable }
|
||||||
|
while dyingPlayer.hp < 1 do
|
||||||
|
Fk.currentResponsePattern = table.concat(cardNames, ",")
|
||||||
|
local result = room:doRequest(player, "AskForUseCard", json.encode(useData))
|
||||||
|
Fk.currentResponsePattern = nil
|
||||||
|
if result ~= "" then
|
||||||
|
result = room:handleUseCardReply(player, result)
|
||||||
|
result.tos = { { data.who } }
|
||||||
|
if result.card.trueName == "analeptic" then
|
||||||
|
result.extra_data = result.extra_data or {}
|
||||||
|
result.extra_data.analepticRecover = true
|
||||||
|
end
|
||||||
|
room:useCard(result)
|
||||||
|
else
|
||||||
|
return
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
room:useCard(peach_use)
|
end,
|
||||||
end
|
[fk.AskForPeachesDone] = function()
|
||||||
end,
|
if player.hp < 1 and not data.ignoreDeath then
|
||||||
[fk.AskForPeachesDone] = function()
|
---@type DeathStruct
|
||||||
if player.hp < 1 and not data.ignoreDeath then
|
local deathData = {
|
||||||
---@type DeathStruct
|
who = player.id,
|
||||||
local deathData = {
|
damage = data.damage,
|
||||||
who = player.id,
|
}
|
||||||
damage = data.damage,
|
room:killPlayer(deathData)
|
||||||
}
|
end
|
||||||
room:killPlayer(deathData)
|
end,
|
||||||
end
|
[fk.GameOverJudge] = function()
|
||||||
end,
|
local winner = Fk.game_modes[room.settings.gameMode]:getWinner(player)
|
||||||
[fk.GameOverJudge] = function()
|
if winner ~= "" then
|
||||||
local winner = Fk.game_modes[room.settings.gameMode]:getWinner(player)
|
room:gameOver(winner)
|
||||||
if winner ~= "" then
|
return true
|
||||||
room:gameOver(winner)
|
end
|
||||||
return true
|
end,
|
||||||
end
|
[fk.BuryVictim] = function()
|
||||||
end,
|
player:bury()
|
||||||
[fk.BuryVictim] = function()
|
if room.tag["SkipNormalDeathProcess"] then
|
||||||
player:bury()
|
return false
|
||||||
if room.tag["SkipNormalDeathProcess"] then
|
end
|
||||||
return false
|
local damage = data.damage
|
||||||
end
|
if damage and damage.from then
|
||||||
local damage = data.damage
|
local killer = damage.from
|
||||||
if damage and damage.from then
|
rewardAndPunish(killer, player);
|
||||||
local killer = damage.from
|
end
|
||||||
rewardAndPunish(killer, player);
|
end,
|
||||||
end
|
default = function()
|
||||||
end,
|
print("game_rule: Event=" .. event)
|
||||||
default = function()
|
room:askForSkillInvoke(player, "rule")
|
||||||
print("game_rule: Event=" .. event)
|
end,
|
||||||
room:askForSkillInvoke(player, "rule")
|
|
||||||
end,
|
|
||||||
})
|
})
|
||||||
return false
|
return false
|
||||||
end,
|
end,
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
local fastchat_m = fk.CreateActiveSkill{ name = "fastchat_m" }
|
local fastchat_m = fk.CreateActiveSkill { name = "fastchat_m" }
|
||||||
local fastchat_f = fk.CreateActiveSkill{ name = "fastchat_f" }
|
local fastchat_f = fk.CreateActiveSkill { name = "fastchat_f" }
|
||||||
Fk:addSkill(fastchat_m)
|
Fk:addSkill(fastchat_m)
|
||||||
Fk:addSkill(fastchat_f)
|
Fk:addSkill(fastchat_f)
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,227 @@
|
||||||
|
fk.ai_use_play.rende = function(self, skill)
|
||||||
|
for _, p in ipairs(self.friends_noself) do
|
||||||
|
if p.kingdom == "shu" and #self.player:getCardIds("h") >= self.player.hp then
|
||||||
|
self.use_id = {}
|
||||||
|
for _, cid in ipairs(self.player:getCardIds("h")) do
|
||||||
|
if #self.use_id < #self.player:getCardIds("h") / 2 then
|
||||||
|
table.insert(self.use_id, cid)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
self.use_tos = { p.id }
|
||||||
|
return
|
||||||
|
end
|
||||||
|
end
|
||||||
|
for _, p in ipairs(self.friends_noself) do
|
||||||
|
if #self.player:getCardIds("h") >= self.player.hp then
|
||||||
|
self.use_id = {}
|
||||||
|
for _, cid in ipairs(self.player:getCardIds("h")) do
|
||||||
|
if #self.use_id < #self.player:getCardIds("h") / 2 then
|
||||||
|
table.insert(self.use_id, cid)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
self.use_tos = { p.id }
|
||||||
|
return
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
fk.ai_card.jijiang = { priority = 10 }
|
||||||
|
|
||||||
|
fk.ai_use_play.lijian = function(self, skill)
|
||||||
|
local c = Fk:cloneCard("duel")
|
||||||
|
c.skillName = "lijian"
|
||||||
|
for _, p in ipairs(self.enemies) do
|
||||||
|
for _, pt in ipairs(self.enemies) do
|
||||||
|
if p.gender == General.Male and pt.gender == General.Male and p.id ~= pt.id and
|
||||||
|
c.skill:targetFilter(pt.id, {}, p.id, c) then
|
||||||
|
self.use_id = { self.player:getCardIds("he")[1] }
|
||||||
|
self.use_tos = { pt.id, p.id }
|
||||||
|
break
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
for _, p in ipairs(self.friends_noself) do
|
||||||
|
for _, pt in ipairs(self.enemies) do
|
||||||
|
if p.gender == General.Male and pt.gender == General.Male and p.id ~= pt.id and
|
||||||
|
c.skill:targetFilter(pt.id, {}, p.id, c) then
|
||||||
|
self.use_id = { self.player:getCardIds("he")[1] }
|
||||||
|
self.use_tos = { pt.id, p.id }
|
||||||
|
break
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
fk.ai_card.lijian = { priority = 2 }
|
||||||
|
|
||||||
|
fk.ai_use_play.zhiheng = function(self, skill)
|
||||||
|
local card_ids = {}
|
||||||
|
for _, h in ipairs(self.player:getCardIds("he")) do
|
||||||
|
if #card_ids < #self.player:getCardIds("he") / 2 then
|
||||||
|
table.insert(card_ids, h)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
if #card_ids > 0 then
|
||||||
|
self.use_id = card_ids
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
fk.ai_use_play.kurou = function(self, skill)
|
||||||
|
if #self:getActives("peach") + self.player.hp > 1 then
|
||||||
|
local slash = Fk:cloneCard("slash")
|
||||||
|
if slash.skill:canUse(self.player, slash) and not self.player:prohibitUse(slash) then
|
||||||
|
fk.ai_use_play.slash(self, slash)
|
||||||
|
if self.use_id then
|
||||||
|
self.use_id = {}
|
||||||
|
self.use_tos = {}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
fk.ai_use_play.fanjian = function(self, skill)
|
||||||
|
for _, p in ipairs(self.enemies) do
|
||||||
|
if #self.player:getCardIds("h") > 0 then
|
||||||
|
self.use_id = {}
|
||||||
|
table.insert(self.use_tos, p.id)
|
||||||
|
break
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
fk.ai_use_play.jieyin = function(self, skill)
|
||||||
|
for cs, p in ipairs(self.friends_noself) do
|
||||||
|
cs = self.player:getCardIds("h")
|
||||||
|
if #cs > 1 and p.gender == General.Male and p:isWounded() then
|
||||||
|
self.use_id = { cs[1], cs[2] }
|
||||||
|
table.insert(self.use_tos, p.id)
|
||||||
|
break
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
fk.ai_use_play.qingnang = function(self, skill)
|
||||||
|
for cs, p in ipairs(self.friends) do
|
||||||
|
cs = self.player:getCardIds("h")
|
||||||
|
if #cs > 0 and p:isWounded() then
|
||||||
|
self.use_id = { cs[1] }
|
||||||
|
table.insert(self.use_tos, p.id)
|
||||||
|
break
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
fk.ai_skill_invoke.jianxiong = true
|
||||||
|
|
||||||
|
fk.ai_card.hujia = { priority = 10 }
|
||||||
|
|
||||||
|
fk.ai_response_card["#hujia-ask"] = function(self, pattern, prompt, cancelable, data)
|
||||||
|
local to = self.room:getPlayerById(tonumber(prompt:split(":")[2]))
|
||||||
|
if to and self:isFriend(to) then
|
||||||
|
self:setUseId(pattern)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
fk.ai_response_card["#jijiang-ask"] = fk.ai_response_card["#hujia-ask"]
|
||||||
|
|
||||||
|
fk.ai_skill_invoke.fankui = function(self, data, prompt)
|
||||||
|
local damage = self:eventData("Damage")
|
||||||
|
return damage and damage.from and not self:isFriend(damage.from)
|
||||||
|
end
|
||||||
|
|
||||||
|
fk.ai_response_card["#guicai-ask"] = function(self, pattern, prompt, cancelable, data)
|
||||||
|
local cards = table.map(self.player:getHandlyIds(true), function(id)
|
||||||
|
return Fk:getCardById(id)
|
||||||
|
end)
|
||||||
|
local id = self:getRetrialCardId(cards)
|
||||||
|
if id then
|
||||||
|
self.use_id = id
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
fk.ai_skill_invoke.ganglie = function(self, data, prompt)
|
||||||
|
local damage = self:eventData("Damage")
|
||||||
|
return damage and damage.from and not self:isFriend(damage.from)
|
||||||
|
end
|
||||||
|
|
||||||
|
fk.ai_skill_invoke.luoyi = function(self, data, prompt)
|
||||||
|
for _, p in ipairs(self.enemies) do
|
||||||
|
if #self:getActives("slash") > 0 and not self:isWeak() then
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
fk.ai_skill_invoke.tiandu = true
|
||||||
|
|
||||||
|
fk.ai_skill_invoke.yiji = true
|
||||||
|
|
||||||
|
fk.ai_skill_invoke.luoshen = true
|
||||||
|
|
||||||
|
fk.ai_skill_invoke.guanxing = true
|
||||||
|
|
||||||
|
fk.ai_skill_invoke.tieqi = function(self, data, prompt)
|
||||||
|
local use = self:eventData("UseCard")
|
||||||
|
for _, p in ipairs(TargetGroup:getRealTargets(use.tos)) do
|
||||||
|
p = self.room:getPlayerById(p)
|
||||||
|
if self:isEnemie(p) then
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
fk.ai_skill_invoke.jizhi = true
|
||||||
|
|
||||||
|
fk.ai_skill_invoke.keji = true
|
||||||
|
|
||||||
|
fk.ai_skill_invoke.yingzi = true
|
||||||
|
|
||||||
|
fk.ai_skill_invoke.lianying = true
|
||||||
|
|
||||||
|
fk.ai_skill_invoke.xiaoji = true
|
||||||
|
|
||||||
|
fk.ai_skill_invoke.biyue = true
|
||||||
|
|
||||||
|
fk.ai_choose_players.tuxi = function(self, targets, min_num, num, cancelable)
|
||||||
|
for _, pid in ipairs(targets) do
|
||||||
|
local p = self.room:getPlayerById(pid)
|
||||||
|
if self:isEnemie(p) and #self.use_tos < num then
|
||||||
|
table.insert(self.use_tos, pid)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
fk.ai_use_skill.yiji_active = function(self, prompt, cancelable, data)
|
||||||
|
for _, p in ipairs(self.friends_noself) do
|
||||||
|
for c, cid in ipairs(self.player.yiji_ids) do
|
||||||
|
c = Fk:getCardById(cid)
|
||||||
|
if c:getMark("yiji") > 0 and c.skill:canUse(p, c) then
|
||||||
|
self.use_tos = { p.id }
|
||||||
|
self.use_id = json.encode {
|
||||||
|
skill = "yiji_active",
|
||||||
|
subcards = { cid }
|
||||||
|
}
|
||||||
|
return
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
fk.ai_choose_players.liuli = function(self, targets, min_num, num, cancelable)
|
||||||
|
for _, pid in ipairs(targets) do
|
||||||
|
local p = self.room:getPlayerById(pid)
|
||||||
|
if self:isEnemie(p) and #self.use_tos < num and #self.player:getCardIds("he") > 0 then
|
||||||
|
table.insert(self.use_tos, pid)
|
||||||
|
self.use_id = { self.player:getCardIds("he")[1] }
|
||||||
|
return
|
||||||
|
end
|
||||||
|
end
|
||||||
|
for _, pid in ipairs(targets) do
|
||||||
|
local p = self.room:getPlayerById(pid)
|
||||||
|
if not self:isFriend(p) and #self.use_tos < num and #self.player:getCardIds("he") > 0 then
|
||||||
|
table.insert(self.use_tos, pid)
|
||||||
|
self.use_id = { self.player:getCardIds("he")[1] }
|
||||||
|
return
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -1,6 +1,6 @@
|
||||||
-- SPDX-License-Identifier: GPL-3.0-or-later
|
-- SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
Fk:loadTranslationTable{
|
Fk:loadTranslationTable {
|
||||||
["standard_cards"] = "标+EX",
|
["standard_cards"] = "标+EX",
|
||||||
|
|
||||||
["unknown_card"] = '<font color="#B5BA00"><b>未知牌</b></font>',
|
["unknown_card"] = '<font color="#B5BA00"><b>未知牌</b></font>',
|
||||||
|
@ -50,13 +50,12 @@ Fk:loadTranslationTable{
|
||||||
["slash"] = "杀",
|
["slash"] = "杀",
|
||||||
[":slash"] = "基本牌<br /><b>时机</b>:出牌阶段<br /><b>目标</b>:攻击范围内的一名其他角色<br /><b>效果</b>:对目标角色造成1点伤害。",
|
[":slash"] = "基本牌<br /><b>时机</b>:出牌阶段<br /><b>目标</b>:攻击范围内的一名其他角色<br /><b>效果</b>:对目标角色造成1点伤害。",
|
||||||
["#slash-jink"] = "%src 对你使用了杀,请使用 %arg 张闪",
|
["#slash-jink"] = "%src 对你使用了杀,请使用 %arg 张闪",
|
||||||
|
["#slash-jinks"] = "%src 对你使用了杀,需 %arg2 张闪,你还需使用 %arg 张闪",
|
||||||
|
|
||||||
["jink"] = "闪",
|
["jink"] = "闪",
|
||||||
[":jink"] = "基本牌<br /><b>时机</b>:【杀】对你生效时<br /><b>目标</b>:此【杀】<br /><b>效果</b>:抵消此【杀】的效果。",
|
[":jink"] = "基本牌<br /><b>时机</b>:【杀】对你生效时<br /><b>目标</b>:此【杀】<br /><b>效果</b>:抵消此【杀】的效果。",
|
||||||
|
|
||||||
["peach"] = "桃",
|
["peach"] = "桃",
|
||||||
[":peach"] = "基本牌<br /><b>时机</b>:出牌阶段/一名角色处于濒死状态时<br /><b>目标</b>:已受伤的你/处于濒死状态的角色<br /><b>效果</b>:目标角色回复1点体力。",
|
[":peach"] = "基本牌<br /><b>时机</b>:出牌阶段/一名角色处于濒死状态时<br /><b>目标</b>:已受伤的你/处于濒死状态的角色<br /><b>效果</b>:目标角色回复1点体力。",
|
||||||
|
|
||||||
["dismantlement"] = "过河拆桥",
|
["dismantlement"] = "过河拆桥",
|
||||||
[":dismantlement"] = "锦囊牌<br /><b>时机</b>:出牌阶段<br /><b>目标</b>:一名区域内有牌的其他角色。<br /><b>效果</b>:你弃置目标角色区域内的一张牌。",
|
[":dismantlement"] = "锦囊牌<br /><b>时机</b>:出牌阶段<br /><b>目标</b>:一名区域内有牌的其他角色。<br /><b>效果</b>:你弃置目标角色区域内的一张牌。",
|
||||||
["dismantlement_skill"] = "过河拆桥",
|
["dismantlement_skill"] = "过河拆桥",
|
||||||
|
@ -67,26 +66,19 @@ Fk:loadTranslationTable{
|
||||||
|
|
||||||
["duel"] = "决斗",
|
["duel"] = "决斗",
|
||||||
[":duel"] = "锦囊牌<br /><b>时机</b>:出牌阶段<br /><b>目标</b>:一名其他角色<br /><b>效果</b>:由目标角色开始,你与其轮流:打出一张【杀】,否则受到对方的1点伤害并结束此牌结算。",
|
[":duel"] = "锦囊牌<br /><b>时机</b>:出牌阶段<br /><b>目标</b>:一名其他角色<br /><b>效果</b>:由目标角色开始,你与其轮流:打出一张【杀】,否则受到对方的1点伤害并结束此牌结算。",
|
||||||
|
|
||||||
["collateral"] = "借刀杀人",
|
["collateral"] = "借刀杀人",
|
||||||
[":collateral"] = "锦囊牌<br /><b>时机</b>:出牌阶段<br /><b>目标</b>:装备区内有武器牌且攻击范围内有【杀】的合法目标的一名其他角色A(你需要选择一名A攻击范围内的【杀】的合法目标B)<br /><b>效果</b>:A须对B使用一张【杀】,否则你获得A装备区内的武器牌。",
|
[":collateral"] = "锦囊牌<br /><b>时机</b>:出牌阶段<br /><b>目标</b>:装备区内有武器牌且攻击范围内有【杀】的合法目标的一名其他角色A(你需要选择一名A攻击范围内的【杀】的合法目标B)<br /><b>效果</b>:A须对B使用一张【杀】,否则你获得A装备区内的武器牌。",
|
||||||
["#collateral-slash"] = "借刀杀人:你需对 %dest 使用【杀】,否则 %src 获得你的武器",
|
["#collateral-slash"] = "借刀杀人:你需对 %dest 使用【杀】,否则 %src 获得你的武器",
|
||||||
|
|
||||||
["ex_nihilo"] = "无中生有",
|
["ex_nihilo"] = "无中生有",
|
||||||
[":ex_nihilo"] = "锦囊牌<br /><b>时机</b>:出牌阶段<br /><b>目标</b>:你<br /><b>效果</b>:目标角色摸两张牌。",
|
[":ex_nihilo"] = "锦囊牌<br /><b>时机</b>:出牌阶段<br /><b>目标</b>:你<br /><b>效果</b>:目标角色摸两张牌。",
|
||||||
|
|
||||||
["nullification"] = "无懈可击",
|
["nullification"] = "无懈可击",
|
||||||
[":nullification"] = "锦囊牌<br /><b>时机</b>:锦囊牌对目标角色生效前,或一张【无懈可击】生效前<br /><b>目标</b>:该锦囊牌<br /><b>效果</b>:抵消该锦囊牌对该角色产生的效果,或抵消另一张【无懈可击】产生的效果。",
|
[":nullification"] = "锦囊牌<br /><b>时机</b>:锦囊牌对目标角色生效前,或一张【无懈可击】生效前<br /><b>目标</b>:该锦囊牌<br /><b>效果</b>:抵消该锦囊牌对该角色产生的效果,或抵消另一张【无懈可击】产生的效果。",
|
||||||
|
|
||||||
["savage_assault"] = "南蛮入侵",
|
["savage_assault"] = "南蛮入侵",
|
||||||
[":savage_assault"] = "锦囊牌<br /><b>时机</b>:出牌阶段<br /><b>目标</b>:所有其他角色<br /><b>效果</b>:每名目标角色须打出一张【杀】,否则受到1点伤害。",
|
[":savage_assault"] = "锦囊牌<br /><b>时机</b>:出牌阶段<br /><b>目标</b>:所有其他角色<br /><b>效果</b>:每名目标角色须打出一张【杀】,否则受到1点伤害。",
|
||||||
|
|
||||||
["archery_attack"] = "万箭齐发",
|
["archery_attack"] = "万箭齐发",
|
||||||
[":archery_attack"] = "锦囊牌<br /><b>时机</b>:出牌阶段<br /><b>目标</b>:所有其他角色<br /><b>效果</b>:每名目标角色须打出一张【闪】,否则受到1点伤害。",
|
[":archery_attack"] = "锦囊牌<br /><b>时机</b>:出牌阶段<br /><b>目标</b>:所有其他角色<br /><b>效果</b>:每名目标角色须打出一张【闪】,否则受到1点伤害。",
|
||||||
|
|
||||||
["god_salvation"] = "桃园结义",
|
["god_salvation"] = "桃园结义",
|
||||||
[":god_salvation"] = "锦囊牌<br /><b>时机</b>:出牌阶段<br /><b>目标</b>:所有角色<br /><b>效果</b>:每名目标角色回复1点体力。",
|
[":god_salvation"] = "锦囊牌<br /><b>时机</b>:出牌阶段<br /><b>目标</b>:所有角色<br /><b>效果</b>:每名目标角色回复1点体力。",
|
||||||
|
|
||||||
["amazing_grace"] = "五谷丰登",
|
["amazing_grace"] = "五谷丰登",
|
||||||
[":amazing_grace"] = "锦囊牌<br /><b>时机</b>:出牌阶段<br /><b>目标</b>:所有角色<br /><b>效果</b>:你亮出牌堆顶等于角色数的牌,每名目标角色获得其中一张牌,然后将其余的牌置入弃牌堆。",
|
[":amazing_grace"] = "锦囊牌<br /><b>时机</b>:出牌阶段<br /><b>目标</b>:所有角色<br /><b>效果</b>:你亮出牌堆顶等于角色数的牌,每名目标角色获得其中一张牌,然后将其余的牌置入弃牌堆。",
|
||||||
["amazing_grace_skill"] = "五谷选牌",
|
["amazing_grace_skill"] = "五谷选牌",
|
||||||
|
@ -94,16 +86,12 @@ Fk:loadTranslationTable{
|
||||||
|
|
||||||
["lightning"] = "闪电",
|
["lightning"] = "闪电",
|
||||||
[":lightning"] = "延时锦囊牌<br /><b>时机</b>:出牌阶段<br /><b>目标</b>:你<br /><b>效果</b>:将此牌置于目标角色判定区内。其判定阶段进行判定:若结果为黑桃2-9,其受到3点雷电伤害并将【闪电】置入弃牌堆,否则将【闪电】移动至其下家判定区内。",
|
[":lightning"] = "延时锦囊牌<br /><b>时机</b>:出牌阶段<br /><b>目标</b>:你<br /><b>效果</b>:将此牌置于目标角色判定区内。其判定阶段进行判定:若结果为黑桃2-9,其受到3点雷电伤害并将【闪电】置入弃牌堆,否则将【闪电】移动至其下家判定区内。",
|
||||||
|
|
||||||
["indulgence"] = "乐不思蜀",
|
["indulgence"] = "乐不思蜀",
|
||||||
[":indulgence"] = "延时锦囊牌<br /><b>时机</b>:出牌阶段<br /><b>目标</b>:一名其他角色<br /><b>效果</b>:将此牌置于目标角色判定区内。其判定阶段进行判定:若结果不为红桃,其跳过出牌阶段。然后将【乐不思蜀】置入弃牌堆。",
|
[":indulgence"] = "延时锦囊牌<br /><b>时机</b>:出牌阶段<br /><b>目标</b>:一名其他角色<br /><b>效果</b>:将此牌置于目标角色判定区内。其判定阶段进行判定:若结果不为红桃,其跳过出牌阶段。然后将【乐不思蜀】置入弃牌堆。",
|
||||||
|
|
||||||
["crossbow"] = "诸葛连弩",
|
["crossbow"] = "诸葛连弩",
|
||||||
[":crossbow"] = "装备牌·武器<br /><b>攻击范围</b>:1<br /><b>武器技能</b>:锁定技。你于出牌阶段内使用【杀】无次数限制。",
|
[":crossbow"] = "装备牌·武器<br /><b>攻击范围</b>:1<br /><b>武器技能</b>:锁定技。你于出牌阶段内使用【杀】无次数限制。",
|
||||||
|
|
||||||
["qinggang_sword"] = "青釭剑",
|
["qinggang_sword"] = "青釭剑",
|
||||||
[":qinggang_sword"] = "装备牌·武器<br /><b>攻击范围</b>:2<br /><b>武器技能</b>:锁定技。你的【杀】无视目标角色的防具。",
|
[":qinggang_sword"] = "装备牌·武器<br /><b>攻击范围</b>:2<br /><b>武器技能</b>:锁定技。你的【杀】无视目标角色的防具。",
|
||||||
|
|
||||||
["ice_sword"] = "寒冰剑",
|
["ice_sword"] = "寒冰剑",
|
||||||
[":ice_sword"] = "装备牌·武器<br /><b>攻击范围</b>:2<br /><b>武器技能</b>:每当你使用【杀】对目标角色造成伤害时,若该角色有牌,你可以防止此伤害,然后依次弃置其两张牌。",
|
[":ice_sword"] = "装备牌·武器<br /><b>攻击范围</b>:2<br /><b>武器技能</b>:每当你使用【杀】对目标角色造成伤害时,若该角色有牌,你可以防止此伤害,然后依次弃置其两张牌。",
|
||||||
["#ice_sword_skill"] = "寒冰剑",
|
["#ice_sword_skill"] = "寒冰剑",
|
||||||
|
@ -130,7 +118,6 @@ Fk:loadTranslationTable{
|
||||||
|
|
||||||
["halberd"] = "方天画戟",
|
["halberd"] = "方天画戟",
|
||||||
[":halberd"] = "装备牌·武器<br /><b>攻击范围</b>:4<br /><b>武器技能</b>:锁定技。你使用最后的手牌【杀】可以额外选择至多两名目标。",
|
[":halberd"] = "装备牌·武器<br /><b>攻击范围</b>:4<br /><b>武器技能</b>:锁定技。你使用最后的手牌【杀】可以额外选择至多两名目标。",
|
||||||
|
|
||||||
["kylin_bow"] = "麒麟弓",
|
["kylin_bow"] = "麒麟弓",
|
||||||
[":kylin_bow"] = "装备牌·武器<br /><b>攻击范围</b>:5<br /><b>武器技能</b>:每当你使用【杀】对目标角色造成伤害时,你可以弃置其装备区内的一张坐骑牌。",
|
[":kylin_bow"] = "装备牌·武器<br /><b>攻击范围</b>:5<br /><b>武器技能</b>:每当你使用【杀】对目标角色造成伤害时,你可以弃置其装备区内的一张坐骑牌。",
|
||||||
["#kylin_bow_skill"] = "麒麟弓",
|
["#kylin_bow_skill"] = "麒麟弓",
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,436 @@
|
||||||
|
fk.ai_card.slash = {
|
||||||
|
intention = 100, -- 身份值
|
||||||
|
value = 4, -- 卡牌价值
|
||||||
|
priority = 2.5 -- 使用优先值
|
||||||
|
}
|
||||||
|
fk.ai_card.peach = {
|
||||||
|
intention = -150,
|
||||||
|
value = 10,
|
||||||
|
priority = 0.5
|
||||||
|
}
|
||||||
|
fk.ai_card.dismantlement = {
|
||||||
|
intention = function(self, card, from)
|
||||||
|
if #self.player.player_cards[Player.Judge] < 1 then
|
||||||
|
return 80
|
||||||
|
elseif fk.ai_role[from.id] == "neutral" then
|
||||||
|
return 30
|
||||||
|
end
|
||||||
|
end,
|
||||||
|
value = 3.5,
|
||||||
|
priority = 10.5
|
||||||
|
}
|
||||||
|
fk.ai_card.snatch = {
|
||||||
|
intention = function(self, card, from)
|
||||||
|
if #self.player.player_cards[Player.Judge] < 1 then
|
||||||
|
return 80
|
||||||
|
elseif fk.ai_role[from.id] == "neutral" then
|
||||||
|
return 30
|
||||||
|
end
|
||||||
|
end,
|
||||||
|
value = 4.5,
|
||||||
|
priority = 10.4
|
||||||
|
}
|
||||||
|
fk.ai_card.duel = {
|
||||||
|
intention = 120,
|
||||||
|
value = 4.5,
|
||||||
|
priority = 3.5
|
||||||
|
}
|
||||||
|
fk.ai_card.collateral = {
|
||||||
|
intention = 20,
|
||||||
|
value = 3,
|
||||||
|
priority = 4.5
|
||||||
|
}
|
||||||
|
fk.ai_card.ex_nihilo = {
|
||||||
|
intention = -200,
|
||||||
|
value = 8,
|
||||||
|
priority = 10
|
||||||
|
}
|
||||||
|
fk.ai_card.savage_assault = {
|
||||||
|
intention = 20,
|
||||||
|
value = 2,
|
||||||
|
priority = 4
|
||||||
|
}
|
||||||
|
fk.ai_card.archery_attack = {
|
||||||
|
intention = 30,
|
||||||
|
value = 2,
|
||||||
|
priority = 3
|
||||||
|
}
|
||||||
|
fk.ai_card.god_salvation = {
|
||||||
|
intention = function(self, card, from)
|
||||||
|
if self.player.hp ~= self.player.maxHp then
|
||||||
|
return -45
|
||||||
|
end
|
||||||
|
end,
|
||||||
|
value = 1.5,
|
||||||
|
priority = 4
|
||||||
|
}
|
||||||
|
fk.ai_card.amazing_grace = {
|
||||||
|
intention = -30,
|
||||||
|
value = 2,
|
||||||
|
priority = 2
|
||||||
|
}
|
||||||
|
fk.ai_card.indulgence = {
|
||||||
|
intention = 150,
|
||||||
|
value = -1,
|
||||||
|
priority = 2
|
||||||
|
}
|
||||||
|
|
||||||
|
local function slashEeffect(slash, to)
|
||||||
|
for _, s in ipairs(to:getAllSkills()) do
|
||||||
|
if s.name == "#vine_skill" then
|
||||||
|
if slash.name == "slash" then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
end
|
||||||
|
if s.name == "#nioh_shield_skill" then
|
||||||
|
if slash.color == Card.Black then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
|
||||||
|
fk.ai_use_play.slash = function(self, card)
|
||||||
|
self:sort(self.enemies)
|
||||||
|
for _, p in ipairs(self.enemies) do
|
||||||
|
if card.skill:targetFilter(p.id, self.use_tos, {}, card) and slashEeffect(card, p) then
|
||||||
|
self.use_id = card.id
|
||||||
|
table.insert(self.use_tos, p.id)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
fk.ai_askuse_card["#slash-jink"] = function(self, pattern, prompt, cancelable, extra_data)
|
||||||
|
local act = self:getActives(pattern)
|
||||||
|
if tonumber(prompt:split(":")[4]) > #act then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
local cards =
|
||||||
|
table.map(
|
||||||
|
self.player:getCardIds("&he"),
|
||||||
|
function(id)
|
||||||
|
return Fk:getCardById(id)
|
||||||
|
end
|
||||||
|
)
|
||||||
|
self:sortValue(cards)
|
||||||
|
for _, sth in ipairs(act) do
|
||||||
|
if sth:isInstanceOf(Card) then
|
||||||
|
self.use_id = sth.id
|
||||||
|
break
|
||||||
|
else
|
||||||
|
local selected = {}
|
||||||
|
for _, c in ipairs(cards) do
|
||||||
|
if sth.cardFilter(sth, c.id, selected) then
|
||||||
|
table.insert(selected, c.id)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
local tc = sth.viewAs(sth, selected)
|
||||||
|
if tc and tc:matchPattern(pattern) then
|
||||||
|
self.use_id =
|
||||||
|
json.encode {
|
||||||
|
skill = sth.name,
|
||||||
|
subcards = selected
|
||||||
|
}
|
||||||
|
break
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
fk.ai_askuse_card["#slash-jinks"] = fk.ai_askuse_card["#slash-jink"]
|
||||||
|
|
||||||
|
fk.ai_use_play.snatch = function(self, card)
|
||||||
|
for _, p in ipairs(self.friends_noself) do
|
||||||
|
if card.skill:targetFilter(p.id, self.use_tos, {}, card) and #p:getCardIds("j") > 0 then
|
||||||
|
self.use_id = card.id
|
||||||
|
table.insert(self.use_tos, p.id)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
self:sort(self.enemies)
|
||||||
|
for _, p in ipairs(self.enemies) do
|
||||||
|
if card.skill:targetFilter(p.id, self.use_tos, {}, card) and #p:getCardIds("he") > 0 then
|
||||||
|
self.use_id = card.id
|
||||||
|
table.insert(self.use_tos, p.id)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
fk.ai_nullification.snatch = function(self, card, to, from, positive)
|
||||||
|
if positive then
|
||||||
|
if self:isFriend(to) and not self:isFriend(from) and fk.ai_role[from.id] ~= "neutral" then
|
||||||
|
if #self.avail_cards > 1 or self:isWeak(to) or to.id == self.player.id then
|
||||||
|
self.use_id = self.avail_cards[1]
|
||||||
|
end
|
||||||
|
end
|
||||||
|
else
|
||||||
|
if self:isEnemie(to) and self:isEnemie(from) then
|
||||||
|
if #self.avail_cards > 1 or self:isWeak(to) then
|
||||||
|
self.use_id = self.avail_cards[1]
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
fk.ai_use_play.dismantlement = function(self, card)
|
||||||
|
for _, p in ipairs(self.friends_noself) do
|
||||||
|
if card.skill:targetFilter(p.id, self.use_tos, {}, card) and #p:getCardIds("j") > 0 then
|
||||||
|
self.use_id = card.id
|
||||||
|
table.insert(self.use_tos, p.id)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
self:sort(self.enemies)
|
||||||
|
for _, p in ipairs(self.enemies) do
|
||||||
|
if card.skill:targetFilter(p.id, self.use_tos, {}, card) and #p:getCardIds("he") > 0 then
|
||||||
|
self.use_id = card.id
|
||||||
|
table.insert(self.use_tos, p.id)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
fk.ai_nullification.dismantlement = function(self, card, to, from, positive)
|
||||||
|
if positive then
|
||||||
|
if self:isFriend(to) and not self:isFriend(from) and fk.ai_role[from.id] ~= "neutral" then
|
||||||
|
if #self.avail_cards > 1 or self:isWeak(to) or to.id == self.player.id then
|
||||||
|
self.use_id = self.avail_cards[1]
|
||||||
|
end
|
||||||
|
end
|
||||||
|
else
|
||||||
|
if self:isEnemie(to) and self:isEnemie(from) then
|
||||||
|
if #self.avail_cards > 1 or self:isWeak(to) then
|
||||||
|
self.use_id = self.avail_cards[1]
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
fk.ai_use_play.indulgence = function(self, card)
|
||||||
|
self:sort(self.enemies, nil, true)
|
||||||
|
for _, p in ipairs(self.enemies) do
|
||||||
|
if card.skill:targetFilter(p.id, self.use_tos, {}, card) then
|
||||||
|
self.use_id = card.id
|
||||||
|
table.insert(self.use_tos, p.id)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
fk.ai_nullification.indulgence = function(self, card, to, from, positive)
|
||||||
|
if positive then
|
||||||
|
if self:isFriend(to) then
|
||||||
|
if #self.avail_cards > 1 or self:isWeak(to) or to.id == self.player.id then
|
||||||
|
self.use_id = self.avail_cards[1]
|
||||||
|
end
|
||||||
|
end
|
||||||
|
else
|
||||||
|
if self:isEnemie(to) then
|
||||||
|
if #self.avail_cards > 1 or self:isWeak(to) then
|
||||||
|
self.use_id = self.avail_cards[1]
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
fk.ai_use_play.collateral = function(self, card)
|
||||||
|
local max = (card.skill:getMaxTargetNum(self.player, card) - 1) * 2
|
||||||
|
self:sort(self.enemies)
|
||||||
|
for _, p in ipairs(self.enemies) do
|
||||||
|
if #self.use_tos < max and card.skill:targetFilter(p.id, {}, {}, card) then
|
||||||
|
for _, pt in ipairs(self.enemies) do
|
||||||
|
if p ~= pt and p:inMyAttackRange(pt) then
|
||||||
|
table.insert(self.use_tos, p.id)
|
||||||
|
table.insert(self.use_tos, pt.id)
|
||||||
|
self.use_id = card.id
|
||||||
|
break
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
for _, p in ipairs(self.friends_noself) do
|
||||||
|
if #self.use_tos < max and card.skill:targetFilter(p.id, {}, {}, card) then
|
||||||
|
for _, pt in ipairs(self.enemies) do
|
||||||
|
if p ~= pt and p:inMyAttackRange(pt) then
|
||||||
|
table.insert(self.use_tos, p.id)
|
||||||
|
table.insert(self.use_tos, pt.id)
|
||||||
|
self.use_id = card.id
|
||||||
|
break
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
fk.ai_nullification.collateral = function(self, card, to, from, positive)
|
||||||
|
if positive then
|
||||||
|
if self:isFriend(to) and self:isEnemie(from) then
|
||||||
|
if #self.avail_cards > 1 or self:isWeak(to) or to.id == self.player.id then
|
||||||
|
self.use_id = self.avail_cards[1]
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
fk.ai_nullification.ex_nihilo = function(self, card, to, from, positive)
|
||||||
|
if positive then
|
||||||
|
if self:isEnemie(to) then
|
||||||
|
if #self.avail_cards > 1 or self:isWeak(to) then
|
||||||
|
self.use_id = self.avail_cards[1]
|
||||||
|
end
|
||||||
|
end
|
||||||
|
else
|
||||||
|
if self:isFriend(to) then
|
||||||
|
if #self.avail_cards > 1 or self:isWeak(to) or to.id == self.player.id then
|
||||||
|
self.use_id = self.avail_cards[1]
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
fk.ai_nullification.savage_assault = function(self, card, to, from, positive)
|
||||||
|
if positive then
|
||||||
|
if self:isFriend(to) then
|
||||||
|
if #self.avail_cards > 1 or self:isWeak(to) or to.id == self.player.id then
|
||||||
|
self.use_id = self.avail_cards[1]
|
||||||
|
end
|
||||||
|
end
|
||||||
|
else
|
||||||
|
if self:isEnemie(to) then
|
||||||
|
if #self.avail_cards > 1 or self:isWeak(to) then
|
||||||
|
self.use_id = self.avail_cards[1]
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
fk.ai_nullification.archery_attack = function(self, card, to, from, positive)
|
||||||
|
if positive then
|
||||||
|
if self:isFriend(to) then
|
||||||
|
if #self.avail_cards > 1 or self:isWeak(to) or to.id == self.player.id then
|
||||||
|
self.use_id = self.avail_cards[1]
|
||||||
|
end
|
||||||
|
end
|
||||||
|
else
|
||||||
|
if self:isEnemie(to) then
|
||||||
|
if #self.avail_cards > 1 or self:isWeak(to) then
|
||||||
|
self.use_id = self.avail_cards[1]
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
fk.ai_nullification.god_salvation = function(self, card, to, from, positive)
|
||||||
|
if positive then
|
||||||
|
if self:isEnemie(to) and to.hp ~= to.maxHp then
|
||||||
|
if #self.avail_cards > 1 or self:isWeak(to) then
|
||||||
|
self.use_id = self.avail_cards[1]
|
||||||
|
end
|
||||||
|
end
|
||||||
|
else
|
||||||
|
if self:isFriend(to) and to.hp ~= to.maxHp then
|
||||||
|
if #self.avail_cards > 1 or self:isWeak(to) or to.id == self.player.id then
|
||||||
|
self.use_id = self.avail_cards[1]
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
fk.ai_use_play.god_salvation = function(self, card)
|
||||||
|
local can = 0
|
||||||
|
for _, p in ipairs(self.enemies) do
|
||||||
|
if p:isWounded()
|
||||||
|
then
|
||||||
|
can = can - 1
|
||||||
|
if self:isWeak(p)
|
||||||
|
then
|
||||||
|
can = can - 1
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
for _, p in ipairs(self.friends) do
|
||||||
|
if p:isWounded()
|
||||||
|
then
|
||||||
|
can = can + 1
|
||||||
|
if self:isWeak(p)
|
||||||
|
then
|
||||||
|
can = can + 1
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
self.use_id = can > 0 and card.id
|
||||||
|
end
|
||||||
|
|
||||||
|
fk.ai_use_play.amazing_grace = function(self, card)
|
||||||
|
self.use_id = #self.player:getCardIds("&h") <= self.player.hp and card.id
|
||||||
|
end
|
||||||
|
|
||||||
|
fk.ai_use_play.ex_nihilo = function(self, card)
|
||||||
|
self.use_id = card.id
|
||||||
|
end
|
||||||
|
|
||||||
|
fk.ai_use_play.lightning = function(self, card)
|
||||||
|
self.use_id = #self.enemies > #self.friends and card.id
|
||||||
|
end
|
||||||
|
|
||||||
|
fk.ai_use_play.peach = function(self, card)
|
||||||
|
if self.command == "PlayCard" then
|
||||||
|
self.use_id = self.player.hp ~= self.player.maxHp and self.player.hp < #self.player:getCardIds("h") and card.id
|
||||||
|
else
|
||||||
|
for _, p in ipairs(self.friends) do
|
||||||
|
if p.dying then
|
||||||
|
self.use_id = card.id
|
||||||
|
self.use_tos = { p.id }
|
||||||
|
break
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
fk.ai_use_play.duel = function(self, card)
|
||||||
|
self:sort(self.enemies)
|
||||||
|
for _, p in ipairs(self.enemies) do
|
||||||
|
if card.skill:targetFilter(p.id, self.use_tos, {}, card) then
|
||||||
|
self.use_id = card.id
|
||||||
|
table.insert(self.use_tos, p.id)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
fk.ai_skill_invoke["#ice_sword_skill"] = function(self)
|
||||||
|
local damage = self:eventData("Damage")
|
||||||
|
return self:isFriend(damage.to) or not self:isWeak(damage.to) and #damage.to:getCardIds("e") > 1
|
||||||
|
end
|
||||||
|
|
||||||
|
fk.ai_skill_invoke["#double_swords_skill"] = function(self)
|
||||||
|
local use = self:eventData("UseCard")
|
||||||
|
for _, p in ipairs(TargetGroup:getRealTargets(use.tos)) do
|
||||||
|
if not self:isFriend(p) and self.room:getPlayerById(p).gender ~= self.player.gender then
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
fk.ai_dis_card["#double_swords_skill"] = function(self, min_num, num, include_equip, cancelable, pattern, prompt)
|
||||||
|
local use = self:eventData("UseCard")
|
||||||
|
return self:isEnemie(use.from) and { self.player:getCardIds("h")[1] }
|
||||||
|
end
|
||||||
|
|
||||||
|
fk.ai_dis_card["#axe_skill"] = function(self, min_num, num, include_equip, cancelable, pattern, prompt)
|
||||||
|
local ids = {}
|
||||||
|
for _, cid in ipairs(self.player:getCardIds("he")) do
|
||||||
|
if Fk:getCardById(cid):matchPattern(pattern) then
|
||||||
|
table.insert(ids, cid)
|
||||||
|
end
|
||||||
|
if
|
||||||
|
#ids >= min_num and self:isEnemie(self.player.axe_to) and
|
||||||
|
(self:isWeak(self.player.axe_to) or #self.player:getCardIds("he") > 3)
|
||||||
|
then
|
||||||
|
return ids
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
fk.ai_skill_invoke["#kylin_bow_skill"] = function(self)
|
||||||
|
local damage = self:eventData("Damage")
|
||||||
|
return not self:isFriend(damage.to)
|
||||||
|
end
|
||||||
|
|
||||||
|
fk.ai_skill_invoke["#eight_diagram_skill"] = true
|
|
@ -90,12 +90,6 @@ local control = fk.CreateActiveSkill{
|
||||||
-- room:swapSeat(from, to)
|
-- room:swapSeat(from, to)
|
||||||
for _, pid in ipairs(effect.tos) do
|
for _, pid in ipairs(effect.tos) do
|
||||||
local to = room:getPlayerById(pid)
|
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
|
if to:getMark("mouxushengcontrolled") == 0 then
|
||||||
room:addPlayerMark(to, "mouxushengcontrolled")
|
room:addPlayerMark(to, "mouxushengcontrolled")
|
||||||
from:control(to)
|
from:control(to)
|
||||||
|
@ -130,22 +124,6 @@ local control = fk.CreateActiveSkill{
|
||||||
-- room:useVirtualCard("slash", nil, from, room:getOtherPlayers(from), self.name, true)
|
-- room:useVirtualCard("slash", nil, from, room:getOtherPlayers(from), self.name, true)
|
||||||
end,
|
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{
|
local test_vs = fk.CreateViewAsSkill{
|
||||||
name = "test_vs",
|
name = "test_vs",
|
||||||
pattern = "nullification",
|
pattern = "nullification",
|
||||||
|
|
Loading…
Reference in New Issue