Modify game core (#294)

- 新增船新“休整”机制;
- 修改作废逻辑,并可在当前响应读条禁用该技能(出牌阶段空闲时间点尚未完成限制);
- 修复锁视技的相关bug,其cardFilter新增标识是否为判定的参数;
- 将护甲扣减融合进体力扣减流程,为伤害流程增加“虚拟伤害”概念,为伤害流程增加“造成过伤害”标识id以供记录搜索使用;
- 为变将新增可删除副将。

---------

Co-authored-by: notify <notify-ctrl@qq.com>
This commit is contained in:
Ho-spair 2023-12-10 18:55:16 +08:00 committed by GitHub
parent 94c1107c2e
commit cec18e0614
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
33 changed files with 629 additions and 228 deletions

20
Fk/Common/Avatar.qml Normal file
View File

@ -0,0 +1,20 @@
import QtQuick
import Fk
Image {
property string general
width: 64
height: 64
source: SkinBank.getGeneralExtraPic(general, "avatar/") ?? SkinBank.getGeneralPicture(general)
// sourceSize.width: 250
// sourceSize.height: 292
property bool useSmallPic: !!SkinBank.getGeneralExtraPic(general, "avatar/")
sourceClipRect: useSmallPic ? undefined : Qt.rect(61, 0, 128, 128)
Rectangle {
anchors.fill: parent
color: "transparent"
border.width: 1
}
}

238
Fk/Common/AvatarChatBox.qml Normal file
View File

@ -0,0 +1,238 @@
// SPDX-License-Identifier: GPL-3.0-or-later
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
import Fk.Pages
Rectangle {
property bool isLobby: false
function append(chatter, data) {
let general = data.general;
let avatar;
if (general == "__server") {
general = "";
avatar = "__server"
} else if (!roomScene.getPhoto(data.sender)) {
avatar = "__observer";
}
chatLogBox.append({
avatar: data.general || roomScene.getPhoto(data.sender)?.general || avatar || "unknown",
general: general,
msg: data.msg,
userName: data.userName,
time: data.time,
isSelf: data.sender === Self.id,
})
}
function loadSkills() {
for (let i = 1; i <= 16; i++) {
skills.append({ name: "fastchat_m", idx: i });
}
}
Timer {
id: opTimer
interval: 1500
}
Component {
id: avatarDelegate
Item {
width: chatLogBox.width
height: childrenRect.height
Avatar {
id: avatarPic
width: 36
height: 36
general: avatar
anchors.top: parent.top
anchors.topMargin: 8
anchors.left: isSelf ? undefined : parent.left
anchors.right: !isSelf ? undefined : parent.right
}
Text {
id: unameLbl
anchors.left: isSelf ? undefined : avatarPic.right
anchors.right: !isSelf ? undefined : avatarPic.left
anchors.margins: 6
font.pixelSize: 14
text: userName + (general ? (" (" + Backend.translate(general) + ")") : "")
+ ' <font color="grey">[' + time + "]</font>"
}
Rectangle {
anchors.left: isSelf ? undefined : avatarPic.right
anchors.right: !isSelf ? undefined : avatarPic.left
anchors.margins: 4
anchors.top: unameLbl.bottom
width: Math.min(parent.width - 80, childrenRect.width + 12)
height: childrenRect.height + 12
radius: 8
color: isSelf ? "lightgreen" : "lightsteelblue"
Text {
width: Math.min(contentWidth, parent.parent.width - 80 - 12)
x: 6; y: 6
text: msg
wrapMode: Text.WrapAnywhere
font.family: fontLibian.name
font.pixelSize: 16
}
}
TapHandler {
acceptedButtons: Qt.LeftButton | Qt.RightButton | Qt.NoButton
gesturePolicy: TapHandler.WithinBounds
onTapped: chatLogBox.currentIndex = index;
}
}
}
ColumnLayout {
anchors.fill: parent
spacing: 0
Item {
Layout.fillWidth: true
Layout.fillHeight: true
LogEdit {
id: chatLogBox
anchors.fill: parent
anchors.margins: 10
delegate: avatarDelegate
//font.pixelSize: 14
}
}
GridView {
id: emojiSelector
Layout.fillWidth: true
Layout.preferredHeight: 120
cellHeight: 48
cellWidth: 48
model: 59
visible: false
clip: true
delegate: ItemDelegate {
Image {
height: 32; width: 32
anchors.centerIn: parent
source: "../../image/emoji/" + index
}
onClicked: chatEdit.insert(chatEdit.cursorPosition, "{emoji" + index + "}");
}
}
ListView {
id: soundSelector
Layout.fillWidth: true
Layout.preferredHeight: 180
visible: false
clip: true
ScrollBar.vertical: ScrollBar {}
model: ListModel {
id: skills
}
// onVisibleChanged: {skills.clear(); loadSkills();}
delegate: ItemDelegate {
width: soundSelector.width
height: 30
text: Backend.translate("$" + name + (idx ? idx.toString() : ""))
onClicked: {
opTimer.start();
const general = roomScene.getPhoto(Self.id).general;
let skill = "fastchat_m";
if (general !== "") {
const data = JSON.parse(Backend.callLuaFunction("GetGeneralDetail", [general]));
const gender = data.gender;
if (gender !== 1) {
skill = "fastchat_f";
}
}
ClientInstance.notifyServer(
"Chat",
JSON.stringify({
type: isLobby ? 1 : 2,
msg: "$" + skill + ":" + idx
})
);
soundSelector.visible = false;
}
}
}
RowLayout {
Rectangle {
Layout.fillWidth: true
Layout.preferredHeight: 28
color: "#040403"
radius: 3
border.width: 1
border.color: "#A6967A"
TextInput {
id: chatEdit
anchors.fill: parent
anchors.margins: 6
color: "white"
clip: true
font.pixelSize: 14
maximumLength: 300
onAccepted: {
if (text != "") {
ClientInstance.notifyServer(
"Chat",
JSON.stringify({
type: isLobby ? 1 : 2,
msg: text
})
);
text = "";
}
}
}
}
MetroButton {
id: soundBtn
text: "🗨️"
visible: !isLobby
enabled: !opTimer.running;
onClicked: {
emojiSelector.visible = false;
soundSelector.visible = !soundSelector.visible;
}
}
MetroButton {
id: emojiBtn
text: "😃"
onClicked: {
soundSelector.visible = false;
emojiSelector.visible = !emojiSelector.visible;
}
}
MetroButton {
text: "✔️"
enabled: !opTimer.running;
onClicked: {
opTimer.start();
chatEdit.accepted();
}
}
}
}
Component.onCompleted: {
loadSkills();
}
}

View File

@ -9,7 +9,7 @@ Rectangle {
property bool isLobby: false property bool isLobby: false
function append(chatter) { function append(chatter) {
chatLogBox.append(chatter) chatLogBox.append({ logText: chatter })
} }
function loadSkills() { function loadSkills() {

View File

@ -33,6 +33,8 @@ ListView {
font.pixelSize: 16 font.pixelSize: 16
TapHandler { TapHandler {
acceptedButtons: Qt.LeftButton | Qt.RightButton | Qt.NoButton
gesturePolicy: TapHandler.WithinBounds
onTapped: root.currentIndex = index; onTapped: root.currentIndex = index;
} }
} }
@ -43,9 +45,9 @@ ListView {
onClicked: root.currentIndex = logModel.count - 1; onClicked: root.currentIndex = logModel.count - 1;
} }
function append(text) { function append(data) {
const autoScroll = root.currentIndex === logModel.count - 1; const autoScroll = root.currentIndex === logModel.count - 1;
logModel.append({ logText: text }); logModel.append(data);
if (autoScroll) { if (autoScroll) {
root.currentIndex = logModel.count - 1; root.currentIndex = logModel.count - 1;
} }

View File

@ -1,3 +1,5 @@
module Fk.Common module Fk.Common
ChatBox 1.0 ChatBox.qml ChatBox 1.0 ChatBox.qml
LogEdit 1.0 LogEdit.qml LogEdit 1.0 LogEdit.qml
Avatar 1.0 Avatar.qml
AvatarChatBox 1.0 AvatarChatBox.qml

View File

@ -3,6 +3,7 @@
import QtQuick import QtQuick
import QtQuick.Layouts import QtQuick.Layouts
import Fk import Fk
import Fk.Common
Item { Item {
id: root id: root
@ -20,19 +21,10 @@ Item {
RowLayout { RowLayout {
Item { Layout.preferredWidth: 16 } Item { Layout.preferredWidth: 16 }
Image { Avatar {
Layout.preferredWidth: 64 Layout.preferredWidth: 64
Layout.preferredHeight: 64 Layout.preferredHeight: 64
source: SkinBank.getGeneralExtraPic(Self.avatar, "avatar/") ?? SkinBank.getGeneralPicture(Self.avatar) general: Self.avatar
// sourceSize.width: 250
// sourceSize.height: 292
sourceClipRect: sourceSize.width > 200 ? Qt.rect(61, 0, 128, 128) : undefined
Rectangle {
anchors.fill: parent
color: "transparent"
border.width: 1
}
} }
Item { Layout.preferredWidth: 8 } Item { Layout.preferredWidth: 8 }

View File

@ -5,6 +5,7 @@ import QtQuick.Controls
import QtQuick.Layouts import QtQuick.Layouts
import QtQuick.Dialogs import QtQuick.Dialogs
import Fk import Fk
import Fk.Common
Item { Item {
id: root id: root
@ -59,21 +60,13 @@ Item {
width: root.width width: root.width
height: 64 height: 64
Image { Avatar {
id: generalPic id: generalPic
width: 48; height: 48
anchors.top: parent.top anchors.top: parent.top
anchors.left: parent.left anchors.left: parent.left
anchors.margins: 8 anchors.margins: 8
width: 48 general: general
height: 48
source: SkinBank.getGeneralExtraPic(general, "avatar/") ?? SkinBank.getGeneralPicture(general)
sourceClipRect: sourceSize.width > 200 ? Qt.rect(61, 0, 128, 128) : undefined
Rectangle {
anchors.fill: parent
color: "transparent"
border.width: 1
}
} }
ColumnLayout { ColumnLayout {

View File

@ -392,6 +392,7 @@ Item {
faceup: model.faceup faceup: model.faceup
chained: model.chained chained: model.chained
drank: model.drank drank: model.drank
rest: model.rest
isOwner: model.isOwner isOwner: model.isOwner
ready: model.ready ready: model.ready
surrendered: model.surrendered surrendered: model.surrendered
@ -855,7 +856,7 @@ Item {
} }
Item { Item {
visible: !config.replaying visible: !config.replaying
ChatBox { AvatarChatBox {
id: chat id: chat
anchors.fill: parent anchors.fill: parent
} }
@ -1121,7 +1122,7 @@ Item {
if (raw.msg.startsWith("$")) { if (raw.msg.startsWith("$")) {
if (specialChat(pid, raw, raw.msg.slice(1))) return; if (specialChat(pid, raw, raw.msg.slice(1))) return;
} }
chat.append(msg); chat.append(msg, raw);
const photo = Logic.getPhoto(pid); const photo = Logic.getPhoto(pid);
if (photo === undefined) { if (photo === undefined) {
const user = raw.userName; const user = raw.userName;
@ -1176,10 +1177,11 @@ Item {
Backend.playSound("./packages/" + extension + "/audio/death/" + g); Backend.playSound("./packages/" + extension + "/audio/death/" + g);
const m = Backend.translate("~" + g); const m = Backend.translate("~" + g);
data.msg = m;
if (general === "") if (general === "")
chat.append(`[${time}] ${userName}: ${m}`); chat.append(`[${time}] ${userName}: ${m}`, data);
else else
chat.append(`[${time}] ${userName}(${general}): ${m}`); chat.append(`[${time}] ${userName}(${general}): ${m}`, data);
const photo = Logic.getPhoto(pid); const photo = Logic.getPhoto(pid);
if (photo === undefined) { if (photo === undefined) {
@ -1205,10 +1207,11 @@ Item {
})); }));
} catch (e) {} } catch (e) {}
const m = Backend.translate("$" + skill + (gene ? "_" + gene : "") + (idx ? idx.toString() : "")); const m = Backend.translate("$" + skill + (gene ? "_" + gene : "") + (idx ? idx.toString() : ""));
data.msg = m;
if (general === "") if (general === "")
chat.append(`[${time}] ${userName}: ${m}`); chat.append(`[${time}] ${userName}: ${m}`, data);
else else
chat.append(`[${time}] ${userName}(${general}): ${m}`); chat.append(`[${time}] ${userName}(${general}): ${m}`, data)
const photo = Logic.getPhoto(pid); const photo = Logic.getPhoto(pid);
if (photo === undefined) { if (photo === undefined) {
@ -1224,12 +1227,17 @@ Item {
} }
function addToLog(msg) { function addToLog(msg) {
log.append(msg); log.append({ logText: msg });
} }
function sendDanmaku(msg) { function sendDanmaku(msg) {
danmaku.sendLog(msg); danmaku.sendLog(msg);
chat.append(msg); chat.append(null, {
msg: msg,
general: "__server", // FIXME:
userName: "",
time: "Server",
});
} }
function showDistance(show) { function showDistance(show) {
@ -1327,6 +1335,7 @@ Item {
faceup: true, faceup: true,
chained: false, chained: false,
drank: 0, drank: 0,
rest: 0,
isOwner: false, isOwner: false,
ready: false, ready: false,
surrendered: false, surrendered: false,

View File

@ -1319,6 +1319,7 @@ callbacks["AskForUseCard"] = (jsonData) => {
const pattern = data[1]; const pattern = data[1];
const prompt = data[2]; const prompt = data[2];
const extra_data = data[4]; const extra_data = data[4];
const disabledSkillNames = data[5];
if (extra_data != null) { if (extra_data != null) {
if (extra_data.effectTo !== Self.id && roomScene.skippedUseEventId.find(id => id === extra_data.useEventId)) { if (extra_data.effectTo !== Self.id && roomScene.skippedUseEventId.find(id => id === extra_data.useEventId)) {
doCancelButton(); doCancelButton();
@ -1336,6 +1337,7 @@ callbacks["AskForUseCard"] = (jsonData) => {
} }
roomScene.responding_card = pattern; roomScene.responding_card = pattern;
roomScene.respond_play = false; roomScene.respond_play = false;
disabledSkillNames && (dashboard.disabledSkillNames = disabledSkillNames);
roomScene.state = "responding"; roomScene.state = "responding";
okButton.enabled = false; okButton.enabled = false;
cancelButton.enabled = true; cancelButton.enabled = true;
@ -1347,6 +1349,7 @@ callbacks["AskForResponseCard"] = (jsonData) => {
const cardname = data[0]; const cardname = data[0];
const pattern = data[1]; const pattern = data[1];
const prompt = data[2]; const prompt = data[2];
const disabledSkillNames = data[5];
if (prompt === "") { if (prompt === "") {
roomScene.promptText = Backend.translate("#AskForResponseCard") roomScene.promptText = Backend.translate("#AskForResponseCard")
@ -1356,6 +1359,7 @@ callbacks["AskForResponseCard"] = (jsonData) => {
} }
roomScene.responding_card = pattern; roomScene.responding_card = pattern;
roomScene.respond_play = true; roomScene.respond_play = true;
disabledSkillNames && (dashboard.disabledSkillNames = disabledSkillNames);
roomScene.state = "responding"; roomScene.state = "responding";
okButton.enabled = false; okButton.enabled = false;
cancelButton.enabled = true; cancelButton.enabled = true;

View File

@ -19,6 +19,8 @@ RowLayout {
property var expanded_piles: ({}) // name -> int[] property var expanded_piles: ({}) // name -> int[]
property var disabledSkillNames: []
signal cardSelected(var card) signal cardSelected(var card)
Item { width: 5 } Item { width: 5 }
@ -454,6 +456,11 @@ RowLayout {
// if cname is presented, we are responding use or play. // if cname is presented, we are responding use or play.
for (let i = 0; i < skillButtons.count; i++) { for (let i = 0; i < skillButtons.count; i++) {
const item = skillButtons.itemAt(i); const item = skillButtons.itemAt(i);
if (disabledSkillNames.includes(item.orig)) {
item.enabled = false;
continue;
}
const fitpattern = JSON.parse(Backend.callLuaFunction("SkillFitPattern", [item.orig, cname])); const fitpattern = JSON.parse(Backend.callLuaFunction("SkillFitPattern", [item.orig, cname]));
const canresp = JSON.parse(Backend.callLuaFunction("SkillCanResponse", [item.orig, cardResponsing])); const canresp = JSON.parse(Backend.callLuaFunction("SkillCanResponse", [item.orig, cardResponsing]));
item.enabled = fitpattern && canresp; item.enabled = fitpattern && canresp;
@ -462,11 +469,17 @@ RowLayout {
} }
for (let i = 0; i < skillButtons.count; i++) { for (let i = 0; i < skillButtons.count; i++) {
const item = skillButtons.itemAt(i); const item = skillButtons.itemAt(i);
if (disabledSkillNames.includes(item.orig)) {
item.enabled = false;
continue;
}
item.enabled = JSON.parse(Backend.callLuaFunction("ActiveCanUse", [item.orig])); item.enabled = JSON.parse(Backend.callLuaFunction("ActiveCanUse", [item.orig]));
} }
} }
function disableSkills() { function disableSkills() {
disabledSkillNames = [];
for (let i = 0; i < skillButtons.count; i++) for (let i = 0; i < skillButtons.count; i++)
skillButtons.itemAt(i).enabled = false; skillButtons.itemAt(i).enabled = false;
} }

View File

@ -3,6 +3,7 @@
import QtQuick import QtQuick
import Qt5Compat.GraphicalEffects import Qt5Compat.GraphicalEffects
import QtQuick.Controls import QtQuick.Controls
import QtQuick.Layouts
import Fk import Fk
import Fk.PhotoElement import Fk.PhotoElement
@ -29,6 +30,7 @@ Item {
property bool faceup: true property bool faceup: true
property bool chained: false property bool chained: false
property int drank: 0 property int drank: 0
property int rest: 0
property bool isOwner: false property bool isOwner: false
property bool ready: false property bool ready: false
property int winGame: 0 property int winGame: 0
@ -269,6 +271,45 @@ Item {
opacity: 0.4 + Math.log(root.drank) * 0.12 opacity: 0.4 + Math.log(root.drank) * 0.12
} }
ColumnLayout {
id: restRect
anchors.centerIn: photoMask
anchors.leftMargin: 20
visible: root.rest > 0
Text {
Layout.alignment: Qt.AlignCenter
text: "休整中"
font.family: fontLibian.name
font.pixelSize: 40
color: "white"
style: Text.Outline
textFormat: Text.RichText
}
Text {
Layout.alignment: Qt.AlignCenter
visible: root.rest > 0 && root.rest < 999
text: root.rest
font.family: fontLibian.name
font.pixelSize: 30
color: "white"
style: Text.Outline
textFormat: Text.RichText
}
Text {
Layout.alignment: Qt.AlignCenter
visible: root.rest > 0 && root.rest < 999
text: "轮次"
font.family: fontLibian.name
font.pixelSize: 28
color: "white"
style: Text.Outline
textFormat: Text.RichText
}
}
Rectangle { Rectangle {
id: winRateRect id: winRateRect
width: 138; x: 31 width: 138; x: 31
@ -389,7 +430,7 @@ Item {
Image { Image {
// id: saveme // id: saveme
visible: root.dead || root.dying || root.surrendered visible: (root.dead && !root.rest) || root.dying || root.surrendered
source: { source: {
if (root.dead) { if (root.dead) {
return SkinBank.getRoleDeathPic(root.role); return SkinBank.getRoleDeathPic(root.role);

View File

@ -455,12 +455,12 @@ end
---@param player Player @ 和这张牌扯上关系的那名玩家 ---@param player Player @ 和这张牌扯上关系的那名玩家
---@param data any @ 随意目前只用到JudgeStruct为了影响判定牌 ---@param data any @ 随意目前只用到JudgeStruct为了影响判定牌
function Engine:filterCard(id, player, data) function Engine:filterCard(id, player, data)
local card = self:getCardById(id, true)
if player == nil then if player == nil then
self.filtered_cards[id] = nil self.filtered_cards[id] = nil
return return
end end
local skills = player:getAllSkills()
local card = self:getCardById(id, true)
local filters = self:currentRoom().status_skills[FilterSkill] or Util.DummyTable local filters = self:currentRoom().status_skills[FilterSkill] or Util.DummyTable
if #filters == 0 then if #filters == 0 then
@ -475,7 +475,7 @@ function Engine:filterCard(id, player, data)
end end
for _, f in ipairs(filters) do for _, f in ipairs(filters) do
if f:cardFilter(card, player) then if f:cardFilter(card, player, type(data) == "table" and data.isJudgeEvent) then
local _card = f:viewAs(card, player) local _card = f:viewAs(card, player)
_card.id = id _card.id = id
_card.skillName = f.name _card.skillName = f.name

View File

@ -27,10 +27,14 @@ end
---@param victim ServerPlayer @ 死者 ---@param victim ServerPlayer @ 死者
---@return string @ 胜者阵营 ---@return string @ 胜者阵营
function GameMode:getWinner(victim) function GameMode:getWinner(victim)
if victim.rest > 0 then
return ""
end
local room = victim.room local room = victim.room
local winner = "" local winner = ""
local alive = table.filter(room.alive_players, function(p) local alive = table.filter(room.players, function(p)
return not p.surrendered return not p.surrendered and not (p.dead and p.rest == 0)
end) end)
if victim.role == "lord" then if victim.role == "lord" then

View File

@ -86,6 +86,7 @@ function Player:initialize()
self.dying = false self.dying = false
self.dead = false self.dead = false
self.drank = 0 self.drank = 0
self.rest = 0
self.player_skills = {} self.player_skills = {}
self.derivative_skills = {} self.derivative_skills = {}
@ -570,9 +571,9 @@ end
---@param ignoreRemoved? boolean @ 忽略被移除 ---@param ignoreRemoved? boolean @ 忽略被移除
---@param num? integer @ 第几个默认1 ---@param num? integer @ 第几个默认1
---@return ServerPlayer ---@return ServerPlayer
function Player:getNextAlive(ignoreRemoved, num) function Player:getNextAlive(ignoreRemoved, num, ignoreRest)
if #Fk:currentRoom().alive_players == 0 then if #Fk:currentRoom().alive_players == 0 then
return self return self.rest > 0 and self.next.rest > 0 and self.next or self
end end
local doNotIgnore = not ignoreRemoved local doNotIgnore = not ignoreRemoved
if doNotIgnore and table.every(Fk:currentRoom().alive_players, function(p) return p:isRemoved() end) then if doNotIgnore and table.every(Fk:currentRoom().alive_players, function(p) return p:isRemoved() end) then
@ -583,7 +584,7 @@ function Player:getNextAlive(ignoreRemoved, num)
num = num or 1 num = num or 1
for _ = 1, num do for _ = 1, num do
ret = ret.next ret = ret.next
while ret.dead or (doNotIgnore and ret:isRemoved()) do while (ret.dead and not ignoreRest) or (doNotIgnore and ret:isRemoved()) do
ret = ret.next ret = ret.next
end end
end end

View File

@ -4,7 +4,7 @@
local FilterSkill = StatusSkill:subclass("FilterSkill") local FilterSkill = StatusSkill:subclass("FilterSkill")
---@param card Card ---@param card Card
function FilterSkill:cardFilter(card, player) function FilterSkill:cardFilter(card, player, isJudgeEvent)
return false return false
end end

View File

@ -135,4 +135,6 @@ fk.BeforePropertyChange = 92
fk.PropertyChange = 93 fk.PropertyChange = 93
fk.AfterPropertyChange = 94 fk.AfterPropertyChange = 94
fk.AfterPlayerRevived = 95
fk.NumOfEvents = 96 fk.NumOfEvents = 96

View File

@ -53,7 +53,11 @@ GameEvent.functions[GameEvent.Death] = function(self)
local room = self.room local room = self.room
local victim = room:getPlayerById(deathStruct.who) local victim = room:getPlayerById(deathStruct.who)
victim.dead = true victim.dead = true
victim._splayer:setDied(true)
if victim.rest <= 0 then
victim._splayer:setDied(true)
end
table.removeOne(room.alive_players, victim) table.removeOne(room.alive_players, victim)
local logic = room.logic local logic = room.logic
@ -65,22 +69,26 @@ GameEvent.functions[GameEvent.Death] = function(self)
type = "#KillPlayer", type = "#KillPlayer",
to = {killer.id}, to = {killer.id},
from = victim.id, from = victim.id,
arg = victim.role, arg = (victim.rest > 0 and 'unknown' or victim.role),
} }
else else
room:sendLog{ room:sendLog{
type = "#KillPlayerWithNoKiller", type = "#KillPlayerWithNoKiller",
from = victim.id, from = victim.id,
arg = victim.role, arg = (victim.rest > 0 and 'unknown' or victim.role),
} }
end end
room:sendLogEvent("Death", {to = victim.id}) room:sendLogEvent("Death", {to = victim.id})
room:broadcastProperty(victim, "role") if victim.rest == 0 then
room:broadcastProperty(victim, "role")
end
room:broadcastProperty(victim, "dead") room:broadcastProperty(victim, "dead")
victim.drank = 0 victim.drank = 0
room:broadcastProperty(victim, "drank") room:broadcastProperty(victim, "drank")
victim.shield = 0
room:broadcastProperty(victim, "shield")
logic:trigger(fk.GameOverJudge, victim, deathStruct) logic:trigger(fk.GameOverJudge, victim, deathStruct)
logic:trigger(fk.Death, victim, deathStruct) logic:trigger(fk.Death, victim, deathStruct)
@ -88,3 +96,23 @@ GameEvent.functions[GameEvent.Death] = function(self)
logic:trigger(fk.Deathed, victim, deathStruct) logic:trigger(fk.Deathed, victim, deathStruct)
end end
GameEvent.functions[GameEvent.Revive] = function(self)
local room = self.room
local player, sendLog, reason = table.unpack(self.data)
if not player.dead then return end
room:setPlayerProperty(player, "dead", false)
player._splayer:setDied(false)
room:setPlayerProperty(player, "dying", false)
room:setPlayerProperty(player, "hp", player.maxHp)
table.insertIfNeed(room.alive_players, player)
sendLog = (sendLog == nil) and true or sendLog
if sendLog then
room:sendLog { type = "#Revive", from = player.id }
end
reason = reason or ""
room.logic:trigger(fk.AfterPlayerRevived, player, { reason = reason })
end

View File

@ -161,8 +161,8 @@ GameEvent.functions[GameEvent.Round] = function(self)
p = room.current p = room.current
GameEvent(GameEvent.Turn, p):exec() GameEvent(GameEvent.Turn, p):exec()
if room.game_finished then break end if room.game_finished then break end
room.current = room.current:getNextAlive(true) room.current = room.current:getNextAlive(true, nil, true)
until p.seat >= p:getNextAlive(true).seat until p.seat >= p:getNextAlive(true, nil, true).seat
logic:trigger(fk.RoundEnd, p) logic:trigger(fk.RoundEnd, p)
end end
@ -194,6 +194,15 @@ GameEvent.prepare_funcs[GameEvent.Turn] = function(self)
local logic = room.logic local logic = room.logic
local player = room.current local player = room.current
if player.rest > 0 and player.rest < 999 then
room:setPlayerRest(player, player.rest - 1)
if player.rest == 0 and player.dead then
room:revivePlayer(player, true, "rest")
else
room:delay(50)
end
end
if player.dead then return true end if player.dead then return true end
room:sendLog{ type = "$AppendSeparator" } room:sendLog{ type = "$AppendSeparator" }
@ -312,9 +321,9 @@ GameEvent.functions[GameEvent.Phase] = function(self)
local result = room:doRequest(player, "PlayCard", player.id) local result = room:doRequest(player, "PlayCard", player.id)
if result == "" then break end if result == "" then break end
local use = room:handleUseCardReply(player, result) local useResult = room:handleUseCardReply(player, result)
if use then if type(useResult) == "table" then
room:useCard(use) room:useCard(useResult)
end end
if player._play_phase_end then if player._play_phase_end then

View File

@ -48,37 +48,50 @@ GameEvent.functions[GameEvent.ChangeHp] = function(self)
damageEvent = damageStruct, damageEvent = damageStruct,
} }
if reason == "damage" then
data.shield_lost = math.min(-num, player.shield)
data.num = num + data.shield_lost
end
if logic:trigger(fk.BeforeHpChanged, player, data) then if logic:trigger(fk.BeforeHpChanged, player, data) then
logic:breakEvent(false) logic:breakEvent(false)
end end
assert(not (data.reason == "recover" and data.num < 0)) if reason == "damage" and data.shield_lost > 0 and not damageStruct.isVirtualDMG then
player.hp = math.min(player.hp + data.num, player.maxHp) room:changeShield(player, -data.shield_lost)
room:broadcastProperty(player, "hp") end
if reason == "damage" then if reason == "damage" then
sendDamageLog(room, damageStruct) sendDamageLog(room, damageStruct)
elseif reason == "loseHp" then
room:sendLog{
type = "#LoseHP",
from = player.id,
arg = 0 - num,
}
room:sendLogEvent("LoseHP", {})
elseif reason == "recover" then
room:sendLog{
type = "#HealHP",
from = player.id,
arg = num,
}
end end
room:sendLog{ if not (reason == "damage" and (data.num == 0 or damageStruct.isVirtualDMG)) then
type = "#ShowHPAndMaxHP", assert(not (data.reason == "recover" and data.num < 0))
from = player.id, player.hp = math.min(player.hp + data.num, player.maxHp)
arg = player.hp, room:broadcastProperty(player, "hp")
arg2 = player.maxHp,
} if reason == "loseHp" then
room:sendLog{
type = "#LoseHP",
from = player.id,
arg = 0 - num,
}
room:sendLogEvent("LoseHP", {})
elseif reason == "recover" then
room:sendLog{
type = "#HealHP",
from = player.id,
arg = num,
}
end
room:sendLog{
type = "#ShowHPAndMaxHP",
from = player.id,
arg = player.hp,
arg2 = player.maxHp,
}
end
logic:trigger(fk.HpChanged, player, data) logic:trigger(fk.HpChanged, player, data)
@ -124,10 +137,12 @@ GameEvent.functions[GameEvent.Damage] = function(self)
local stages = { local stages = {
{fk.PreDamage, damageStruct.from}, {fk.PreDamage, damageStruct.from},
{fk.DamageCaused, damageStruct.from},
{fk.DamageInflicted, damageStruct.to},
} }
if not damageStruct.isVirtualDMG then
table.insertTable(stages, { { fk.DamageCaused, damageStruct.from }, { fk.DamageInflicted, damageStruct.to } })
end
for _, struct in ipairs(stages) do for _, struct in ipairs(stages) do
local event, player = table.unpack(struct) local event, player = table.unpack(struct)
if logic:trigger(event, player, damageStruct) or damageStruct.damage < 1 then if logic:trigger(event, player, damageStruct) or damageStruct.damage < 1 then
@ -141,6 +156,9 @@ GameEvent.functions[GameEvent.Damage] = function(self)
return false return false
end end
damageStruct.dealtRecorderId = room.logic.specific_events_id[GameEvent.Damage]
room.logic.specific_events_id[GameEvent.Damage] = room.logic.specific_events_id[GameEvent.Damage] + 1
if damageStruct.card and damageStruct.damage > 0 then if damageStruct.card and damageStruct.damage > 0 then
local parentUseData = logic:getCurrentEvent():findParent(GameEvent.UseCard) local parentUseData = logic:getCurrentEvent():findParent(GameEvent.UseCard)
if parentUseData then if parentUseData then
@ -155,23 +173,16 @@ GameEvent.functions[GameEvent.Damage] = function(self)
damageStruct.to:setChainState(false) damageStruct.to:setChainState(false)
end end
-- 先扣减护甲,再扣体力值 if not room:changeHp(
local shield_to_lose = math.min(damageStruct.damage, damageStruct.to.shield) damageStruct.to,
room:changeShield(damageStruct.to, -shield_to_lose) -damageStruct.damage,
"damage",
if shield_to_lose < damageStruct.damage then damageStruct.skillName,
if not room:changeHp( damageStruct) then
damageStruct.to, logic:breakEvent(false)
shield_to_lose - damageStruct.damage,
"damage",
damageStruct.skillName,
damageStruct) then
logic:breakEvent(false)
end
else
sendDamageLog(room, damageStruct)
end end
stages = { stages = {
{fk.Damage, damageStruct.from}, {fk.Damage, damageStruct.from},
{fk.Damaged, damageStruct.to}, {fk.Damaged, damageStruct.to},
@ -201,9 +212,15 @@ GameEvent.exit_funcs[GameEvent.Damage] = function(self)
type = "#ChainDamage", type = "#ChainDamage",
from = p.id from = p.id
} }
local dmg = table.simpleClone(damageStruct) local dmg = {
dmg.to = p from = damageStruct.from,
dmg.chain = true to = p,
damage = damageStruct.damage,
card = damageStruct.card,
skillName = damageStruct.skillName,
chain = true,
}
room:damage(dmg) room:damage(dmg)
end end
end end

View File

@ -14,6 +14,7 @@ dofile "lua/server/events/hp.lua"
GameEvent.Dying = 6 GameEvent.Dying = 6
GameEvent.Death = 7 GameEvent.Death = 7
GameEvent.Revive = 22
dofile "lua/server/events/death.lua" dofile "lua/server/events/death.lua"
GameEvent.MoveCards = 8 GameEvent.MoveCards = 8
@ -64,6 +65,7 @@ local eventTranslations = {
[GameEvent.ChangeMaxHp] = "GameEvent.ChangeMaxHp", [GameEvent.ChangeMaxHp] = "GameEvent.ChangeMaxHp",
[GameEvent.Dying] = "GameEvent.Dying", [GameEvent.Dying] = "GameEvent.Dying",
[GameEvent.Death] = "GameEvent.Death", [GameEvent.Death] = "GameEvent.Death",
[GameEvent.Revive] = "GameEvent.Revive",
[GameEvent.MoveCards] = "GameEvent.MoveCards", [GameEvent.MoveCards] = "GameEvent.MoveCards",
[GameEvent.UseCard] = "GameEvent.UseCard", [GameEvent.UseCard] = "GameEvent.UseCard",
[GameEvent.RespondCard] = "GameEvent.RespondCard", [GameEvent.RespondCard] = "GameEvent.RespondCard",

View File

@ -5,6 +5,8 @@ GameEvent.functions[GameEvent.Judge] = function(self)
local room = self.room local room = self.room
local logic = room.logic local logic = room.logic
local who = data.who local who = data.who
data.isJudgeEvent = true
logic:trigger(fk.StartJudge, who, data) logic:trigger(fk.StartJudge, who, data)
data.card = data.card or Fk:getCardById(room:getNCards(1)[1]) data.card = data.card or Fk:getCardById(room:getNCards(1)[1])

View File

@ -39,28 +39,33 @@ GameEvent.functions[GameEvent.ChangeProperty] = function(self)
room:setPlayerProperty(player, "general", data.general) room:setPlayerProperty(player, "general", data.general)
end end
if data.deputyGeneral and data.deputyGeneral ~= "" and data.deputyGeneral ~= player.deputyGeneral then if data.deputyGeneral and data.deputyGeneral ~= player.deputyGeneral then
local originalDeputy = Fk.generals[player.deputyGeneral] or Fk.generals["blank_shibing"] local originalDeputy = Fk.generals[player.deputyGeneral] or Fk.generals["blank_shibing"]
local originalSkills = originalDeputy and originalDeputy:getSkillNameList() or Util.DummyTable local originalSkills = originalDeputy and originalDeputy:getSkillNameList() or Util.DummyTable
table.insertTableIfNeed(skills, table.map(originalSkills, function(e) table.insertTableIfNeed(skills, table.map(originalSkills, function(e)
return "-" .. e return "-" .. e
end)) end))
local newDeputy = Fk.generals[data.deputyGeneral] or Fk.generals["blank_shibing"]
for _, name in ipairs(newDeputy:getSkillNameList()) do if data.deputyGeneral ~= "" then
local s = Fk.skills[name] local newDeputy = Fk.generals[data.deputyGeneral] or Fk.generals["blank_shibing"]
if not s.relate_to_place or s.relate_to_place == "d" then for _, name in ipairs(newDeputy:getSkillNameList()) do
table.insertIfNeed(skills, name) local s = Fk.skills[name]
if not s.relate_to_place or s.relate_to_place == "d" then
table.insertIfNeed(skills, name)
end
end
if data.sendLog then
room:sendLog{
type = "#ChangeHero",
from = player.id,
arg = player.deputyGeneral,
arg2 = data.deputyGeneral,
arg3 = "deputyGeneral",
}
end end
end end
if data.sendLog then
room:sendLog{
type = "#ChangeHero",
from = player.id,
arg = player.deputyGeneral,
arg2 = data.deputyGeneral,
arg3 = "deputyGeneral",
}
end
data.results["deputyChange"] = {player.deputyGeneral, data.deputyGeneral} data.results["deputyChange"] = {player.deputyGeneral, data.deputyGeneral}
room:setPlayerProperty(player, "deputyGeneral", data.deputyGeneral) room:setPlayerProperty(player, "deputyGeneral", data.deputyGeneral)
end end

View File

@ -152,10 +152,18 @@ GameEvent.functions[GameEvent.MoveCards] = function(self)
room:doBroadcastNotify("UpdateDrawPile", #room.draw_pile) room:doBroadcastNotify("UpdateDrawPile", #room.draw_pile)
end end
if not (data.to and data.toArea ~= Card.PlayerHand) then local beforeCard = Fk:getCardById(info.cardId)
Fk:filterCard(info.cardId, room:getPlayerById(data.to)) if
realFromArea == Player.Equip and
beforeCard.type == Card.TypeEquip and
data.from ~= nil and
beforeCard.equip_skill
then
beforeCard:onUninstall(room, room:getPlayerById(data.from))
end end
Fk:filterCard(info.cardId, room:getPlayerById(data.to))
local currentCard = Fk:getCardById(info.cardId) local currentCard = Fk:getCardById(info.cardId)
for name, _ in pairs(currentCard.mark) do for name, _ in pairs(currentCard.mark) do
if name:endsWith("-inhand") and if name:endsWith("-inhand") and
@ -174,15 +182,6 @@ GameEvent.functions[GameEvent.MoveCards] = function(self)
then then
currentCard:onInstall(room, room:getPlayerById(data.to)) currentCard:onInstall(room, room:getPlayerById(data.to))
end end
if
realFromArea == Player.Equip and
currentCard.type == Card.TypeEquip and
data.from ~= nil and
currentCard.equip_skill
then
currentCard:onUninstall(room, room:getPlayerById(data.from))
end
end end
end end
end end

View File

@ -155,13 +155,29 @@ local sendCardEmotionAndLog = function(room, cardUseEvent)
} }
end end
end end
end
GameEvent.functions[GameEvent.UseCard] = function(self)
local cardUseEvent = table.unpack(self.data)
local room = self.room
local logic = room.logic
if cardUseEvent.card.skill then
cardUseEvent.card.skill:onUse(room, cardUseEvent)
end
sendCardEmotionAndLog(room, cardUseEvent)
room:moveCardTo(cardUseEvent.card, Card.Processing, nil, fk.ReasonUse)
local card = cardUseEvent.card
local useCardIds = card:isVirtual() and card.subcards or { card.id }
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 = cardUseEvent.from,
to = tos, to = tos,
}) })
if card:isVirtual() then if card:isVirtual() then
@ -170,26 +186,12 @@ local sendCardEmotionAndLog = function(room, cardUseEvent)
else else
room:sendFootnote(useCardIds, { room:sendFootnote(useCardIds, {
type = "##UseCard", type = "##UseCard",
from = from, from = cardUseEvent.from,
}) })
if card:isVirtual() then if card:isVirtual() then
room:sendCardVirtName(useCardIds, card.name) room:sendCardVirtName(useCardIds, card.name)
end end
end end
end
GameEvent.functions[GameEvent.UseCard] = function(self)
local cardUseEvent = table.unpack(self.data)
local room = self.room
local logic = room.logic
room:moveCardTo(cardUseEvent.card, Card.Processing, nil, fk.ReasonUse)
if cardUseEvent.card.skill then
cardUseEvent.card.skill:onUse(room, cardUseEvent)
end
sendCardEmotionAndLog(room, cardUseEvent)
if logic:trigger(fk.PreCardUse, room:getPlayerById(cardUseEvent.from), cardUseEvent) then if logic:trigger(fk.PreCardUse, room:getPlayerById(cardUseEvent.from), cardUseEvent) then
logic:breakEvent() logic:breakEvent()

View File

@ -23,6 +23,9 @@ function GameLogic:initialize(room)
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.specific_events_id = {
[GameEvent.Damage] = 0,
}
self.role_table = { self.role_table = {
{ "lord" }, { "lord" },

View File

@ -1913,10 +1913,13 @@ function Room:handleUseCardReply(player, data)
end end
use.card = c use.card = c
skill:beforeUse(player, use)
self:useSkill(player, skill, Util.DummyFunc) self:useSkill(player, skill, Util.DummyFunc)
local rejectSkillName = skill:beforeUse(player, use)
if type(rejectSkillName) == "string" then
return rejectSkillName
end
return use return use
end end
end end
@ -2007,17 +2010,29 @@ function Room:askForUseCard(player, card_name, pattern, prompt, cancelable, extr
player.room:setPlayerMark(player, MarkEnum.BypassTimesLimit .. "-tmp", 0) player.room:setPlayerMark(player, MarkEnum.BypassTimesLimit .. "-tmp", 0)
return askForUseCardData.result return askForUseCardData.result
else else
local data = {card_name, pattern, prompt, cancelable, extra_data} local useResult
local disabledSkillNames = {}
Fk.currentResponsePattern = pattern repeat
local result = self:doRequest(player, command, json.encode(data)) useResult = nil
Fk.currentResponsePattern = nil local data = {card_name, pattern, prompt, cancelable, extra_data, disabledSkillNames}
if result ~= "" then Fk.currentResponsePattern = pattern
player.room:setPlayerMark(player, MarkEnum.BypassDistancesLimit .. "-tmp", 0) local result = self:doRequest(player, command, json.encode(data))
player.room:setPlayerMark(player, MarkEnum.BypassTimesLimit .. "-tmp", 0) Fk.currentResponsePattern = nil
return self:handleUseCardReply(player, result)
end if result ~= "" then
player.room:setPlayerMark(player, MarkEnum.BypassDistancesLimit .. "-tmp", 0)
player.room:setPlayerMark(player, MarkEnum.BypassTimesLimit .. "-tmp", 0)
useResult = self:handleUseCardReply(player, result)
if type(useResult) == "string" and useResult ~= "" then
table.insertIfNeed(disabledSkillNames, useResult)
end
end
until type(useResult) ~= "string"
return useResult
end end
player.room:setPlayerMark(player, MarkEnum.BypassDistancesLimit .. "-tmp", 0) player.room:setPlayerMark(player, MarkEnum.BypassDistancesLimit .. "-tmp", 0)
player.room:setPlayerMark(player, MarkEnum.BypassTimesLimit .. "-tmp", 0) player.room:setPlayerMark(player, MarkEnum.BypassTimesLimit .. "-tmp", 0)
@ -2056,17 +2071,28 @@ function Room:askForResponse(player, card_name, pattern, prompt, cancelable, ext
if eventData.result then if eventData.result then
return eventData.result return eventData.result
else else
local data = {card_name, pattern, prompt, cancelable, extra_data} local useResult
local disabledSkillNames = {}
Fk.currentResponsePattern = pattern repeat
local result = self:doRequest(player, command, json.encode(data)) useResult = nil
Fk.currentResponsePattern = nil local data = {card_name, pattern, prompt, cancelable, extra_data, disabledSkillNames}
if result ~= "" then Fk.currentResponsePattern = pattern
local use = self:handleUseCardReply(player, result) local result = self:doRequest(player, command, json.encode(data))
if use then Fk.currentResponsePattern = nil
return use.card
if result ~= "" then
useResult = self:handleUseCardReply(player, result)
if type(useResult) == "string" and useResult ~= "" then
table.insertIfNeed(disabledSkillNames, useResult)
end
end end
until type(useResult) ~= "string"
if useResult then
return useResult.card
end end
end end
return nil return nil
@ -2094,20 +2120,31 @@ function Room:askForNullification(players, card_name, pattern, prompt, cancelabl
prompt = prompt or "" prompt = prompt or ""
pattern = pattern or card_name pattern = pattern or card_name
self:notifyMoveFocus(self.alive_players, card_name) local useResult
self:doBroadcastNotify("WaitForNullification", "") local disabledSkillNames = {}
local data = {card_name, pattern, prompt, cancelable, extra_data} repeat
useResult = nil
self:notifyMoveFocus(self.alive_players, card_name)
self:doBroadcastNotify("WaitForNullification", "")
Fk.currentResponsePattern = pattern local data = {card_name, pattern, prompt, cancelable, extra_data, disabledSkillNames}
local winner = self:doRaceRequest(command, players, json.encode(data))
if winner then Fk.currentResponsePattern = pattern
local result = winner.client_reply local winner = self:doRaceRequest(command, players, json.encode(data))
return self:handleUseCardReply(winner, result)
end if winner then
Fk.currentResponsePattern = nil local result = winner.client_reply
return nil useResult = self:handleUseCardReply(winner, result)
if type(useResult) == "string" and useResult ~= "" then
table.insertIfNeed(disabledSkillNames, useResult)
end
end
Fk.currentResponsePattern = nil
until type(useResult) ~= "string"
return useResult
end end
-- AG(a.k.a. Amazing Grace) functions -- AG(a.k.a. Amazing Grace) functions
@ -2409,9 +2446,8 @@ local onAim = function(room, cardUseEvent, aimEventCollaborators)
firstTarget = false firstTarget = false
if room.logic:trigger(stage, (stage == fk.TargetSpecifying or stage == fk.TargetSpecified) and room:getPlayerById(aimStruct.from) or room:getPlayerById(aimStruct.to), aimStruct) then room.logic:trigger(stage, (stage == fk.TargetSpecifying or stage == fk.TargetSpecified) and room:getPlayerById(aimStruct.from) or room:getPlayerById(aimStruct.to), aimStruct)
return false
end
AimGroup:removeDeadTargets(room, aimStruct) AimGroup:removeDeadTargets(room, aimStruct)
local aimEventTargetGroup = aimStruct.targetGroup local aimEventTargetGroup = aimStruct.targetGroup
@ -3292,19 +3328,9 @@ function Room:useSkill(player, skill, effect_cb)
end end
---@param player ServerPlayer ---@param player ServerPlayer
---@param sendLog? boolean ---@param sendLog? bool
function Room:revivePlayer(player, sendLog) function Room:revivePlayer(player, sendLog, reason)
if not player.dead then return end return execGameEvent(GameEvent.Revive, player, sendLog, reason)
self:setPlayerProperty(player, "dead", false)
player._splayer:setDied(false)
self:setPlayerProperty(player, "dying", false)
self:setPlayerProperty(player, "hp", player.maxHp)
table.insertIfNeed(self.alive_players, player)
sendLog = (sendLog == nil) and true or sendLog
if sendLog then
self:sendLog { type = "#Revive", from = player.id }
end
end end
---@param room Room ---@param room Room
@ -3573,4 +3599,9 @@ function Room:resumePlayerArea(player, playerSlots)
end end
end end
function Room:setPlayerRest(player, roundNum)
player.rest = roundNum
self:broadcastProperty(player, "rest")
end
return Room return Room

View File

@ -39,6 +39,7 @@
--- 描述和一次体力变化有关的数据 --- 描述和一次体力变化有关的数据
---@class HpChangedData ---@class HpChangedData
---@field public num integer @ 体力变化量,可能是正数或者负数 ---@field public num integer @ 体力变化量,可能是正数或者负数
---@field public shield_lost integer|nil
---@field public reason string @ 体力变化原因 ---@field public reason string @ 体力变化原因
---@field public skillName string @ 引起体力变化的技能名 ---@field public skillName string @ 引起体力变化的技能名
---@field public damageEvent? DamageStruct @ 引起这次体力变化的伤害数据 ---@field public damageEvent? DamageStruct @ 引起这次体力变化的伤害数据

View File

@ -82,7 +82,7 @@ GameRule = fk.CreateTriggerSkill{
end, end,
[fk.BuryVictim] = function() [fk.BuryVictim] = function()
player:bury() player:bury()
if room.tag["SkipNormalDeathProcess"] then if room.tag["SkipNormalDeathProcess"] or player.rest > 0 then
return false return false
end end
local damage = data.damage local damage = data.damage

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.6 KiB

View File

@ -423,6 +423,31 @@ local jijiang = fk.CreateViewAsSkill{
c.skillName = self.name c.skillName = self.name
return c return c
end, end,
before_use = function(self, player, use)
local room = player.room
if use.tos then
room:doIndicate(player.id, TargetGroup:getRealTargets(use.tos))
end
for _, p in ipairs(room:getOtherPlayers(player)) do
if p.kingdom == "shu" then
local cardResponded = room:askForResponse(p, "slash", "slash", "#jijiang-ask:" .. player.id, true)
if cardResponded then
room:responseCard({
from = p.id,
card = cardResponded,
skipDrop = true,
})
use.card = cardResponded
return
end
end
end
room:setPlayerMark(player, "jijiang-failed-phase", 1)
return self.name
end,
enabled_at_play = function(self, player) enabled_at_play = function(self, player)
return player:getMark("jijiang-failed-phase") == 0 and not table.every(Fk:currentRoom().alive_players, function(p) return player:getMark("jijiang-failed-phase") == 0 and not table.every(Fk:currentRoom().alive_players, function(p)
return p == player or p.kingdom ~= "shu" return p == player or p.kingdom ~= "shu"
@ -434,51 +459,6 @@ local jijiang = fk.CreateViewAsSkill{
end) end)
end, end,
} }
local jijiangResponse = fk.CreateTriggerSkill{
name = "#jijiangResponse",
events = {fk.PreCardUse, fk.PreCardRespond},
mute = true,
priority = 10,
can_trigger = function(self, event, target, player, data)
return target == player and player:hasSkill(self.name, true) and table.contains(data.card.skillNames, "jijiang")
end,
on_cost = function(self, event, target, player, data)
local room = player.room
room:doIndicate(player.id, TargetGroup:getRealTargets(data.tos))
return true
end,
on_use = function(self, event, target, player, data)
local room = player.room
for _, p in ipairs(room:getOtherPlayers(player)) do
if p.kingdom == "shu" then
local cardResponded = room:askForResponse(p, "slash", "slash", "#jijiang-ask:" .. player.id, true)
if cardResponded then
room:responseCard({
from = p.id,
card = cardResponded,
skipDrop = true,
})
data.card = cardResponded
return false
end
end
end
if event == fk.PreCardUse and player.phase == Player.Play then
room:setPlayerMark(player, "jijiang-failed-phase", 1)
end
return true
end,
refresh_events = {fk.CardUsing},
can_refresh = function(self, event, target, player, data)
return target == player and player:hasSkill(self.name, true) and player:getMark("jijiang-failed-phase") > 0
end,
on_refresh = function(self, event, target, player, data)
player.room:setPlayerMark(player, "jijiang-failed-phase", 0)
end,
}
jijiang:addRelatedSkill(jijiangResponse)
local liubei = General:new(extension, "liubei", "shu", 4) local liubei = General:new(extension, "liubei", "shu", 4)
liubei:addSkill(rende) liubei:addSkill(rende)

View File

@ -338,6 +338,7 @@ local test_feichu = fk.CreateActiveSkill{
room:abortPlayerArea(from, eqipSlots) room:abortPlayerArea(from, eqipSlots)
end, end,
} }
local test2 = General(extension, "mouxusheng", "wu", 4, 4, General.Female) local test2 = General(extension, "mouxusheng", "wu", 4, 4, General.Female)
test2.shield = 3 test2.shield = 3
test2.hidden = true test2.hidden = true