diff --git a/Fk/Pages/CardsOverview.qml b/Fk/Pages/CardsOverview.qml
index fb435f74..a2716b58 100644
--- a/Fk/Pages/CardsOverview.qml
+++ b/Fk/Pages/CardsOverview.qml
@@ -226,8 +226,9 @@ Item {
}
detailCard.known = true;
cardText.clear();
+ audioRow.clear();
cardText.append(Backend.translate(":" + data.name));
-
+ addCardAudio(data)
const skills = JSON.parse(Backend.callLuaFunction
("GetCardSpecialSkills", [cid]));
if (skills.length > 0) {
@@ -287,6 +288,35 @@ Item {
textFormat: TextEdit.RichText
font.pixelSize: 16
}
+
+ GridLayout {
+ columns: 2
+ Repeater {
+ model: ListModel {
+ id: audioRow
+ }
+ Button {
+ Layout.fillWidth: true
+ contentItem: Text {
+ Layout.fillWidth: true
+ horizontalAlignment: Text.AlignHCenter
+ verticalAlignment: Text.AlignVCenter
+ text: {
+ if (gender === "male") {
+ return Backend.translate("Male Audio");
+ } else {
+ return Backend.translate("Female Audio");
+ }
+ }
+ font.pixelSize: 14
+ }
+ onClicked: {
+ const data = JSON.parse(Backend.callLuaFunction("GetCardData", [cardDetail.cid]));
+ Backend.playSound("./packages/" + extension + "/audio/card/" + gender + "/" + data.name);
+ }
+ }
+ }
+ }
}
}
}
@@ -299,6 +329,29 @@ Item {
}
}
+ function addCardAudio(card) {
+ const extension = card.extension;
+ const orig_extension = Backend.callLuaFunction("GetCardExtensionByName", [card.name]);
+ let fname = AppPath + "/packages/" + extension + "/audio/card/male/" + card.name + ".mp3";
+ if (Backend.exists(fname)) {
+ audioRow.append( {gender: "male", extension: extension} );
+ } else {
+ fname = AppPath + "/packages/" + orig_extension + "/audio/card/male/" + card.name + ".mp3";
+ if (Backend.exists(fname)) {
+ audioRow.append( {gender: "male", extension: orig_extension} );
+ }
+ }
+ fname = AppPath + "/packages/" + extension + "/audio/card/female/" + card.name + ".mp3";
+ if (Backend.exists(fname)) {
+ audioRow.append( {gender: "female", extension: extension} );
+ }else {
+ fname = AppPath + "/packages/" + orig_extension + "/audio/card/female/" + card.name + ".mp3";
+ if (Backend.exists(fname)) {
+ audioRow.append( {gender: "female", extension: orig_extension} );
+ }
+ }
+ }
+
function loadPackages() {
if (loaded) return;
const packs = JSON.parse(Backend.callLuaFunction("GetAllCardPack", []));
diff --git a/Fk/Pages/Room.qml b/Fk/Pages/Room.qml
index 292e252d..9f4f1157 100644
--- a/Fk/Pages/Room.qml
+++ b/Fk/Pages/Room.qml
@@ -81,104 +81,140 @@ Item {
}
}
- // tmp
- Button {
+ ToolButton {
id: menuButton
anchors.top: parent.top
+ anchors.topMargin: 12
anchors.right: parent.right
- anchors.rightMargin: 10
- text: Backend.translate("Menu")
- z: 2
+ anchors.rightMargin: 36
+ icon.source: AppPath + "/image/modmaker/menu"
+ icon.width: 36
+ icon.height: 36
onClicked: {
- menuContainer.visible || menuContainer.open();
+ if (menuContainer.visible){
+ menuContainer.close();
+ } else {
+ menuContainer.open();
+ }
}
- }
- Menu {
- id: menuContainer
- x: parent.width - menuButton.width - menuContainer.width - 17
- width: menuRow.width
- height: menuRow.height
- verticalPadding: 0
- spacing: 7
- z: 2
+ background.implicitWidth: 60
+ background.implicitHeight: 60
- Row {
- id: menuRow
- spacing: 7
-
- Button {
- id: surrenderButton
- enabled: !config.observing && !config.replaying
- text: Backend.translate("Surrender")
- onClicked: {
- if (isStarted && !getPhoto(Self.id).dead) {
- const surrenderCheck = JSON.parse(Backend.callLuaFunction('CheckSurrenderAvailable', [miscStatus.playedTime]));
- if (!surrenderCheck.length) {
- surrenderDialog.informativeText = Backend.translate('Surrender is disabled in this mode');
- } else {
- surrenderDialog.informativeText = surrenderCheck.map(str => `${Backend.translate(str.text)}(${str.passed ? '√' : '×'})`).join('
');
- }
- surrenderDialog.open();
- }
- }
+ Menu {
+ id: menuContainer
+ y: menuButton.height - 12
+ width: 115
+ height: menuRow.height
+ verticalPadding: 0
+ spacing: 15
+ z: 2
+ background: Rectangle {
+ implicitWidth: 200
+ implicitHeight: 40
+ color: "transparent"
+ border.color: "transparent"
}
- MessageDialog {
- id: surrenderDialog
- title: Backend.translate("Surrender")
- informativeText: ''
- buttons: MessageDialog.Ok | MessageDialog.Cancel
- onButtonClicked: function (button, role) {
- switch (button) {
- case MessageDialog.Ok: {
- const surrenderCheck = JSON.parse(Backend.callLuaFunction('CheckSurrenderAvailable', [miscStatus.playedTime]));
- if (surrenderCheck.length && !surrenderCheck.find(check => !check.passed)) {
- ClientInstance.notifyServer("PushRequest", [
- "surrender", true
- ]);
- }
- surrenderDialog.close();
- break;
- }
- case MessageDialog.Cancel: {
- surrenderDialog.close();
- }
- }
- }
- }
+ Column {
+ id: menuRow
+ width: menuContainer.width
+ Layout.fillWidth: true
+ spacing: 7
- Button {
- id: quitButton
- text: Backend.translate("Quit")
- onClicked: {
- if (config.replaying) {
- Backend.controlReplayer("shutdown");
- mainStack.pop();
- } else if (config.observing) {
- ClientInstance.notifyServer("QuitRoom", "[]");
- } else {
- quitDialog.open();
- }
- }
- }
-
- MessageDialog {
- id: quitDialog
- title: Backend.translate("Quit")
- informativeText: Backend.translate("Are you sure to quit?")
- buttons: MessageDialog.Ok | MessageDialog.Cancel
- onButtonClicked: function (button) {
- switch (button) {
- case MessageDialog.Ok: {
+ Button {
+ id: quitButton
+ text: Backend.translate("Quit")
+ font.pixelSize: 28
+ Layout.fillWidth: true
+ width: menuContainer.width
+ height: menuContainer.width * 0.65
+ onClicked: {
+ if (config.replaying) {
+ Backend.controlReplayer("shutdown");
+ mainStack.pop();
+ } else if (config.observing) {
ClientInstance.notifyServer("QuitRoom", "[]");
- break;
+ } else {
+ quitDialog.open();
}
- case MessageDialog.Cancel: {
- quitDialog.close();
+ }
+ }
+
+ MessageDialog {
+ id: quitDialog
+ title: Backend.translate("Quit")
+ informativeText: Backend.translate("Are you sure to quit?")
+ buttons: MessageDialog.Ok | MessageDialog.Cancel
+ onButtonClicked: function (button) {
+ switch (button) {
+ case MessageDialog.Ok: {
+ ClientInstance.notifyServer("QuitRoom", "[]");
+ break;
+ }
+ case MessageDialog.Cancel: {
+ quitDialog.close();
+ }
}
}
}
+
+ Button {
+ id: surrenderButton
+ enabled: !config.observing && !config.replaying
+ text: Backend.translate("Surrender")
+ font.pixelSize: 28
+ Layout.fillWidth: true
+ width: menuContainer.width
+ height: menuContainer.width * 0.65
+ onClicked: {
+ if (isStarted && !getPhoto(Self.id).dead) {
+ const surrenderCheck = JSON.parse(Backend.callLuaFunction('CheckSurrenderAvailable', [miscStatus.playedTime]));
+ if (!surrenderCheck.length) {
+ surrenderDialog.informativeText = Backend.translate('Surrender is disabled in this mode');
+ } else {
+ surrenderDialog.informativeText = surrenderCheck.map(str => `${Backend.translate(str.text)}(${str.passed ? '√' : '×'})`).join('
');
+ }
+ surrenderDialog.open();
+ }
+ }
+ }
+
+ MessageDialog {
+ id: surrenderDialog
+ title: Backend.translate("Surrender")
+ informativeText: ''
+ buttons: MessageDialog.Ok | MessageDialog.Cancel
+ onButtonClicked: function (button, role) {
+ switch (button) {
+ case MessageDialog.Ok: {
+ const surrenderCheck = JSON.parse(Backend.callLuaFunction('CheckSurrenderAvailable', [miscStatus.playedTime]));
+ if (surrenderCheck.length && !surrenderCheck.find(check => !check.passed)) {
+ ClientInstance.notifyServer("PushRequest", [
+ "surrender", true
+ ]);
+ }
+ surrenderDialog.close();
+ break;
+ }
+ case MessageDialog.Cancel: {
+ surrenderDialog.close();
+ }
+ }
+ }
+ }
+
+ Button {
+ id: volumeButton
+ text: Backend.translate("Audio Settings")
+ font.pixelSize: 28
+ Layout.fillWidth: true
+ width: menuContainer.width
+ height: menuContainer.width * 0.65
+ onClicked: {
+ volumeDialog.open();
+ }
+ }
}
}
}
@@ -518,6 +554,7 @@ Item {
onCardSelected: function(card) {
Logic.enableTargets(card);
+ roomScene.resetPrompt();
if (typeof card === "number" && card !== -1 && roomScene.state === "playing"
&& JSON.parse(Backend.callLuaFunction("GetPlayerHandcards", [Self.id])).includes(card)) {
@@ -527,6 +564,14 @@ Item {
skills.unshift("_normal_use");
}
specialCardSkills.model = skills;
+ const skillName = Backend.callLuaFunction("GetCardSkill", [card]);
+ const prompt = JSON.parse(Backend.callLuaFunction(
+ "ActiveSkillPrompt",
+ [skillName, card, selected_targets]
+ ));
+ if (prompt !== "") {
+ roomScene.setPrompt(processPrompt(prompt));
+ }
} else {
specialCardSkills.model = [];
}
@@ -692,13 +737,28 @@ Item {
text: Backend.translate(modelData)
checked: index === 0
onCheckedChanged: {
+ roomScene.resetPrompt();
+ const card = dashboard.selected_card;
+ let prompt = ""
if (modelData === "_normal_use") {
- Logic.enableTargets(dashboard.selected_card);
+ Logic.enableTargets(card);
+ const skillName = Backend.callLuaFunction("GetCardSkill", [card]);
+ prompt = JSON.parse(Backend.callLuaFunction(
+ "ActiveSkillPrompt",
+ [skillName, card, selected_targets]
+ ));
} else {
Logic.enableTargets(JSON.stringify({
skill: modelData,
- subcards: [dashboard.selected_card],
+ subcards: [card],
}));
+ prompt = JSON.parse(Backend.callLuaFunction(
+ "ActiveSkillPrompt",
+ [modelData, card, selected_targets]
+ ));
+ }
+ if (prompt !== "") {
+ roomScene.setPrompt(processPrompt(prompt));
}
}
}
@@ -928,6 +988,60 @@ Item {
anchors.fill: parent
}
+ Dialog {
+ id: volumeDialog
+ title: Backend.translate("Audio Settings")
+ anchors.centerIn: roomScene
+ ColumnLayout {
+ RowLayout {
+ anchors.rightMargin: 8
+ spacing: 16
+ Text {
+ text: Backend.translate("BGM Volume")
+ }
+ Slider {
+ Layout.rightMargin: 16
+ Layout.fillWidth: true
+ from: 0
+ to: 100
+ value: config.bgmVolume
+ onValueChanged: config.bgmVolume = value;
+ }
+ }
+
+ RowLayout {
+ anchors.rightMargin: 8
+ spacing: 16
+ Text {
+ text: Backend.translate("Effect Volume")
+ }
+ Slider {
+ Layout.rightMargin: 16
+ Layout.fillWidth: true
+ from: 0
+ to: 100
+ value: Backend.volume
+ onValueChanged: Backend.volume = value;
+ }
+ }
+
+ Switch {
+ text: Backend.translate("Disable message audio")
+ checked: config.disableMsgAudio
+ onCheckedChanged: config.disableMsgAudio = checked;
+ }
+
+ Switch {
+ text: Backend.translate("Hide unselectable cards")
+ checked: config.hideUseless
+ onCheckedChanged: {
+ config.hideUseless = checked;
+ }
+ }
+
+ }
+ }
+
Rectangle {
anchors.fill: dashboard
visible: config.observing && !config.replaying
@@ -1039,6 +1153,18 @@ Item {
onActivated: Logic.doCancelButton();
}
+ function processPrompt(prompt) {
+ const data = prompt.split(":");
+ let raw = Backend.translate(data[0]);
+ const src = parseInt(data[1]);
+ const dest = parseInt(data[2]);
+ if (raw.match("%src")) raw = raw.replace(/%src/g, Backend.translate(getPhoto(src).general));
+ if (raw.match("%dest")) raw = raw.replace(/%dest/g, Backend.translate(getPhoto(dest).general));
+ if (raw.match("%arg2")) raw = raw.replace(/%arg2/g, Backend.translate(data[4]));
+ if (raw.match("%arg")) raw = raw.replace(/%arg/g, Backend.translate(data[3]));
+ return raw;
+ }
+
function getCurrentCardUseMethod() {
if (specialCardSkills.count === 1 && specialCardSkills.model[0] !== "_normal_use") {
return specialCardSkills.model[0];
diff --git a/Fk/Pages/RoomLogic.js b/Fk/Pages/RoomLogic.js
index 6e27cc18..f1c3dd45 100644
--- a/Fk/Pages/RoomLogic.js
+++ b/Fk/Pages/RoomLogic.js
@@ -576,9 +576,9 @@ function enableTargets(card) { // card: int | { skill: string, subcards: int[] }
if (roomScene.extra_data instanceof Object) {
const must = roomScene.extra_data.must_targets;
const included = roomScene.extra_data.include_targets;
- const exclusived = roomScene.extra_data.exclusive_targets;
- if (exclusived instanceof Array) {
- if (exclusived.indexOf(id) === -1) photo.selectable = false;
+ const exclusive = roomScene.extra_data.exclusive_targets;
+ if (exclusive instanceof Array) {
+ if (exclusive.indexOf(id) === -1) photo.selectable = false;
}
if (must instanceof Array) {
if (must.filter((val) => {
@@ -654,9 +654,9 @@ function updateSelectedTargets(playerid, selected) {
if (roomScene.extra_data instanceof Object) {
const must = roomScene.extra_data.must_targets;
const included = roomScene.extra_data.include_targets;
- const exclusived = roomScene.extra_data.exclusive_targets;
- if (exclusived instanceof Array) {
- if (exclusived.indexOf(id) === -1) photo.selectable = false;
+ const exclusive = roomScene.extra_data.exclusive_targets;
+ if (exclusive instanceof Array) {
+ if (exclusive.indexOf(id) === -1) photo.selectable = false;
}
if (must instanceof Array) {
if (must.filter((val) => {
diff --git a/lua/client/client_util.lua b/lua/client/client_util.lua
index 83aaa849..4e6e131a 100644
--- a/lua/client/client_util.lua
+++ b/lua/client/client_util.lua
@@ -123,10 +123,7 @@ function GetCardData(id, virtualCardForm)
end
function GetCardExtensionByName(cardName)
- local card = table.find(Fk.cards, function(card)
- return card.name == cardName
- end)
-
+ local card = Fk.all_card_types[cardName]
return card and card.package.extensionName or ""
end
@@ -220,6 +217,10 @@ function GetCards(pack_name)
return json.encode(ret)
end
+function GetCardSkill(cid)
+ return Fk:getCardById(cid).skill and Fk:getCardById(cid).skill.name or ""
+end
+
function GetCardSpecialSkills(cid)
return json.encode(Fk:getCardById(cid).special_skills or Util.DummyTable)
end
diff --git a/lua/client/i18n/zh_CN.lua b/lua/client/i18n/zh_CN.lua
index 31aa502f..1d19be8e 100644
--- a/lua/client/i18n/zh_CN.lua
+++ b/lua/client/i18n/zh_CN.lua
@@ -89,6 +89,8 @@ Fk:loadTranslationTable{
["Cards Overview"] = "卡牌一览",
["Special card skills:"] = "卡牌的特殊用法:",
["Every suit & number:"] = "所有的花色和点数:",
+ ["Male Audio"] = "男性音效",
+ ["Female Audio"] = "女性音效",
["Scenarios Overview"] = "玩法一览",
["Replay"] = "录像",
["Replay Manager"] = "来欣赏潇洒的录像吧!",
diff --git a/lua/core/card.lua b/lua/core/card.lua
index 4a292c86..6cb51c34 100644
--- a/lua/core/card.lua
+++ b/lua/core/card.lua
@@ -428,7 +428,7 @@ end
--- 比较两张卡牌的花色是否相同
---@param anotherCard Card @ 另一张卡牌
----@param diff? boolean @ 比较二者相同还是不同
+---@param diff? boolean @ 比较二者不同
---@return boolean 返回比较结果
function Card:compareSuitWith(anotherCard, diff)
if self ~= anotherCard and table.contains({ self.suit, anotherCard.suit }, Card.NoSuit) then
@@ -444,8 +444,8 @@ end
--- 比较两张卡牌的颜色是否相同
---@param anotherCard Card @ 另一张卡牌
----@param diff boolean @ 比较二者相同还是不同
----@return boolean 返回比较结果
+---@param diff? boolean @ 比较二者不同
+---@return boolean @ 返回比较结果
function Card:compareColorWith(anotherCard, diff)
if self ~= anotherCard and table.contains({ self.color, anotherCard.color }, Card.NoColor) then
return false
@@ -460,8 +460,8 @@ end
--- 比较两张卡牌的点数是否相同
---@param anotherCard Card @ 另一张卡牌
----@param diff boolean @ 比较二者相同还是不同
----@return boolean 返回比较结果
+---@param diff? boolean @ 比较二者不同
+---@return boolean @ 返回比较结果
function Card:compareNumberWith(anotherCard, diff)
if self ~= anotherCard and self.number < 1 or anotherCard.number < 1 then
return false
diff --git a/lua/core/engine.lua b/lua/core/engine.lua
index 309c6b9b..9294b22b 100644
--- a/lua/core/engine.lua
+++ b/lua/core/engine.lua
@@ -293,6 +293,7 @@ function Engine:addCard(card)
cardId = cardId + 1
table.insert(self.cards, card)
if self.all_card_types[card.name] == nil then
+ self.skills[card.skill.name] = card.skill
self.all_card_types[card.name] = card
end
end
diff --git a/lua/core/player.lua b/lua/core/player.lua
index b1cb839f..ef47e8a4 100644
--- a/lua/core/player.lua
+++ b/lua/core/player.lua
@@ -451,7 +451,7 @@ function Player:getMaxCards()
return math.max(baseValue, 0)
end
---- 获取玩家攻击距离。
+--- 获取玩家攻击范围。
function Player:getAttackRange()
local weapon = Fk:getCardById(self:getEquipment(Card.SubtypeWeapon))
local baseAttackRange = math.max(weapon and weapon.attack_range or 1, 0)
@@ -544,7 +544,7 @@ function Player:distanceTo(other, mode, ignore_dead)
return math.max(ret, 1)
end
---- 获取其他玩家是否在玩家的攻击距离内。
+--- 获取其他玩家是否在玩家的攻击范围内。
---@param other Player @ 其他玩家
---@param fixLimit? integer @ 卡牌距离限制增加专用
function Player:inMyAttackRange(other, fixLimit)
@@ -596,7 +596,7 @@ end
---@return ServerPlayer
function Player:getLastAlive(ignoreRemoved, num)
num = num or 1
- local index = ignoreRemoved and #Fk:currentRoom().alive_players or #table.filter(Fk:currentRoom().alive_players, function(p) return not p:isRemoved() end) - num
+ local index = (ignoreRemoved and #Fk:currentRoom().alive_players or #table.filter(Fk:currentRoom().alive_players, function(p) return not p:isRemoved() end)) - num
return self:getNextAlive(ignoreRemoved, index)
end
@@ -1047,4 +1047,19 @@ function Player:isBuddy(other)
return self.id == id or table.contains(self.buddy_list, id)
end
+--- 比较两名角色的性别是否相同。
+---@param other Player @ 另一名角色
+---@param diff? bool @ 比较二者不同
+---@return boolean @ 返回比较结果
+function Player:compareGenderWith(other, diff)
+ if self == other then return not diff end
+ if self.gender == General.Agender or other.gender == General.Agender then return false end
+ if self.gender == General.Bigender or other.gender == General.Bigender then return true end
+ if diff then
+ return self.gender ~= other.gender
+ else
+ return self.gender == other.gender
+ end
+end
+
return Player
diff --git a/lua/fk_ex.lua b/lua/fk_ex.lua
index d6f329c4..dca92384 100644
--- a/lua/fk_ex.lua
+++ b/lua/fk_ex.lua
@@ -445,6 +445,9 @@ local defaultCardSkill = fk.CreateActiveSkill{
local defaultEquipSkill = fk.CreateActiveSkill{
name = "default_equip_skill",
+ prompt = function(_, selected_cards, _)
+ return "#default_equip_skill:::" .. Fk:getCardById(selected_cards).name .. ":" .. Fk:getCardById(selected_cards):getSubtypeString()
+ end,
mod_target_filter = function(self, to_select, selected, user, card, distance_limited)
return #Fk:currentRoom():getPlayerById(to_select):getAvailableEquipSlots(card.sub_type) > 0
end,
diff --git a/lua/server/ai/random_ai.lua b/lua/server/ai/random_ai.lua
index 74271b8a..aee2894f 100644
--- a/lua/server/ai/random_ai.lua
+++ b/lua/server/ai/random_ai.lua
@@ -12,10 +12,10 @@ function RandomAI:useActiveSkill(skill, card)
local filter_func = skill.cardFilter
if card then
- filter_func = function() return false end
+ filter_func = Util.FalseFunc
end
- if self.command == "PlayCard" and card and (not player:canUse(card) or player:prohibitUse(card)) then
+ if self.command == "PlayCard" and card and (not skill:canUse(player, card) or player:prohibitUse(card)) then
return ""
end
@@ -28,7 +28,7 @@ function RandomAI:useActiveSkill(skill, card)
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 avail_targets = table.filter(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:isProhibited(p, card) then
diff --git a/lua/server/event.lua b/lua/server/event.lua
index a1167b07..7be5d5ab 100644
--- a/lua/server/event.lua
+++ b/lua/server/event.lua
@@ -125,6 +125,7 @@ fk.AfterSkillEffect = 82
fk.AreaAborted = 87
fk.AreaResumed = 88
+fk.GeneralShown = 95
fk.GeneralRevealed = 89
fk.GeneralHidden = 90
@@ -134,4 +135,4 @@ fk.BeforePropertyChange = 92
fk.PropertyChange = 93
fk.AfterPropertyChange = 94
-fk.NumOfEvents = 95
+fk.NumOfEvents = 96
diff --git a/lua/server/events/usecard.lua b/lua/server/events/usecard.lua
index df258768..7d5a4077 100644
--- a/lua/server/events/usecard.lua
+++ b/lua/server/events/usecard.lua
@@ -28,11 +28,9 @@ local playCardEmotionAndSound = function(room, player, card)
soundName = "./packages/" .. card.package.extensionName .. "/audio/card/"
.. (player.gender == General.Male and "male/" or "female/") .. card.name
if not FileIO.exists(soundName .. ".mp3") then
- for _, dir in ipairs(FileIO.ls("./packages/")) do
- soundName = "./packages/" .. dir .. "/audio/card/"
- .. (player.gender == General.Male and "male/" or "female/") .. card.name
- if FileIO.exists(soundName .. ".mp3") then break end
- end
+ local orig = Fk.all_card_types[card.name]
+ soundName = "./packages/" .. orig.package.extensionName .. "/audio/card/"
+ .. (player.gender == General.Male and "male/" or "female/") .. orig.name
end
end
room:broadcastPlaySound(soundName)
diff --git a/lua/server/gamelogic.lua b/lua/server/gamelogic.lua
index d5669e05..a5abc2b5 100644
--- a/lua/server/gamelogic.lua
+++ b/lua/server/gamelogic.lua
@@ -446,7 +446,7 @@ function GameLogic:getEventsOfScope(eventType, n, func, scope)
elseif scope == Player.HistoryPhase then
start_event = event:findParent(GameEvent.Phase, true)
end
-
+ if not start_event then return {} end
return start_event:searchEvents(eventType, n, func)
end
diff --git a/lua/server/room.lua b/lua/server/room.lua
index bd2a6816..e9cd8bc6 100644
--- a/lua/server/room.lua
+++ b/lua/server/room.lua
@@ -310,7 +310,7 @@ function Room:getPlayerById(id)
return nil
end
---- 将房间中的玩家按照座位顺序重新排序。
+--- 将房间中的玩家按照行动顺序重新排序。
---@param playerIds integer[] @ 玩家id列表,这个数组会被这个函数排序
function Room:sortPlayersByAction(playerIds, isTargetGroup)
table.sort(playerIds, function(prev, next)
@@ -1273,19 +1273,12 @@ function Room:askForCard(player, minNum, maxNum, includeEquip, skillName, cancel
chosenCards = ret.cards
else
if cancelable then return {} end
- local hands = player:getCardIds(Player.Hand)
- if includeEquip then
- table.insertTable(hands, player:getCardIds(Player.Equip))
- end
+ local cards = player:getCardIds("he&")
local exp = Exppattern:Parse(pattern)
- hands = table.filter(hands, function(cid)
+ cards = table.filter(cards, function(cid)
return exp:match(Fk:getCardById(cid))
end)
- for _ = 1, minNum do
- local randomId = hands[math.random(1, #hands)]
- table.insert(chosenCards, randomId)
- table.removeOne(hands, randomId)
- end
+ chosenCards = table.random(cards, minNum)
end
return chosenCards
@@ -2873,14 +2866,14 @@ function Room:drawCards(player, num, skillName, fromPlace)
end
--- 将一张或多张牌移动到某处
----@param card integer | Card | Card[] @ 要移动的牌
+---@param card integer | integer[] | Card | Card[] @ 要移动的牌
---@param to_place integer @ 移动的目标位置
----@param target ServerPlayer @ 移动的目标玩家
+---@param target? ServerPlayer @ 移动的目标角色
---@param reason? integer @ 移动时使用的移牌原因
---@param skill_name? string @ 技能名
---@param special_name? string @ 私人牌堆名
---@param visible? boolean @ 是否明置
----@param proposer? integer
+---@param proposer? integer @ 移动操作者的id
function Room:moveCardTo(card, to_place, target, reason, skill_name, special_name, visible, proposer)
reason = reason or fk.ReasonJustMove
skill_name = skill_name or ""
@@ -3268,10 +3261,15 @@ function Room:useSkill(player, skill, effect_cb)
player:revealBySkillName(skill.name)
if not skill.mute then
if skill.attached_equip then
- local equip = Fk:cloneCard(skill.attached_equip)
+ local equip = Fk.all_card_types[skill.attached_equip]
local pkgPath = "./packages/" .. equip.package.extensionName
local soundName = pkgPath .. "/audio/card/" .. equip.name
self:broadcastPlaySound(soundName)
+ self:sendLog{
+ type = "#InvokeSkill",
+ from = player.id,
+ arg = skill.name,
+ }
self:setEmotion(player, pkgPath .. "/image/anim/" .. equip.name)
else
player:broadcastSkillInvoke(skill.name)
diff --git a/lua/server/serverplayer.lua b/lua/server/serverplayer.lua
index 677ad4b9..5966320e 100644
--- a/lua/server/serverplayer.lua
+++ b/lua/server/serverplayer.lua
@@ -38,6 +38,7 @@ function ServerPlayer:initialize(_self)
self.reply_cancel = false
self.phases = {}
self.skipped_phases = {}
+ self.phase_state = {}
self._fake_skills = {}
self._manually_fake_skills = {}
@@ -469,11 +470,13 @@ function ServerPlayer:changePhase(from_phase, to_phase)
return false
end
+---@param phase Phase
+---@param delay? boolean
function ServerPlayer:gainAnExtraPhase(phase, delay)
local room = self.room
delay = (delay == nil) and true or delay
+ local logic = room.logic
if delay then
- local logic = room.logic
local turn = logic:getCurrentEvent():findParent(GameEvent.Phase, true)
if turn then
turn:prependExitFunc(function() self:gainAnExtraPhase(phase, false) end)
@@ -482,16 +485,43 @@ function ServerPlayer:gainAnExtraPhase(phase, delay)
end
local current = self.phase
+
+ local phase_change = {
+ from = current,
+ to = phase
+ }
+
+ local skip = logic:trigger(fk.EventPhaseChanging, self, phase_change)
+
+ phase = phase_change.to
self.phase = phase
room:broadcastProperty(self, "phase")
- room:sendLog{
- type = "#GainAnExtraPhase",
- from = self.id,
- arg = phase_name_table[phase],
- }
+ local cancel_skip = true
+ if phase ~= Player.NotActive and (skip) then
+ cancel_skip = logic:trigger(fk.EventPhaseSkipping, self)
+ end
+ if (not skip) or (cancel_skip) then
+ room:sendLog{
+ type = "#GainAnExtraPhase",
+ from = self.id,
+ arg = phase_name_table[phase],
+ }
- GameEvent(GameEvent.Phase, self, self.phase):exec()
+ GameEvent(GameEvent.Phase, self, self.phase):exec()
+
+ phase_change = {
+ from = phase,
+ to = current
+ }
+ logic:trigger(fk.EventPhaseChanging, self, phase_change)
+ else
+ room:sendLog{
+ type = "#PhaseSkipped",
+ from = self.id,
+ arg = phase_name_table[phase],
+ }
+ end
self.phase = current
room:broadcastProperty(self, "phase")
@@ -592,6 +622,7 @@ function ServerPlayer:endPlayPhase()
-- TODO: send log
end
+---@param delay? boolean
function ServerPlayer:gainAnExtraTurn(delay)
local room = self.room
delay = (delay == nil) and true or delay
@@ -624,10 +655,13 @@ function ServerPlayer:gainAnExtraTurn(delay)
room.current = current
end
+--- 当前是否处于额外的回合。
+--- @return boolean
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"]
@@ -637,13 +671,18 @@ function ServerPlayer:getCurrentExtraTurnReason()
return ex_tag[#ex_tag]
end
+--- 角色摸牌。
+---@param num integer @ 摸牌数
+---@param skillName? string @ 技能名
+---@param fromPlace? string @ 摸牌的位置,"top" 或者 "bottom"
+---@return integer[] @ 摸到的牌
function ServerPlayer:drawCards(num, skillName, fromPlace)
return self.room:drawCards(self, num, skillName, fromPlace)
end
---@param pile_name string
---@param card integer|Card
----@param visible boolean
+---@param visible? boolean
---@param skillName? string
function ServerPlayer:addToPile(pile_name, card, visible, skillName)
local room = self.room
@@ -923,13 +962,21 @@ function ServerPlayer:revealGeneral(isDeputy, no_trigger)
arg2 = generalName,
}
+ local data = {[isDeputy and "d" or "m"] = generalName}
+ room.logic:trigger(fk.GeneralShown, self, data)
if not no_trigger then
local current_event = room.logic:getCurrentEvent()
if table.contains({GameEvent.Round, GameEvent.Turn, GameEvent.Phase}, current_event.event) then
- room.logic:trigger(fk.GeneralRevealed, self, {[isDeputy and "d" or "m"] = generalName})
+ room.logic:trigger(fk.GeneralRevealed, self, data)
else
+ if current_event.parent then
+ repeat
+ if table.contains({GameEvent.Round, GameEvent.Turn, GameEvent.Phase}, current_event.parent.event) then break end
+ current_event = current_event.parent
+ until (not current_event.parent)
+ end
current_event:addExitFunc(function ()
- room.logic:trigger(fk.GeneralRevealed, self, {[isDeputy and "d" or "m"] = generalName})
+ room.logic:trigger(fk.GeneralRevealed, self, data)
end)
end
end
@@ -940,11 +987,18 @@ function ServerPlayer:revealGenerals()
self:revealGeneral(true, true)
local room = self.room
local current_event = room.logic:getCurrentEvent()
+ local data = {["m"] = self:getMark("__heg_general"), ["d"] = self:getMark("__heg_deputy")}
if table.contains({GameEvent.Round, GameEvent.Turn, GameEvent.Phase}, current_event.event) then
- room.logic:trigger(fk.GeneralRevealed, self, {["m"] = self:getMark("__heg_general"), ["d"] = self:getMark("__heg_deputy")})
+ room.logic:trigger(fk.GeneralRevealed, self, data)
else
+ if current_event.parent then
+ repeat
+ if table.contains({GameEvent.Round, GameEvent.Turn, GameEvent.Phase}, current_event.parent.event) then break end
+ current_event = current_event.parent
+ until (not current_event.parent)
+ end
current_event:addExitFunc(function ()
- room.logic:trigger(fk.GeneralRevealed, self, {["m"] = self:getMark("__heg_general"), ["d"] = self:getMark("__heg_deputy")})
+ room.logic:trigger(fk.GeneralRevealed, self, data)
end)
end
end
diff --git a/packages/maneuvering/i18n/en_US.lua b/packages/maneuvering/i18n/en_US.lua
index fadb5107..0f1c9cf7 100644
--- a/packages/maneuvering/i18n/en_US.lua
+++ b/packages/maneuvering/i18n/en_US.lua
@@ -3,27 +3,36 @@ return {
["thunder__slash"] = "Thunder Slash",
[":thunder__slash"] = "Thunder Slash (basic card)
Phase: Action phase
Target: Another player within your ATK range
Effect: Deal 1 Thunder DMG to the targets.
Note: You can only use 1 Slash per action phase.",
+ ["#thunder__slash_skill"] = "Choose 1 player within your ATK range, deal 1 Thunder DMG to him",
+ ["#thunder__slash_skill_multi"] = "Choose up to %arg players within your ATK range. Deal 1 Thunder DMG to them",
["fire__slash"] = "Fire Slash",
[":fire__slash"] = "Fire Slash (basic card)
Phase: Action phase
Target: Another player within your ATK range
Effect: Deal 1 Fire DMG to the targets.
Note: You can only use 1 Slash per action phase.",
+ ["#fire__slash_skill"] = "Choose 1 player within your ATK range, deal 1 Fire DMG to him",
+ ["#fire__slash_skill_multi"] = "Choose up to %arg players within your ATK range. Deal 1 Fire DMG to them",
["analeptic"] = "Alcohol",
[":analeptic"] = "Alcohol (basic card)
Phase: 1. Action phase 2. When you are dying
Target: Yourself
Effect: 1. the DMG of the next Slash you use this turn is increased by +1. (This effect can only be used once per turn) 2. You heal 1 HP.",
+ ["#analeptic_skill"] = "the DMG of the next Slash you use this turn is increased by +1",
["iron_chain"] = "Iron Chain",
[":iron_chain"] = "Iron Chain (trick card)
Phase: Action phase
Target: 1~2 players
Effect: Change chain state of the targets.",
+ ["#iron_chain_skill"] = "Choose 1~2 players. Change their chain states",
["_normal_use"] = "Normally use",
["recast"] = "Recast",
[":recast"] = "Put this card into discard pile, then draw 1 card.",
+ ["#recast"] = "Put this card into discard pile, then draw 1 card",
["fire_attack"] = "Fire Attack",
["fire_attack_skill"] = "Fire Attack",
- [":fire_attack"] = "Fire Attack (trick card)
Phase: Action phase
Target: A player with hand cards
Effect: That player shows 1 hand card; then, if you discard 1 card with the same suit, you deal 1 Fire DMG to him.",
+ [":fire_attack"] = "Fire Attack (trick card)
Phase: Action phase
Target: A player with hand cards
Effect: The target player shows 1 hand card; then, if you discard 1 card with the same suit, you deal 1 Fire DMG to him.",
["#fire_attack-show"] = "%src used Fire Attack to you, please show 1 hand card",
["#fire_attack-discard"] = "You can discard 1 %arg hand card, then deal 1 Fire DMG to %src",
+ ["#fire_attack_skill"] = "Choose a player with hand cards. He shows 1 hand card;
then, if you discard 1 card with the same suit, you deal 1 Fire DMG to him",
["supply_shortage"] = "Supply Shortage",
- [":supply_shortage"] = "Supply Shortage (delayed trick card)
Phase: Action phase
Target: Another player at distance 1
Effect: Place this card in target's judgement area. He performs a judgement in his judge phase: if result is not club, he skips his draw phase.",
+ [":supply_shortage"] = "Supply Shortage (delayed trick card)
Phase: Action phase
Target: Another player at distance 1
Effect: Place this card in target's judgement area. He performs a judgement in his judge phase: if result is not ♣, he skips his draw phase.",
+ ["#supply_shortage_skill"] = "Place this card in another player's judgement area. He performs a judgement in his judge phase:
If result is not ♣, he skips his draw phase",
["guding_blade"] = "Ancient Scimitar",
[":guding_blade"] = "Ancient Scimitar (equip card, weapon)
ATK range: 2
Weapon skill: When your used Slash is about to cause DMG, if the target player has no hand cards: the DMG is increased by +1.",
diff --git a/packages/maneuvering/init.lua b/packages/maneuvering/init.lua
index 517371bb..583f677c 100644
--- a/packages/maneuvering/init.lua
+++ b/packages/maneuvering/init.lua
@@ -6,6 +6,19 @@ local slash = Fk:cloneCard("slash")
local thunderSlashSkill = fk.CreateActiveSkill{
name = "thunder__slash_skill",
+ prompt = function(self, selected_cards)
+ local card = Fk:cloneCard("thunder__slash")
+ card.subcards = Card:getIdList(selected_cards)
+ local max_num = self:getMaxTargetNum(Self, card)
+ if max_num > 1 then
+ local num = #table.filter(Fk:currentRoom().alive_players, function (p)
+ return p ~= Self and not Self:isProhibited(p, card)
+ end)
+ max_num = math.min(num, max_num)
+ end
+ card.subcards = {}
+ return max_num > 1 and "#thunder__slash_skill_multi:::" .. max_num or "#thunder__slash_skill"
+ end,
max_phase_use_time = 1,
target_num = 1,
can_use = slash.skill.canUse,
@@ -45,6 +58,19 @@ extension:addCards{
local fireSlashSkill = fk.CreateActiveSkill{
name = "fire__slash_skill",
+ prompt = function(self, selected_cards)
+ local card = Fk:cloneCard("fire__slash")
+ card.subcards = Card:getIdList(selected_cards)
+ local max_num = self:getMaxTargetNum(Self, card)
+ if max_num > 1 then
+ local num = #table.filter(Fk:currentRoom().alive_players, function (p)
+ return p ~= Self and not Self:isProhibited(p, card)
+ end)
+ max_num = math.min(num, max_num)
+ end
+ card.subcards = {}
+ return max_num > 1 and "#fire__slash_skill_multi:::" .. max_num or "#fire__slash_skill"
+ end,
max_phase_use_time = 1,
target_num = 1,
can_use = slash.skill.canUse,
@@ -80,8 +106,9 @@ extension:addCards{
local analepticSkill = fk.CreateActiveSkill{
name = "analeptic_skill",
+ prompt = "#analeptic_skill",
max_turn_use_time = 1,
- mod_target_filter = function(self, to_select, selected, user, card, distance_limited)
+ mod_target_filter = function(self, to_select, _, _, card, _)
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
@@ -90,7 +117,7 @@ local analepticSkill = fk.CreateActiveSkill{
can_use = function(self, player, card)
return self:withinTimesLimit(player, Player.HistoryTurn, card, "analeptic", player)
end,
- on_use = function(self, room, use)
+ on_use = function(_, _, use)
if not use.tos or #TargetGroup:getRealTargets(use.tos) == 0 then
use.tos = { { use.from } }
end
@@ -99,7 +126,7 @@ local analepticSkill = fk.CreateActiveSkill{
use.extraUse = true
end
end,
- on_effect = function(self, room, effect)
+ on_effect = function(_, room, effect)
local to = room:getPlayerById(effect.to)
if effect.extra_data and effect.extra_data.analepticRecover then
room:recover({
@@ -120,7 +147,7 @@ local analepticEffect = fk.CreateTriggerSkill{
global = true,
priority = 0, -- game rule
events = { fk.PreCardUse, fk.EventPhaseStart },
- can_trigger = function(self, event, target, player, data)
+ can_trigger = function(_, event, target, player, data)
if target ~= player then
return false
end
@@ -131,7 +158,7 @@ local analepticEffect = fk.CreateTriggerSkill{
return target.phase == Player.NotActive
end
end,
- on_trigger = function(self, event, target, player, data)
+ on_trigger = function(_, event, _, player, data)
local room = player.room
if event == fk.PreCardUse then
data.additionalDamage = (data.additionalDamage or 0) + player.drank
@@ -168,8 +195,9 @@ extension:addCards({
local recast = fk.CreateActiveSkill{
name = "recast",
+ prompt = "#recast",
target_num = 0,
- on_use = function(self, room, effect)
+ on_use = function(_, room, effect)
room:recastCard(effect.cards, room:getPlayerById(effect.from))
end
}
@@ -177,17 +205,16 @@ Fk:addSkill(recast)
local ironChainCardSkill = fk.CreateActiveSkill{
name = "iron_chain_skill",
+ prompt = "#iron_chain_skill",
min_target_num = 1,
max_target_num = 2,
- mod_target_filter = function(self, to_select, selected, user, card, distance_limited)
- return true
- end,
+ mod_target_filter = Util.TrueFunc,
target_filter = function(self, to_select, selected, _, card)
if #selected < self:getMaxTargetNum(Self, card) then
return self:modTargetFilter(to_select, selected, Self.id, card)
end
end,
- on_effect = function(self, room, cardEffectEvent)
+ on_effect = function(_, room, cardEffectEvent)
local to = room:getPlayerById(cardEffectEvent.to)
to:setChainState(not to.chained)
end,
@@ -210,8 +237,9 @@ extension:addCards{
local fireAttackSkill = fk.CreateActiveSkill{
name = "fire_attack_skill",
+ prompt = "#fire_attack_skill",
target_num = 1,
- mod_target_filter = function(self, to_select, selected, user, card, distance_limited)
+ mod_target_filter = function(_, to_select, _, _, _, _)
local to = Fk:currentRoom():getPlayerById(to_select)
return not to:isKongcheng()
end,
@@ -256,8 +284,9 @@ extension:addCards{
local supplyShortageSkill = fk.CreateActiveSkill{
name = "supply_shortage_skill",
+ prompt = "#supply_shortage_skill",
distance_limit = 1,
- mod_target_filter = function(self, to_select, selected, user, card, distance_limited)
+ mod_target_filter = function(self, to_select, _, user, card, distance_limited)
local player = Fk:currentRoom():getPlayerById(to_select)
local from = Fk:currentRoom():getPlayerById(user)
return from ~= player and not (distance_limited and not self:withinDistanceLimit(from, false, card, player))
@@ -302,8 +331,8 @@ local gudingSkill = fk.CreateTriggerSkill{
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
+ can_trigger = function(self, _, target, player, data)
+ return target == player and player:hasSkill(self) and
data.to:isKongcheng() and data.card and data.card.trueName == "slash" and
not data.chain
end,
@@ -326,8 +355,8 @@ local fanSkill = fk.CreateTriggerSkill{
name = "#fan_skill",
attached_equip = "fan",
events = { fk.AfterCardUseDeclared },
- can_trigger = function(self, event, target, player, data)
- return target == player and player:hasSkill(self.name) and data.card.name == "slash"
+ can_trigger = function(self, _, target, player, data)
+ return target == player and player:hasSkill(self) and data.card.name == "slash"
end,
on_use = function(_, _, _, _, data)
local card = Fk:cloneCard("fire__slash")
@@ -356,15 +385,15 @@ local vineSkill = fk.CreateTriggerSkill{
events = {fk.PreCardEffect, fk.DamageInflicted},
can_trigger = function(self, event, target, player, data)
if event == fk.DamageInflicted then
- return target == player and player:hasSkill(self.name) and
+ return target == player and player:hasSkill(self) and
data.damageType == fk.FireDamage
end
local effect = data ---@type CardEffectEvent
- return player.id == effect.to and player:hasSkill(self.name) and
+ return player.id == effect.to and player:hasSkill(self) 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)
+ on_use = function(_, event, _, player, data)
local room = player.room
if event == fk.DamageInflicted then
room:broadcastPlaySound("./packages/maneuvering/audio/card/vineburn")
@@ -392,8 +421,8 @@ local silverLionSkill = fk.CreateTriggerSkill{
attached_equip = "silver_lion",
frequency = Skill.Compulsory,
events = {fk.DamageInflicted},
- can_trigger = function(self, event, target, player, data)
- return target == player and player:hasSkill(self.name) and data.damage > 1
+ can_trigger = function(self, _, target, player, data)
+ return target == player and player:hasSkill(self) and data.damage > 1
end,
on_use = function(_, _, _, _, data)
data.damage = 1
@@ -456,31 +485,50 @@ Fk:loadTranslationTable{
["thunder__slash"] = "雷杀",
[":thunder__slash"] = "基本牌
时机:出牌阶段
目标:攻击范围内的一名角色
效果:对目标角色造成1点雷电伤害。",
+ ["#thunder__slash_skill"] = "选择攻击范围内的一名角色,对其造成1点雷电伤害",
+ ["#thunder__slash_skill_multi"] = "选择攻击范围内的至多%arg名角色,对这些角色各造成1点雷电伤害",
+
["fire__slash"] = "火杀",
[":fire__slash"] = "基本牌
时机:出牌阶段
目标:攻击范围内的一名角色
效果:对目标角色造成1点火焰伤害。",
+ ["#fire__slash_skill"] = "选择攻击范围内的一名角色,对其造成1点火焰伤害",
+ ["#fire__slash_skill_multi"] = "选择攻击范围内的至多%arg名角色,对这些角色各造成1点火焰伤害",
+
["analeptic"] = "酒",
[":analeptic"] = "基本牌
时机:出牌阶段/你处于濒死状态时
目标:你
效果:目标角色本回合使用的下一张【杀】将要造成的伤害+1/目标角色回复1点体力。",
+ ["#analeptic_skill"] = "你于此回合内使用的下一张【杀】的伤害值基数+1",
+
["iron_chain"] = "铁锁连环",
[":iron_chain"] = "锦囊牌
时机:出牌阶段
目标:一至两名角色
效果:横置或重置目标角色的武将牌。",
+ ["#iron_chain_skill"] = "选择一至两名角色,这些角色横置或重置",
["_normal_use"] = "正常使用",
["recast"] = "重铸",
[":recast"] = "你可以将此牌置入弃牌堆,然后摸一张牌。",
+ ["#recast"] = "将此牌置入弃牌堆,然后摸一张牌",
+
["fire_attack"] = "火攻",
["fire_attack_skill"] = "火攻",
- [":fire_attack"] = "锦囊牌
时机:出牌阶段
目标:一名有手牌的角色
效果:目标角色展示一张手牌,然后你可以弃置一张与所展示牌花色相同的手牌令其受到1点火焰伤害。",
+ [":fire_attack"] = "锦囊牌
时机:出牌阶段
目标:一名有手牌的角色
效果:目标角色展示一张手牌,然后你可以弃置一张与此牌花色相同的手牌对其造成1点火焰伤害。",
["#fire_attack-show"] = "%src 对你使用了火攻,请展示一张手牌",
["#fire_attack-discard"] = "你可弃置一张 %arg 手牌,对 %src 造成1点火属性伤害",
+ ["#fire_attack_skill"] = "选择一名有手牌的角色,令其展示一张手牌,
然后你可以弃置一张与此牌花色相同的手牌对其造成1点火焰伤害",
+
["supply_shortage"] = "兵粮寸断",
- [":supply_shortage"] = "延时锦囊牌
时机:出牌阶段
目标:距离1的一名其他角色
效果:将此牌置于目标角色判定区内。其判定阶段进行判定:若结果不为梅花,其跳过摸牌阶段。然后将【兵粮寸断】置入弃牌堆。",
+ [":supply_shortage"] = "延时锦囊牌
时机:出牌阶段
目标:距离1的一名其他角色
效果:将此牌置于目标角色判定区内。其判定阶段进行判定:若结果不为♣,其跳过摸牌阶段。然后将【兵粮寸断】置入弃牌堆。",
+ ["#supply_shortage_skill"] = "选择距离1的一名角色,将此牌置于其判定区内。其判定阶段判定:
若结果不为♣,其跳过摸牌阶段",
+
["guding_blade"] = "古锭刀",
[":guding_blade"] = "装备牌·武器
攻击范围:2
武器技能:锁定技。每当你使用【杀】对目标角色造成伤害时,若该角色没有手牌,此伤害+1。",
+
["fan"] = "朱雀羽扇",
[":fan"] = "装备牌·武器
攻击范围:4
武器技能:你可以将一张普通【杀】当火【杀】使用。",
["#fan_skill"] = "朱雀羽扇",
+
["vine"] = "藤甲",
[":vine"] = "装备牌·防具
防具技能:锁定技。【南蛮入侵】、【万箭齐发】和普通【杀】对你无效。每当你受到火焰伤害时,此伤害+1。",
+
["silver_lion"] = "白银狮子",
[":silver_lion"] = "装备牌·防具
防具技能:锁定技。每当你受到伤害时,若此伤害大于1点,防止多余的伤害。每当你失去装备区里的【白银狮子】后,你回复1点体力。",
+
["hualiu"] = "骅骝",
[":hualiu"] = "装备牌·坐骑
坐骑技能:其他角色与你的距离+1。",
}
diff --git a/packages/standard/i18n/en_US.lua b/packages/standard/i18n/en_US.lua
index 9be46e9a..5c6238c5 100644
--- a/packages/standard/i18n/en_US.lua
+++ b/packages/standard/i18n/en_US.lua
@@ -15,8 +15,8 @@ Fk:loadTranslationTable({
["jianxiong"] = "Villainous Hero",
[":jianxiong"] = "After you suffer DMG: you can take the card(s) that caused it.",
["hujia"] = "Royal Escort",
- [":hujia"] = "Lord skill, when you need to use/play Dodge: you can ask other Wei characters to play Dodge, which is regard as you use/play that.",
- ["#hujia-ask"] = "Royal Escort: you can play a jink, which is regarded as %src uses/plays",
+ [":hujia"] = "(lord) When you need to use/play Dodge: you can ask other Wei characters to play Dodge, which is regard as you use/play that.",
+ ["#hujia-ask"] = "Royal Escort: you can play a Dodge, which is regarded as %src uses/plays",
["simayi"] = "Sima Yi",
["guicai"] = "Demonic Talent",
@@ -57,7 +57,7 @@ Fk:loadTranslationTable({
["rende"] = "Benevolence",
[":rende"] = "In your Action Phase: you can give any # of hand cards to other players; then, if you have given a total of 2 or more cards, you heal 1 HP (only once).",
["jijiang"] = "Rouse",
- [":jijiang"] = "Lord skill, when you need to use/play Slash: you can ask other Shu characters to play Slash, which is regard as you use/play that.",
+ [":jijiang"] = "(lord) When you need to use/play Slash: you can ask other Shu characters to play Slash, which is regard as you use/play that.",
["#jijiang-ask"] = "Rouse: you can play a Slash, which is regarded as %src uses/plays",
["guanyu"] = "Guan Yu",
@@ -66,13 +66,13 @@ Fk:loadTranslationTable({
["zhangfei"] = "Zhang Fei",
["paoxiao"] = "Roar",
- [":paoxiao"] = "Compulsory skill, you can use any # of Slash.",
+ [":paoxiao"] = "(forced) You can use any # of Slash.",
["zhugeliang"] = "Zhuge Liang",
["guanxing"] = "Stargaze",
[":guanxing"] = "In your Beginning Phase: you can examine X cards from the deck; then, you can place any # of them at the top of the deck and the rest at the bottom. (X = # of living players, max. 5)",
["kongcheng"] = "Empty Fort",
- [":kongcheng"] = "Compulsory skill, if you don’t have hand cards, you cannot be the target of Sha or Duel.",
+ [":kongcheng"] = "(forced) If you don’t have hand cards, you cannot be the target of Sha or Duel.",
["zhaoyun"] = "Zhao Yun",
["longdan"] = "Dragon Heart",
@@ -80,7 +80,7 @@ Fk:loadTranslationTable({
["machao"] = "Ma Chao",
["mashu"] = "Horsemanship",
- [":mashu"] = "Compulsory skill, the distance from you to other players is reduced by -1.",
+ [":mashu"] = "(forced) The distance from you to other players is reduced by -1.",
["tieqi"] = "Iron Cavalry",
[":tieqi"] = "After you use Slash to target a player: you can perform a judgment; if it’s red, he can't use Dodge.",
@@ -88,19 +88,19 @@ Fk:loadTranslationTable({
["jizhi"] = "Wisdom",
[":jizhi"] = "When you use a non-delay trick card, you can draw 1 card.",
["qicai"] = "Genius",
- [":qicai"] = "Compulsory skill, your trick cards have unlimited range.",
+ [":qicai"] = "(forced) Your trick cards have unlimited range.",
["sunquan"] = "Sun Quan",
["zhiheng"] = "Balance of Power",
[":zhiheng"] = "Once per Action Phase: you can discard any # of cards; then, draw the same # of cards.",
["jiuyuan"] = "Rescued",
- [":jiuyuan"] = "Lord skill, compulsory skill, when another Wu character uses Peach to you, you heal +1 HP.",
+ [":jiuyuan"] = "(lord, forced) When another Wu character uses Peach to you, you heal +1 HP.",
["ganning"] = "Gan Ning",
["qixi"] = "Surprise Raid",
[":qixi"] = "You can use any black card as Dismantlement.",
- ["lvmeng"] = "Lv Meng",
+ ["lvmeng"] = "Lü Meng",
["keji"] = "Self Mastery",
[":keji"] = "If you haven't used/played Slash in your Action Phase, you can skip your Discard Phase.",
@@ -123,7 +123,7 @@ Fk:loadTranslationTable({
["luxun"] = "Lu Xun",
["qianxun"] = "Humility",
- [":qianxun"] = "Compulsory skill, you can't be the target of Snatch or Indulgence.",
+ [":qianxun"] = "(forced) You can't be the target of Snatch or Indulgence.",
["lianying"] = "One After Another",
[":lianying"] = "When you lose your last hand card: you can draw 1 card.",
@@ -139,9 +139,9 @@ Fk:loadTranslationTable({
["jijiu"] = "First Aid",
[":jijiu"] = "Outside of your turn: you can use any red card as Peach.",
- ["lvbu"] = "Lv Bu",
+ ["lvbu"] = "Lü Bu",
["wushuang"] = "Without Equal",
- [":wushuang"] = "Compulsory skill, if you use Slash to target a player, the target needs to use 2 Dodge to evade it. During Duel, the opponent must play 2 Slash per round.",
+ [":wushuang"] = "(forced) If you use Slash to target a player, the target needs to use 2 Dodge to evade it. During Duel, the opponent must play 2 Slash per round.",
["diaochan"] = "Diao Chan",
["lijian"] = "Seed of Animosity",
@@ -199,7 +199,17 @@ Fk:loadTranslationTable({
["choose_players_to_move_card_in_board"] = "Choose players",
["reveal_skill"] = "Reveal",
["#reveal_skill"] = "Choose a character to reveal",
- [":reveal_skill"] = "In action phase, you can reveal a character who has Compulsory skills.",
+ [":reveal_skill"] = "In action phase, you can reveal a character who has Forced skills.",
["game_rule"] = "Discard",
}, "en_US")
+
+-- init
+Fk:loadTranslationTable({
+ ["left lord and loyalist alive"] = "You're the only surviving Renegade and all others alive are lord and loyalists.",
+ ["left one rebel alive"] = "You're the only surviving Rebel and there's no surviving Renegade.",
+ ["left you alive"] = "No surviving loyalists and only another fraction remains.",
+ ["loyalist never surrender"] = "Loyalist never surrender!",
+
+ ["anjiang"] = "Hidden Char.",
+}, "en_US")
diff --git a/packages/standard/i18n/zh_CN.lua b/packages/standard/i18n/zh_CN.lua
index 2396f98a..152d88e6 100644
--- a/packages/standard/i18n/zh_CN.lua
+++ b/packages/standard/i18n/zh_CN.lua
@@ -55,7 +55,7 @@ Fk:loadTranslationTable{
["$luoyi1"] = "脱!",
["$luoyi2"] = "谁来与我大战三百回合?",
["luoyi"] = "裸衣",
- [":luoyi"] = "摸牌阶段,你可以少摸一张牌,若如此做,本回合你使用【杀】或【决斗】对目标角色造成伤害时,此伤害+1。",
+ [":luoyi"] = "摸牌阶段,你可以少摸一张牌,然后本回合你使用【杀】或【决斗】对目标角色造成伤害时,此伤害+1。",
["guojia"] = "郭嘉",
["~guojia"] = "咳,咳……",
@@ -66,7 +66,7 @@ Fk:loadTranslationTable{
["$yiji1"] = "也好。",
["$yiji2"] = "罢了。",
["yiji"] = "遗计",
- [":yiji"] = "每当你受到1点伤害后,你可以观看牌堆顶的两张牌并任意分配它们。",
+ [":yiji"] = "当你受到1点伤害后,你可以观看牌堆顶的两张牌并任意分配它们。",
["yiji_active"] = "遗计",
["#yiji-give"] = "遗计:你可以将这些牌分配给任意角色,点“取消”自己保留",
@@ -75,7 +75,7 @@ Fk:loadTranslationTable{
["$luoshen1"] = "髣髴兮若轻云之蔽月。",
["$luoshen2"] = "飘飖兮若流风之回雪。",
["luoshen"] = "洛神",
- [":luoshen"] = "准备阶段开始时,你可以进行判定:若结果为黑色,判定牌生效后你获得之,然后你可以再次发动“洛神”。",
+ [":luoshen"] = "准备阶段开始时,你可以进行判定:若结果为黑色,判定牌生效后你获得之,然后你可以重复此流程。",
["#luoshen_obtain"] = "洛神",
["$qingguo1"] = "凌波微步,罗袜生尘。",
["$qingguo2"] = "体迅飞凫,飘忽若神。",
@@ -129,27 +129,27 @@ Fk:loadTranslationTable{
["machao"] = "马超",
["~machao"] = "(马蹄远去声)",
["mashu"] = "马术",
- [":mashu"] = "锁定技。你与其他角色的距离-1。",
+ [":mashu"] = "锁定技,你与其他角色的距离-1。",
["$tieqi1"] = "全军突击!",
["$tieqi2"] = "(马蹄、马嘶声)",
["tieqi"] = "铁骑",
- [":tieqi"] = "每当你指定【杀】的目标后,你可以进行判定:若结果为红色,该角色不能使用【闪】响应此【杀】。",
+ [":tieqi"] = "当你指定【杀】的目标后,你可以进行判定:若结果为红色,该角色不能使用【闪】响应此【杀】。",
["huangyueying"] = "黄月英",
["~huangyueying"] = "亮……",
["$jizhi1"] = "哼哼~",
["$jizhi2"] = "哼~",
["jizhi"] = "集智",
- [":jizhi"] = "每当你使用一张非延时锦囊牌时,你可以摸一张牌。",
+ [":jizhi"] = "当你使用非转化的普通锦囊牌时,你可摸一张牌。",
["qicai"] = "奇才",
- [":qicai"] = "锁定技。你使用锦囊牌无距离限制。",
+ [":qicai"] = "锁定技,你使用锦囊牌无距离限制。",
["sunquan"] = "孙权",
["~sunquan"] = "父亲,大哥,仲谋愧矣……",
["$zhiheng1"] = "容我三思。",
["$zhiheng2"] = "且慢。",
["zhiheng"] = "制衡",
- [":zhiheng"] = "阶段技,你可以弃置至少一张牌然后摸等量的牌。",
+ [":zhiheng"] = "出牌阶段限一次,你可以弃置至少一张牌然后摸等量的牌。",
["$jiuyuan1"] = "有汝辅佐,甚好!",
["$jiuyuan2"] = "好舒服啊。",
["jiuyuan"] = "救援",
@@ -196,7 +196,7 @@ Fk:loadTranslationTable{
["$liuli1"] = "交给你了。",
["$liuli2"] = "你来嘛~",
["liuli"] = "流离",
- [":liuli"] = "每当你成为【杀】的目标时,你可以弃置一张牌并选择你攻击范围内为此【杀】合法目标(无距离限制)的一名角色:若如此做,该角色代替你成为此【杀】的目标。",
+ [":liuli"] = "当你成为【杀】的目标时,你可以弃置一张牌并选择你攻击范围内为此【杀】合法目标(无距离限制)的一名角色:若如此做,该角色代替你成为此【杀】的目标。",
["#liuli-target"] = "流离:你可以弃置一张牌,将【杀】的目标转移给一名其他角色",
["luxun"] = "陆逊",
@@ -208,25 +208,25 @@ Fk:loadTranslationTable{
["$lianying1"] = "牌不是万能的,但是没牌是万万不能的。",
["$lianying2"] = "旧的不去,新的不来。",
["lianying"] = "连营",
- [":lianying"] = "每当你失去最后的手牌后,你可以摸一张牌。",
+ [":lianying"] = "当你失去最后的手牌后,你可以摸一张牌。",
["sunshangxiang"] = "孙尚香",
["~sunshangxiang"] = "不,还不可以死……",
["$xiaoji1"] = "哼!",
["$xiaoji2"] = "看我的厉害!",
["xiaoji"] = "枭姬",
- [":xiaoji"] = "每当你失去一张装备区的装备牌后,你可以摸两张牌。",
+ [":xiaoji"] = "当你失去一张装备区的装备牌后,你可以摸两张牌。",
["$jieyin1"] = "夫君,身体要紧。",
["$jieyin2"] = "他好,我也好。",
["jieyin"] = "结姻",
- [":jieyin"] = "阶段技,你可以弃置两张手牌并选择一名已受伤的男性角色:若如此做,你和该角色各回复1点体力。",
+ [":jieyin"] = "出牌阶段限一次,你可以弃置两张手牌并选择一名已受伤的男性角色:若如此做,你和该角色各回复1点体力。",
["huatuo"] = "华佗",
["~huatuo"] = "医者……不能自医啊……",
["$qingnang1"] = "早睡早起,方能养生。",
["$qingnang2"] = "越老越要补啊。",
["qingnang"] = "青囊",
- [":qingnang"] = "阶段技,你可以弃置一张手牌并选择一名已受伤的角色:若如此做,该角色回复1点体力。",
+ [":qingnang"] = "出牌阶段限一次,你可以弃置一张手牌并选择一名已受伤的角色:若如此做,该角色回复1点体力。",
["$jijiu1"] = "别紧张,有老夫呢。",
["$jijiu2"] = "救人一命,胜造七级浮屠。",
["jijiu"] = "急救",
@@ -244,7 +244,7 @@ Fk:loadTranslationTable{
["$lijian1"] = "嗯呵呵~~呵呵~~",
["$lijian2"] = "夫君,你要替妾身作主啊……",
["lijian"] = "离间",
- [":lijian"] = "阶段技,你可以弃置一张牌并选择两名其他男性角色,后选择的角色视为对先选择的角色使用了一张不能被无懈可击的决斗。",
+ [":lijian"] = "出牌阶段限一次,你可以弃置一张牌并选择两名其他男性角色,后选择的角色视为对先选择的角色使用了一张不能被无懈可击的决斗。",
["$biyue1"] = "失礼了~",
["$biyue2"] = "羡慕吧~",
["biyue"] = "闭月",
diff --git a/packages/standard/init.lua b/packages/standard/init.lua
index 4fce65d5..b1cec657 100644
--- a/packages/standard/init.lua
+++ b/packages/standard/init.lua
@@ -10,7 +10,7 @@ local jianxiong = fk.CreateTriggerSkill{
anim_type = "masochism",
events = {fk.Damaged},
can_trigger = function(self, event, target, player, data)
- if target == player and player:hasSkill(self.name) and data.card then
+ if target == player and player:hasSkill(self) and data.card then
local room = player.room
local subcards = data.card:isVirtual() and data.card.subcards or {data.card.id}
return #subcards>0 and table.every(subcards, function(id) return room:getCardArea(id) == Card.Processing end)
@@ -78,7 +78,7 @@ local guicai = fk.CreateTriggerSkill{
anim_type = "control",
events = {fk.AskForRetrial},
can_trigger = function(self, event, target, player, data)
- return player:hasSkill(self.name) and not player:isKongcheng()
+ return player:hasSkill(self) and not player:isKongcheng()
end,
on_cost = function(self, event, target, player, data)
local room = player.room
@@ -98,7 +98,7 @@ local fankui = fk.CreateTriggerSkill{
anim_type = "masochism",
events = {fk.Damaged},
can_trigger = function(self, event, target, player, data)
- if target == player and player:hasSkill(self.name) and data.from and not data.from.dead then
+ if target == player and player:hasSkill(self) and data.from and not data.from.dead then
if data.from == player then
return #player.player_cards[Player.Equip] > 0
else
@@ -122,9 +122,6 @@ local ganglie = fk.CreateTriggerSkill{
name = "ganglie",
anim_type = "masochism",
events = {fk.Damaged},
- can_trigger = function(self, event, target, player, data)
- return target == player and player:hasSkill(self.name)
- end,
on_use = function(self, event, target, player, data)
local room = player.room
local from = data.from
@@ -156,13 +153,13 @@ local tuxi = fk.CreateTriggerSkill{
anim_type = "control",
events = {fk.EventPhaseStart},
can_trigger = function(self, event, target, player, data)
- return target == player and player:hasSkill(self.name) and player.phase == Player.Draw and
+ return target == player and player:hasSkill(self) and player.phase == Player.Draw and
table.find(player.room:getOtherPlayers(player), function(p) return not p:isKongcheng() end)
end,
on_cost = function(self, event, target, player, data)
local room = player.room
local targets = table.map(table.filter(room:getOtherPlayers(player), function(p)
- return not p:isKongcheng() end), function (p) return p.id end)
+ return not p:isKongcheng() end), Util.IdMapper)
local result = room:askForChoosePlayers(player, targets, 1, 2, "#tuxi-ask", self.name)
if #result > 0 then
@@ -191,7 +188,7 @@ local luoyi = fk.CreateTriggerSkill{
anim_type = "offensive",
events = {fk.DrawNCards},
can_trigger = function(self, event, target, player, data)
- return target == player and player:hasSkill(self.name) and data.n > 0
+ return target == player and player:hasSkill(self) and data.n > 0
end,
on_use = function(self, event, target, player, data)
data.n = data.n - 1
@@ -205,9 +202,7 @@ local luoyi_trigger = fk.CreateTriggerSkill{
return target == player and player:usedSkillTimes("luoyi", Player.HistoryTurn) > 0 and
not data.chain and data.card and (data.card.trueName == "slash" or data.card.name == "duel")
end,
- on_cost = function(self, event, target, player, data)
- return true
- end,
+ on_cost = Util.TrueFunc,
on_use = function(self, event, target, player, data)
local room = player.room
player:broadcastSkillInvoke("luoyi")
@@ -224,7 +219,7 @@ local tiandu = fk.CreateTriggerSkill{
anim_type = "drawcard",
events = {fk.FinishJudge},
can_trigger = function(self, event, target, player, data)
- return target == player and player:hasSkill(self.name) and player.room:getCardArea(data.card) == Card.Processing
+ return target == player and player:hasSkill(self) and player.room:getCardArea(data.card) == Card.Processing
end,
on_use = function(self, event, target, player, data)
player.room:obtainCard(player.id, data.card, true, fk.ReasonJustMove)
@@ -332,7 +327,7 @@ local luoshen = fk.CreateTriggerSkill{
anim_type = "drawcard",
events = {fk.EventPhaseStart},
can_trigger = function(self, event, target, player, data)
- return target == player and player:hasSkill(self.name) and player.phase == Player.Start
+ return target == player and player:hasSkill(self) and player.phase == Player.Start
end,
on_use = function(self, event, target, player, data)
local room = player.room
@@ -419,9 +414,7 @@ local jijiang = fk.CreateViewAsSkill{
name = "jijiang$",
anim_type = "offensive",
pattern = "slash",
- card_filter = function(self, to_select, selected)
- return false
- end,
+ card_filter = Util.FalseFunc,
view_as = function(self, cards)
if #cards ~= 0 then
return nil
@@ -517,7 +510,7 @@ local paoxiaoAudio = fk.CreateTriggerSkill{
visible = false,
refresh_events = {fk.CardUsing},
can_refresh = function(self, event, target, player, data)
- return target == player and player:hasSkill(self.name) and
+ return target == player and player:hasSkill(self) and
data.card.trueName == "slash" and
player:usedCardTimes("slash") > 1
end,
@@ -534,7 +527,7 @@ local paoxiao = fk.CreateTargetModSkill{
name = "paoxiao",
frequency = Skill.Compulsory,
bypass_times = function(self, player, skill, scope)
- if player:hasSkill(self.name) and skill.trueName == "slash_skill"
+ if player:hasSkill(self) and skill.trueName == "slash_skill"
and scope == Player.HistoryPhase then
return true
end
@@ -549,7 +542,7 @@ local guanxing = fk.CreateTriggerSkill{
anim_type = "control",
events = {fk.EventPhaseStart},
can_trigger = function(self, event, target, player, data)
- return target == player and player:hasSkill(self.name) and
+ return target == player and player:hasSkill(self) and
player.phase == Player.Start
end,
on_use = function(self, event, target, player, data)
@@ -561,7 +554,7 @@ local kongchengAudio = fk.CreateTriggerSkill{
name = "#kongchengAudio",
refresh_events = {fk.AfterCardsMove},
can_refresh = function(self, event, target, player, data)
- if not player:hasSkill(self.name) then return end
+ if not player:hasSkill(self) then return end
if not player:isKongcheng() then return end
for _, move in ipairs(data) do
if move.from == player.id then
@@ -582,7 +575,7 @@ local kongcheng = fk.CreateProhibitSkill{
name = "kongcheng",
frequency = Skill.Compulsory,
is_prohibited = function(self, from, to, card)
- if to:hasSkill(self.name) and to:isKongcheng() then
+ if to:hasSkill(self) and to:isKongcheng() then
return card.trueName == "slash" or card.trueName == "duel"
end
end,
@@ -631,7 +624,7 @@ local mashu = fk.CreateDistanceSkill{
name = "mashu",
frequency = Skill.Compulsory,
correct_func = function(self, from, to)
- if from:hasSkill(self.name) then
+ if from:hasSkill(self) then
return -1
end
end,
@@ -641,7 +634,7 @@ local tieqi = fk.CreateTriggerSkill{
anim_type = "offensive",
events = {fk.TargetSpecified},
can_trigger = function(self, event, target, player, data)
- return target == player and player:hasSkill(self.name) and
+ return target == player and player:hasSkill(self) and
data.card.trueName == "slash"
end,
on_use = function(self, event, target, player, data)
@@ -666,7 +659,7 @@ local jizhi = fk.CreateTriggerSkill{
anim_type = "drawcard",
events = {fk.CardUsing},
can_trigger = function(self, event, target, player, data)
- return target == player and player:hasSkill(self.name) and data.card:isCommonTrick() and
+ return target == player and player:hasSkill(self) and data.card:isCommonTrick() and
(not data.card:isVirtual() or #data.card.subcards == 0)
end,
on_use = function(self, event, target, player, data)
@@ -677,7 +670,7 @@ local qicai = fk.CreateTargetModSkill{
name = "qicai",
frequency = Skill.Compulsory,
bypass_distances = function(self, player, skill, card)
- return player:hasSkill(self.name) and card and card.type == Card.TypeTrick
+ return player:hasSkill(self) and card and card.type == Card.TypeTrick
end,
}
local huangyueying = General:new(extension, "huangyueying", "shu", 3, 3, General.Female)
@@ -690,11 +683,11 @@ local zhiheng = fk.CreateActiveSkill{
can_use = function(self, player)
return player:usedSkillTimes(self.name, Player.HistoryPhase) == 0
end,
- card_filter = function(self, to_select, selected)
- return not Self:prohibitDiscard(Fk:getCardById(to_select))
- end,
target_num = 0,
min_card_num = 1,
+ card_filter = function(self, to_select)
+ return not Self:prohibitDiscard(Fk:getCardById(to_select))
+ end,
on_use = function(self, room, effect)
local from = room:getPlayerById(effect.from)
room:throwCard(effect.cards, self.name, from, from)
@@ -712,7 +705,7 @@ local jiuyuan = fk.CreateTriggerSkill{
can_trigger = function(self, event, target, player, data)
return
target == player and
- player:hasSkill(self.name) and
+ player:hasSkill(self) and
data.card and
data.card.trueName == "peach" and
data.recoverBy and
@@ -754,7 +747,7 @@ local keji = fk.CreateTriggerSkill{
anim_type = "defensive",
events = {fk.EventPhaseChanging},
can_trigger = function(self, event, target, player, data)
- if target == player and player:hasSkill(self.name) and data.to == Player.Discard then
+ if target == player and player:hasSkill(self) and data.to == Player.Discard then
local room = player.room
local logic = room.logic
local e = logic:getCurrentEvent():findParent(GameEvent.Turn, true)
@@ -866,7 +859,7 @@ local liuli = fk.CreateTriggerSkill{
anim_type = "defensive",
events = {fk.TargetConfirming},
can_trigger = function(self, event, target, player, data)
- local ret = target == player and player:hasSkill(self.name) and
+ local ret = target == player and player:hasSkill(self) and
data.card.trueName == "slash"
if ret then
local room = player.room
@@ -912,7 +905,7 @@ local qianxun = fk.CreateProhibitSkill{
name = "qianxun",
frequency = Skill.Compulsory,
is_prohibited = function(self, from, to, card)
- if to:hasSkill(self.name) then
+ if to:hasSkill(self) then
return card.name == "indulgence" or card.name == "snatch"
end
end,
@@ -922,7 +915,7 @@ local lianying = fk.CreateTriggerSkill{
anim_type = "drawcard",
events = {fk.AfterCardsMove},
can_trigger = function(self, event, target, player, data)
- if not player:hasSkill(self.name) then return end
+ if not player:hasSkill(self) then return end
if not player:isKongcheng() then return end
for _, move in ipairs(data) do
if move.from == player.id then
@@ -947,7 +940,7 @@ local xiaoji = fk.CreateTriggerSkill{
anim_type = "drawcard",
events = {fk.AfterCardsMove},
can_trigger = function(self, event, target, player, data)
- if not player:hasSkill(self.name) then return end
+ if not player:hasSkill(self) then return end
for _, move in ipairs(data) do
if move.from == player.id then
for _, info in ipairs(move.moveInfo) do
@@ -992,7 +985,7 @@ local jieyin = fk.CreateActiveSkill{
return player:usedSkillTimes(self.name, Player.HistoryPhase) == 0
end,
card_filter = function(self, to_select, selected)
- return #selected < 2 and Fk:currentRoom():getCardArea(to_select) == Player.Hand
+ return #selected < 2 and Fk:currentRoom():getCardArea(to_select) == Player.Hand and not Self:prohibitDiscard(Fk:getCardById(to_select))
end,
target_filter = function(self, to_select, selected)
local target = Fk:currentRoom():getPlayerById(to_select)
@@ -1090,7 +1083,7 @@ local wushuang = fk.CreateTriggerSkill{
frequency = Skill.Compulsory,
events = {fk.TargetSpecified, fk.TargetConfirmed},
can_trigger = function(self, event, target, player, data)
- if not player:hasSkill(self.name) then
+ if not player:hasSkill(self) then
return false
end
@@ -1150,7 +1143,7 @@ local biyue = fk.CreateTriggerSkill{
anim_type = "drawcard",
events = {fk.EventPhaseStart},
can_trigger = function(self, event, target, player, data)
- return target == player and player:hasSkill(self.name)
+ return target == player and player:hasSkill(self)
and player.phase == Player.Finish
end,
on_use = function(self, event, target, player, data)
@@ -1346,7 +1339,7 @@ local role_mode = fk.CreateGameMode{
}
extension:addGameMode(role_mode)
Fk:loadTranslationTable{
- ["time limitation: 5 sec"] = "游戏时长达到5秒(测试用)",
+ ["time limitation: 5 min"] = "游戏时长达到5分钟",
["left lord and loyalist alive"] = "仅剩你和主忠方存活",
["left one rebel alive"] = "反贼仅剩你存活且不存在存活内奸",
["left you alive"] = "主忠方仅剩你存活且其他阵营仅剩一方",
diff --git a/packages/standard_cards/i18n/en_US.lua b/packages/standard_cards/i18n/en_US.lua
index 0b8903af..b27709ed 100644
--- a/packages/standard_cards/i18n/en_US.lua
+++ b/packages/standard_cards/i18n/en_US.lua
@@ -28,7 +28,9 @@ Fk:loadTranslationTable({
-- ["weapon"] = "武器牌",
-- ["armor"] = "防具牌",
["defensive_horse"] = "+1 horse",
+ ["defensive_ride"] = "+1 horse",
["offensive_horse"] = "-1 horse",
+ ["offensive_ride"] = "-1 horse",
["equip_horse"] = "horse",
-- ["treasure"] = "宝物牌",
["delayed_trick"] = "delayed trick",
@@ -50,53 +52,67 @@ Fk:loadTranslationTable({
["slash"] = "Slash",
[":slash"] = "Slash (basic card)
Phase: Action phase
Target: Another player within your ATK range
Effect: Deal 1 DMG to the targets.
Note: You can only use 1 Slash per action phase.",
["#slash-jink"] = "%src used Slash to you, please use %arg Dodge(s)",
+ ["#slash_skill"] = "Choose 1 player within your ATK range, deal 1 DMG to him",
+ ["#slash_skill_multi"] = "Choose up to %arg players within your ATK range. Deal 1 DMG to them",
["jink"] = "Dodge",
[":jink"] = "Dodge (basic card)
Phase: When Slash is about to effect on you
Target: This Slash
Effect: Counter the target Slash.",
["peach"] = "Peach",
[":peach"] = "Peach (basic card)
Phase: 1. Action phase 2. When a player is dying
Target: Wounded yourself/the player who is dying
Effect: The target heals 1 HP.",
+ ["#peach_skill"] = "You heal 1 HP",
["dismantlement"] = "Dismantlement",
[":dismantlement"] = "Dismantlement (trick card)
Phase: Action phase
Target: Another player with cards in any area
Effect: Discard 1 card in one of the areas of the target player.",
["dismantlement_skill"] = "Dismantlement",
+ ["#dismantlement_skill"] = "Choose another player with cards in any area. Discard 1 card in one of his areas",
["snatch"] = "Snatch",
- [":snatch"] = "Snatch (trick card)
Phase: Action phase
Target: Another player at distance 1 with cards in any area
Effect: Take 1 card in one of the areas of the target player.",
+ [":snatch"] = "Snatch (trick card)
Phase: Action phase
Target: Another player at distance 1 with cards in any area
Effect: Take 1 card in one of the areas of the target player.",
["snatch_skill"] = "Snatch",
+ ["#snatch_skill"] = "Choose another player at distance 1 with cards in any area. Take 1 card in one of his areas",
["duel"] = "Duel",
[":duel"] = "Duel (trick card)
Phase: Action phase
Target: Another player
Effect: In turns (starting with the target player), both of you play Slash successively. The first player who doesn't play Slash suffers 1 DMG from the other player.",
+ ["#duel_skill"] = "Choose another player. In turns (starting with the target player), both of you play Slash successively.
The first player who doesn't play Slash suffers 1 DMG from the other player",
["collateral"] = "Collateral",
[":collateral"] = "Collateral (trick card)
Phase: Action phase
Target: Another player with an equipped weapon (Player A)
Sub-target: A player within Player A's ATK range (Player B)
Effect: Unless A uses Slash to B, he gives you his equipped weapon.",
- ["#collateral-slash"] = "Collateral: You shall use Slash to %dest , or %src takes your weapon",
+ ["#collateral-slash"] = "Collateral: You shall use Slash to %dest , or give your weapon to %src",
+ ["#collateral_skill"] = "Choose another player with an equipped weapon (Player A),
then choose another player within Player A's ATK range (Player B).
Unless A uses Slash to B, he gives you his equipped weapon",
["ex_nihilo"] = "Ex Nihilo",
[":ex_nihilo"] = "Ex Nihilo (trick card)
Phase: Action phase
Target: Yourself
Effect: The target draws 2 cards.",
+ ["#ex_nihilo_skill"] = "You draw 2 cards",
["nullification"] = "Nullification",
[":nullification"] = "Nullification (trick card)
Phase: When a trick card is about to take effect (including Nullification itself)
Target: This trick card
Effect: Counter the target trick card.",
["savage_assault"] = "Savage Assault",
[":savage_assault"] = "Savage Assault (trick card)
Phase: Action phase
Target: All other players
Effect: Each target player needs to play Slash, otherwise they suffer 1 DMG from you.",
+ ["#savage_assault_skill"] = "Each other players needs to play Slash, otherwise they suffer 1 DMG from you",
["archery_attack"] = "Archery Attack",
[":archery_attack"] = "Archery Attack (trick card)
Phase: Action phase
Target: All other players
Effect: Each target player needs to play Dodge, otherwise they suffer 1 DMG from you.",
+ ["#archery_attack_skill"] = "Each other players needs to play Dodge, otherwise they suffer 1 DMG from you",
["god_salvation"] = "God Salvation",
[":god_salvation"] = "God Salvation (trick card)
Phase: Action phase
Target: All players
Effect: Each target player heals 1 HP.",
+ ["#god_salvation_skill"] = "Each players heals 1 HP",
["amazing_grace"] = "Amazing Grace",
[":amazing_grace"] = "Amazing Grace (trick card)
Phase: Action phase
Target: All players
Effect: Reveal as many cards from the draw pile as target players; then, each target player takes 1 of those cards.",
["amazing_grace_skill"] = "AG",
["Please choose cards"] = "Please choose a card",
+ ["#amazing_grace_skill"] = "Reveal as many cards from the draw pile as all players;
then, each player takes 1 of those cards",
["lightning"] = "Lightning",
[":lightning"] = "Lightning (delayed trick card)
Phase: Action phase
Target: Yourself
Effect: Place this card in target's judgement area. He performs a judgement in his judge phase: If the judgement result is 2-9♠, he suffers 3 Thunder DMG, otherwise move Lightning to his next player's judgement area.",
+ ["#lightning_skill"] = "Place this card in your judgement area. Target player performs a judgement in his judge phase:
If the judgement result is 2-9♠, he suffers 3 Thunder DMG, otherwise move Lightning to his next player's judgement area",
["indulgence"] = "Indulgence",
- [":indulgence"] = "Indulgence (delayed trick card)
Phase: Action phase
Target: Another player
Effect: Place this card in target's judgement area. He performs a judgement in his judge phase: if result is not heart, he skips his action phase.",
+ [":indulgence"] = "Indulgence (delayed trick card)
Phase: Action phase
Target: Another player
Effect: Place this card in target's judgement area. He performs a judgement in his judge phase: if result is not ♥, he skips his action phase.",
+ ["#indulgence_skill"] = "Place this card in another player's judgement area. He performs a judgement in his judge phase:
If result is not ♥, he skips his action phase",
["crossbow"] = "Crossbow",
[":crossbow"] = "Crossbow (equip card, weapon)
ATK range: 1
Weapon skill: You can use any amount of Slash in your action phase.",
@@ -122,6 +138,7 @@ Fk:loadTranslationTable({
[":spear"] = "Spear (equip card, weapon)
ATK range: 3
Weapon skill: You can use/play 2 hand cards as Slash.",
["spear_skill"] = "Spear",
[":spear_skill"] = "You can use/play 2 hand cards as Slash.",
+ ["#spear_skill"] = "You can use/play 2 hand cards as Slash",
["axe"] = "Axe",
[":axe"] = "Axe (equip card, weapon)
ATK range: 3
Weapon skill: When your used Slash is countered by Dodge, you can discard 2 cards (except equipped Axe), then make this Slash still effective to the target.",
@@ -159,4 +176,6 @@ Fk:loadTranslationTable({
["zixing"] = "Zi Xing",
[":zixing"] = "Zi Xing (equip card, horse)
Horse skill: The distance from you to other players is reduced by -1.",
+
+ ["#default_equip_skill"] = "Equip %arg2 %arg",
}, "en_US")
diff --git a/packages/standard_cards/i18n/zh_CN.lua b/packages/standard_cards/i18n/zh_CN.lua
index 8bd9ec4d..c241beae 100644
--- a/packages/standard_cards/i18n/zh_CN.lua
+++ b/packages/standard_cards/i18n/zh_CN.lua
@@ -28,7 +28,9 @@ Fk:loadTranslationTable{
["weapon"] = "武器牌",
["armor"] = "防具牌",
["defensive_horse"] = "防御坐骑牌",
+ ["defensive_ride"] = "防御坐骑牌",
["offensive_horse"] = "进攻坐骑牌",
+ ["offensive_ride"] = "进攻坐骑牌",
["equip_horse"] = "坐骑牌",
["treasure"] = "宝物牌",
["delayed_trick"] = "延时类锦囊牌",
@@ -48,73 +50,87 @@ Fk:loadTranslationTable{
["method_discard"] = "弃置",
["slash"] = "杀",
- [":slash"] = "基本牌
时机:出牌阶段
目标:攻击范围内的一名其他角色
效果:对目标角色造成1点伤害。",
+ [":slash"] = "基本牌
时机:出牌阶段
目标:攻击范围内的一名角色
效果:对目标角色造成1点伤害。",
["#slash-jink"] = "%src 对你使用了杀,请使用 %arg 张闪",
+ ["#slash_skill"] = "选择攻击范围内的一名角色,对其造成1点伤害",
+ ["#slash_skill_multi"] = "选择攻击范围内的至多%arg名角色,对这些角色各造成1点伤害",
["jink"] = "闪",
[":jink"] = "基本牌
时机:【杀】对你生效时
目标:此【杀】
效果:抵消此【杀】的效果。",
["peach"] = "桃",
[":peach"] = "基本牌
时机:出牌阶段/一名角色处于濒死状态时
目标:已受伤的你/处于濒死状态的角色
效果:目标角色回复1点体力。",
+ ["#peach_skill"] = "你回复1点体力",
["dismantlement"] = "过河拆桥",
[":dismantlement"] = "锦囊牌
时机:出牌阶段
目标:一名区域内有牌的其他角色。
效果:你弃置目标角色区域内的一张牌。",
["dismantlement_skill"] = "过河拆桥",
+ ["#dismantlement_skill"] = "选择一名区域内有牌的其他角色,你弃置其区域内的一张牌",
["snatch"] = "顺手牵羊",
[":snatch"] = "锦囊牌
时机:出牌阶段
目标:距离1的一名区域内有牌的角色
效果:你获得目标角色区域内的一张牌。",
["snatch_skill"] = "顺手牵羊",
+ ["#snatch_skill"] = "选择距离1的区域内有牌的角色,你获得其区域内的一张牌",
["duel"] = "决斗",
- [":duel"] = "锦囊牌
时机:出牌阶段
目标:一名其他角色
效果:由目标角色开始,你与其轮流:打出一张【杀】,否则受到对方的1点伤害并结束此牌结算。",
+ [":duel"] = "锦囊牌
时机:出牌阶段
目标:一名其他角色
效果:由目标角色开始,其与你轮流打出一张【杀】,直到其与你中的一名角色未打出【杀】。未打出【杀】的角色受到其与你中的另一名角色造成的1点伤害。",
+ ["#duel_skill"] = "选择一名其他角色,由其开始,其与你轮流打出一张【杀】,直到其与你中的一名角色未打出【杀】。
未打出【杀】的角色受到其与你中的另一名角色造成的1点伤害",
["collateral"] = "借刀杀人",
- [":collateral"] = "锦囊牌
时机:出牌阶段
目标:装备区内有武器牌且攻击范围内有【杀】的合法目标的一名其他角色A(你需要选择一名A攻击范围内的【杀】的合法目标B)
效果:A须对B使用一张【杀】,否则你获得A装备区内的武器牌。",
- ["#collateral-slash"] = "借刀杀人:你需对 %dest 使用【杀】,否则 %src 获得你的武器",
+ [":collateral"] = "锦囊牌
时机:出牌阶段
目标:装备区里有武器牌且攻击范围内有【杀】的合法目标的一名其他角色A(你需要选择一名A攻击范围内的【杀】的合法目标B)
效果:A需对B使用一张【杀】,否则将装备区里的武器牌交给你。",
+ ["#collateral-slash"] = "借刀杀人:你需对 %dest 使用【杀】,否则将你的装备区里的武器牌交给 %src",
+ ["#collateral_skill"] = "选择装备区里有武器牌且攻击范围内有【杀】的合法目标的一名其他角色A,
再选择一名A攻击范围内的【杀】的合法目标B。A需对B使用【杀】,否则将装备区里的武器牌交给你",
["ex_nihilo"] = "无中生有",
[":ex_nihilo"] = "锦囊牌
时机:出牌阶段
目标:你
效果:目标角色摸两张牌。",
+ ["#ex_nihilo_skill"] = "你摸两张牌",
["nullification"] = "无懈可击",
[":nullification"] = "锦囊牌
时机:锦囊牌对目标角色生效前,或一张【无懈可击】生效前
目标:该锦囊牌
效果:抵消该锦囊牌对该角色产生的效果,或抵消另一张【无懈可击】产生的效果。",
["savage_assault"] = "南蛮入侵",
- [":savage_assault"] = "锦囊牌
时机:出牌阶段
目标:所有其他角色
效果:每名目标角色须打出一张【杀】,否则受到1点伤害。",
+ [":savage_assault"] = "锦囊牌
时机:出牌阶段
目标:所有其他角色
效果:每名目标角色需打出一张【杀】,否则受到1点伤害。",
+ ["#savage_assault_skill"] = "所有其他角色需打出一张【杀】,否则受到1点伤害",
["archery_attack"] = "万箭齐发",
- [":archery_attack"] = "锦囊牌
时机:出牌阶段
目标:所有其他角色
效果:每名目标角色须打出一张【闪】,否则受到1点伤害。",
+ [":archery_attack"] = "锦囊牌
时机:出牌阶段
目标:所有其他角色
效果:每名目标角色需打出一张【闪】,否则受到1点伤害。",
+ ["#archery_attack_skill"] = "所有其他角色需打出一张【闪】,否则受到1点伤害",
["god_salvation"] = "桃园结义",
[":god_salvation"] = "锦囊牌
时机:出牌阶段
目标:所有角色
效果:每名目标角色回复1点体力。",
+ ["#god_salvation_skill"] = "所有角色回复1点体力",
["amazing_grace"] = "五谷丰登",
- [":amazing_grace"] = "锦囊牌
时机:出牌阶段
目标:所有角色
效果:你亮出牌堆顶等于角色数的牌,每名目标角色获得其中一张牌,然后将其余的牌置入弃牌堆。",
+ [":amazing_grace"] = "锦囊牌
时机:出牌阶段
目标:所有角色
效果:亮出牌堆顶等于目标角色数的牌,每名目标角色获得其中一张牌,结算结束后将其余的牌置入弃牌堆。",
["amazing_grace_skill"] = "五谷选牌",
["Please choose cards"] = "请选择一张卡牌",
+ ["#amazing_grace_skill"] = "亮出牌堆顶等于所有角色数的牌,每名角色获得其中一张牌",
["lightning"] = "闪电",
- [":lightning"] = "延时锦囊牌
时机:出牌阶段
目标:你
效果:将此牌置于目标角色判定区内。其判定阶段进行判定:若结果为黑桃2-9,其受到3点雷电伤害并将【闪电】置入弃牌堆,否则将【闪电】移动至其下家判定区内。",
+ [":lightning"] = "延时锦囊牌
时机:出牌阶段
目标:你
效果:将此牌置于目标角色判定区内。其判定阶段进行判定:若结果为♠2-9,其受到3点雷电伤害并将【闪电】置入弃牌堆,否则将【闪电】移动至其下家判定区内。",
+ ["#lightning_skill"] = "将此牌置于你的判定区内。目标角色判定阶段判定:
若结果为♠2-9,其受到3点雷电伤害并将【闪电】置入弃牌堆,否则将【闪电】移动至其下家判定区内",
["indulgence"] = "乐不思蜀",
- [":indulgence"] = "延时锦囊牌
时机:出牌阶段
目标:一名其他角色
效果:将此牌置于目标角色判定区内。其判定阶段进行判定:若结果不为红桃,其跳过出牌阶段。然后将【乐不思蜀】置入弃牌堆。",
+ [":indulgence"] = "延时锦囊牌
时机:出牌阶段
目标:一名其他角色
效果:将此牌置于目标角色判定区内。其判定阶段进行判定:若结果不为♥,其跳过出牌阶段。然后将【乐不思蜀】置入弃牌堆。",
+ ["#indulgence_skill"] = "选择一名其他角色,将此牌置于其判定区内。其判定阶段判定:
若结果不为♥,其跳过出牌阶段",
["crossbow"] = "诸葛连弩",
- [":crossbow"] = "装备牌·武器
攻击范围:1
武器技能:锁定技。你于出牌阶段内使用【杀】无次数限制。",
+ [":crossbow"] = "装备牌·武器
攻击范围:1
武器技能:锁定技,你于出牌阶段内使用【杀】无次数限制。",
["qinggang_sword"] = "青釭剑",
- [":qinggang_sword"] = "装备牌·武器
攻击范围:2
武器技能:锁定技。你的【杀】无视目标角色的防具。",
+ [":qinggang_sword"] = "装备牌·武器
攻击范围:2
武器技能:锁定技,你的【杀】无视目标角色的防具。",
["ice_sword"] = "寒冰剑",
- [":ice_sword"] = "装备牌·武器
攻击范围:2
武器技能:每当你使用【杀】对目标角色造成伤害时,若该角色有牌,你可以防止此伤害,然后依次弃置其两张牌。",
+ [":ice_sword"] = "装备牌·武器
攻击范围:2
武器技能:当你使用【杀】对目标角色造成伤害时,若该角色有牌,你可以防止此伤害,然后依次弃置其两张牌。",
["#ice_sword_skill"] = "寒冰剑",
["double_swords"] = "雌雄双股剑",
- [":double_swords"] = "装备牌·武器
攻击范围:2
武器技能:每当你指定异性角色为【杀】的目标后,你可以令其选择一项:弃置一张手牌,或令你摸一张牌。",
+ [":double_swords"] = "装备牌·武器
攻击范围:2
武器技能:当你指定异性角色为【杀】的目标后,你可以令其选择一项:弃置一张手牌,或令你摸一张牌。",
["#double_swords_skill"] = "雌雄双股剑",
["#double_swords-invoke"] = "雌雄双股剑:你需弃置一张手牌,否则 %src 摸一张牌",
["blade"] = "青龙偃月刀",
- [":blade"] = "装备牌·武器
攻击范围:3
武器技能:每当你使用的【杀】被【闪】抵消后,你可以对该角色再使用一张【杀】(无距离限制且不能选择额外目标)。",
+ [":blade"] = "装备牌·武器
攻击范围:3
武器技能:当你使用的【杀】被【闪】抵消后,你可以对该角色再使用一张【杀】(无距离限制且不能选择额外目标)。",
["#blade_skill"] = "青龙偃月刀",
["#blade_slash"] = "青龙偃月刀:你可以对 %src 再使用一张【杀】",
@@ -122,21 +138,22 @@ Fk:loadTranslationTable{
[":spear"] = "装备牌·武器
攻击范围:3
武器技能:你可以将两张手牌当【杀】使用或打出。",
["spear_skill"] = "丈八矛",
[":spear_skill"] = "你可以将两张手牌当【杀】使用或打出。",
+ ["#spear_skill"] = "你可以将两张手牌当【杀】使用或打出",
["axe"] = "贯石斧",
- [":axe"] = "装备牌·武器
攻击范围:3
武器技能:每当你使用的【杀】被【闪】抵消后,你可以弃置两张牌,则此【杀】继续造成伤害。",
+ [":axe"] = "装备牌·武器
攻击范围:3
武器技能:当你使用的【杀】被【闪】抵消后,你可以弃置两张牌,则此【杀】继续造成伤害。",
["#axe_skill"] = "贯石斧",
["#axe-invoke"] = "贯石斧:你可以弃置两张牌,令你对 %dest 使用的【杀】依然生效",
["halberd"] = "方天画戟",
- [":halberd"] = "装备牌·武器
攻击范围:4
武器技能:锁定技。你使用最后的手牌【杀】可以额外选择至多两名目标。",
+ [":halberd"] = "装备牌·武器
攻击范围:4
武器技能:锁定技,你使用最后的手牌【杀】可以额外选择至多两名目标。",
["kylin_bow"] = "麒麟弓",
- [":kylin_bow"] = "装备牌·武器
攻击范围:5
武器技能:每当你使用【杀】对目标角色造成伤害时,你可以弃置其装备区内的一张坐骑牌。",
+ [":kylin_bow"] = "装备牌·武器
攻击范围:5
武器技能:当你使用【杀】对目标角色造成伤害时,你可以弃置其装备区里的一张坐骑牌。",
["#kylin_bow_skill"] = "麒麟弓",
["eight_diagram"] = "八卦阵",
- [":eight_diagram"] = "装备牌·防具
防具技能:每当你需要使用或打出一张【闪】时,你可以进行判定:若结果为红色,视为你使用或打出了一张【闪】。",
+ [":eight_diagram"] = "装备牌·防具
防具技能:当你需要使用或打出一张【闪】时,你可以进行判定:若结果为红色,视为你使用或打出了一张【闪】。",
["#eight_diagram_skill"] = "八卦阵",
["nioh_shield"] = "仁王盾",
@@ -159,4 +176,6 @@ Fk:loadTranslationTable{
["zixing"] = "紫骍",
[":zixing"] = "装备牌·坐骑
坐骑技能:你与其他角色的距离-1。",
+
+ ["#default_equip_skill"] = "装备%arg2【%arg】",
}
diff --git a/packages/standard_cards/init.lua b/packages/standard_cards/init.lua
index 5c72d1fc..26b674b0 100644
--- a/packages/standard_cards/init.lua
+++ b/packages/standard_cards/init.lua
@@ -5,6 +5,19 @@ extension.metadata = require "packages.standard_cards.metadata"
local slashSkill = fk.CreateActiveSkill{
name = "slash_skill",
+ prompt = function(self, selected_cards)
+ local slash = Fk:cloneCard("slash")
+ slash.subcards = Card:getIdList(selected_cards)
+ local max_num = self:getMaxTargetNum(Self, slash) -- halberd
+ if max_num > 1 then
+ local num = #table.filter(Fk:currentRoom().alive_players, function (p)
+ return p ~= Self and not Self:isProhibited(p, slash)
+ end)
+ max_num = math.min(num, max_num)
+ end
+ slash.subcards = {}
+ return max_num > 1 and "#slash_skill_multi:::" .. max_num or "#slash_skill"
+ end,
max_phase_use_time = 1,
target_num = 1,
can_use = function(self, player, card)
@@ -86,9 +99,7 @@ extension:addCards({
local jinkSkill = fk.CreateActiveSkill{
name = "jink_skill",
- can_use = function()
- return false
- end,
+ can_use = Util.FalseFunc,
on_effect = function(self, room, effect)
if effect.responseToEvent then
effect.responseToEvent.isCancellOut = true
@@ -123,6 +134,7 @@ extension:addCards({
local peachSkill = fk.CreateActiveSkill{
name = "peach_skill",
+ prompt = "#peach_skill",
mod_target_filter = function(self, to_select)
return Fk:currentRoom():getPlayerById(to_select):isWounded() and
not table.find(Fk:currentRoom().alive_players, function(p)
@@ -171,6 +183,7 @@ extension:addCards({
local dismantlementSkill = fk.CreateActiveSkill{
name = "dismantlement_skill",
+ prompt = "#dismantlement_skill",
target_num = 1,
mod_target_filter = function(self, to_select, selected, user, card)
local player = Fk:currentRoom():getPlayerById(to_select)
@@ -209,6 +222,7 @@ extension:addCards({
local snatchSkill = fk.CreateActiveSkill{
name = "snatch_skill",
+ prompt = "#snatch_skill",
distance_limit = 1,
mod_target_filter = function(self, to_select, selected, user, card, distance_limited)
local player = Fk:currentRoom():getPlayerById(to_select)
@@ -247,6 +261,7 @@ extension:addCards({
local duelSkill = fk.CreateActiveSkill{
name = "duel_skill",
+ prompt = "#duel_skill",
mod_target_filter = function(self, to_select, selected, user, card)
return user ~= to_select
end,
@@ -332,6 +347,7 @@ extension:addCards({
local collateralSkill = fk.CreateActiveSkill{
name = "collateral_skill",
+ prompt = "#collateral_skill",
mod_target_filter = function(self, to_select, selected, user, card, distance_limited)
local player = Fk:currentRoom():getPlayerById(to_select)
return user ~= to_select and player:getEquipment(Card.SubtypeWeapon)
@@ -374,9 +390,7 @@ local collateralSkill = fk.CreateActiveSkill{
use.extraUse = true
room:useCard(use)
else
- room:obtainCard(effect.from,
- room:getPlayerById(effect.to):getEquipment(Card.SubtypeWeapon),
- true, fk.ReasonGive)
+ room:moveCardTo(to:getEquipment(Card.SubtypeWeapon), Card.PlayerHand, room:getPlayerById(effect.from), fk.ReasonGive, "collateral", nil, true, to.id)
end
end
}
@@ -394,9 +408,8 @@ extension:addCards({
local exNihiloSkill = fk.CreateActiveSkill{
name = "ex_nihilo_skill",
- mod_target_filter = function(self, to_select, selected, user, card, distance_limited)
- return true
- end,
+ prompt = "#ex_nihilo_skill",
+ mod_target_filter = Util.TrueFunc,
can_use = function(self, player, card)
return not player:isProhibited(player, card)
end,
@@ -427,9 +440,7 @@ extension:addCards({
local nullificationSkill = fk.CreateActiveSkill{
name = "nullification_skill",
- can_use = function()
- return false
- end,
+ can_use = Util.FalseFunc,
on_use = function() RoomInstance:delay(1200) end,
on_effect = function(self, room, effect)
if effect.responseToEvent then
@@ -455,6 +466,7 @@ extension:addCards({
local savageAssaultSkill = fk.CreateActiveSkill{
name = "savage_assault_skill",
+ prompt = "#savage_assault_skill",
can_use = Util.AoeCanUse,
on_use = Util.AoeOnUse,
mod_target_filter = function(self, to_select, selected, user, card, distance_limited)
@@ -498,6 +510,7 @@ extension:addCards({
local archeryAttackSkill = fk.CreateActiveSkill{
name = "archery_attack_skill",
+ prompt = "#archery_attack_skill",
can_use = Util.AoeCanUse,
on_use = Util.AoeOnUse,
mod_target_filter = function(self, to_select, selected, user, card, distance_limited)
@@ -539,11 +552,10 @@ extension:addCards({
local godSalvationSkill = fk.CreateActiveSkill{
name = "god_salvation_skill",
+ prompt = "#god_salvation_skill",
can_use = Util.GlobalCanUse,
on_use = Util.GlobalOnUse,
- mod_target_filter = function(self, to_select, selected, user, card, distance_limited)
- return true
- end,
+ mod_target_filter = Util.TrueFunc,
about_to_effect = function(self, room, effect)
if not room:getPlayerById(effect.to):isWounded() then
return true
@@ -577,11 +589,10 @@ extension:addCards({
local amazingGraceSkill = fk.CreateActiveSkill{
name = "amazing_grace_skill",
+ prompt = "#amazing_grace_skill",
can_use = Util.GlobalCanUse,
on_use = Util.GlobalOnUse,
- mod_target_filter = function(self, to_select, selected, user, card, distance_limited)
- return true
- end,
+ mod_target_filter = Util.TrueFunc,
on_effect = function(self, room, effect)
local to = room:getPlayerById(effect.to)
if not (effect.extra_data and effect.extra_data.AGFilled) then
@@ -664,9 +675,8 @@ extension:addCards({
local lightningSkill = fk.CreateActiveSkill{
name = "lightning_skill",
- mod_target_filter = function(self, to_select, selected, user, card, distance_limited)
- return true
- end,
+ prompt = "#lightning_skill",
+ mod_target_filter = Util.TrueFunc,
can_use = function(self, player, card)
return not player:isProhibited(player, card)
end,
@@ -737,6 +747,7 @@ extension:addCards({
local indulgenceSkill = fk.CreateActiveSkill{
name = "indulgence_skill",
+ prompt = "#indulgence_skill",
mod_target_filter = function(self, to_select, selected, user, card, distance_limited)
return user ~= to_select
end,
@@ -783,20 +794,25 @@ local crossbowAudio = fk.CreateTriggerSkill{
name = "#crossbowAudio",
refresh_events = {fk.CardUsing},
can_refresh = function(self, event, target, player, data)
- return target == player and player:hasSkill(self.name) and player.phase == Player.Play and
+ return target == player and player:hasSkill(self) and player.phase == Player.Play and
data.card.trueName == "slash" and player:usedCardTimes("slash", Player.HistoryPhase) > 1
end,
on_refresh = function(self, event, target, player, data)
local room = player.room
room:broadcastPlaySound("./packages/standard_cards/audio/card/crossbow")
room:setEmotion(player, "./packages/standard_cards/image/anim/crossbow")
+ room:sendLog{
+ type = "#InvokeSkill",
+ from = player.id,
+ arg = "crossbow",
+ }
end,
}
local crossbowSkill = fk.CreateTargetModSkill{
name = "#crossbow_skill",
attached_equip = "crossbow",
bypass_times = function(self, player, skill, scope)
- if player:hasSkill(self.name) and skill.trueName == "slash_skill"
+ if player:hasSkill(self) and skill.trueName == "slash_skill"
and scope == Player.HistoryPhase then
return true
end
@@ -841,7 +857,7 @@ local qingGangSkill = fk.CreateTriggerSkill{
frequency = Skill.Compulsory,
events = { fk.TargetSpecified },
can_trigger = function(self, event, target, player, data)
- return target == player and player:hasSkill(self.name) and
+ return target == player and player:hasSkill(self) and
data.card and data.card.trueName == "slash"
end,
on_use = function(self, event, target, player, data)
@@ -874,7 +890,7 @@ local iceSwordSkill = fk.CreateTriggerSkill{
attached_equip = "ice_sword",
events = {fk.DamageCaused},
can_trigger = function(self, event, target, player, data)
- return target == player and player:hasSkill(self.name) and (not data.chain) and
+ return target == player and player:hasSkill(self) and (not data.chain) and
data.card and data.card.trueName == "slash" and not data.to:isNude()
end,
on_use = function(self, event, target, player, data)
@@ -907,11 +923,10 @@ local doubleSwordsSkill = fk.CreateTriggerSkill{
attached_equip = "double_swords",
events = {fk.TargetSpecified},
can_trigger = function(self, event, target, player, data)
- if target == player and player:hasSkill(self.name) and
+ if target == player and player:hasSkill(self) and
data.card and data.card.trueName == "slash" then
- local to = player.room:getPlayerById(data.to)
- if player.gender == General.Agender or to.gender == General.Agender then return false end
- return to.gender ~= player.gender or player.gender == General.Bigender or to.gender == General.Bigender
+ local target = player.room:getPlayerById(data.to)
+ return player:compareGenderWith(target, true)
end
end,
on_use = function(self, event, target, player, data)
@@ -945,7 +960,7 @@ local bladeSkill = fk.CreateTriggerSkill{
attached_equip = "blade",
events = {fk.CardEffectCancelledOut},
can_trigger = function(self, event, target, player, data)
- return player:hasSkill(self.name) and data.from == player.id and data.card.trueName == "slash" and not player.room:getPlayerById(data.to).dead
+ return player:hasSkill(self) and data.from == player.id and data.card.trueName == "slash" and not player.room:getPlayerById(data.to).dead
end,
on_cost = function(self, event, target, player, data)
local room = player.room
@@ -976,6 +991,7 @@ extension:addCards({
local spearSkill = fk.CreateViewAsSkill{
name = "spear_skill",
+ prompt = "#spear_skill",
attached_equip = "spear",
pattern = "slash",
card_filter = function(self, to_select, selected)
@@ -1010,7 +1026,7 @@ local axeSkill = fk.CreateTriggerSkill{
attached_equip = "axe",
events = {fk.CardEffectCancelledOut},
can_trigger = function(self, event, target, player, data)
- return player:hasSkill(self.name) and data.from == player.id and data.card.trueName == "slash" and not player.room:getPlayerById(data.to).dead
+ return player:hasSkill(self) and data.from == player.id and data.card.trueName == "slash" and not player.room:getPlayerById(data.to).dead
end,
on_cost = function(self, event, target, player, data)
local room = player.room
@@ -1049,7 +1065,7 @@ local halberdAudio = fk.CreateTriggerSkill{
name = "#halberdAudio",
refresh_events = {fk.CardUsing},
can_refresh = function(self, event, target, player, data)
- return target == player and player:hasSkill(self.name) and
+ return target == player and player:hasSkill(self) and
data.card.trueName == "slash" and #TargetGroup:getRealTargets(data.tos) > 1
end,
on_refresh = function(self, event, target, player, data)
@@ -1062,7 +1078,7 @@ local halberdSkill = fk.CreateTargetModSkill{
name = "#halberd_skill",
attached_equip = "halberd",
extra_target_func = function(self, player, skill, card)
- if player:hasSkill(self.name) and skill.trueName == "slash_skill" then
+ if player:hasSkill(self) and skill.trueName == "slash_skill" then
local cards = card:isVirtual() and card.subcards or {card.id}
local handcards = player:getCardIds(Player.Hand)
if #cards == #handcards and table.every(cards, function(id) return table.contains(handcards, id) end) then
@@ -1090,7 +1106,7 @@ local kylinBowSkill = fk.CreateTriggerSkill{
attached_equip = "kylin_bow",
events = {fk.DamageCaused},
can_trigger = function(self, event, target, player, data)
- local ret = target == player and player:hasSkill(self.name) and
+ local ret = target == player and player:hasSkill(self) and
data.card and data.card.trueName == "slash" and (not data.chain)
if ret then
---@type ServerPlayer
@@ -1135,7 +1151,7 @@ local eightDiagramSkill = fk.CreateTriggerSkill{
attached_equip = "eight_diagram",
events = {fk.AskForCardUse, fk.AskForCardResponse},
can_trigger = function(self, event, target, player, data)
- return target == player and player:hasSkill(self.name) and
+ return target == player and player:hasSkill(self) and
(data.cardName == "jink" or (data.pattern and Exppattern:Parse(data.pattern):matchExp("jink|0|nosuit|none")))
end,
on_use = function(self, event, target, player, data)
@@ -1188,10 +1204,10 @@ local niohShieldSkill = fk.CreateTriggerSkill{
events = {fk.PreCardEffect},
can_trigger = function(self, event, target, player, data)
local effect = data ---@type CardEffectEvent
- return player.id == effect.to and player:hasSkill(self.name) and
+ return player.id == effect.to and player:hasSkill(self) and
effect.card.trueName == "slash" and effect.card.color == Card.Black
end,
- on_use = function() return true end,
+ on_use = Util.TrueFunc,
}
Fk:addSkill(niohShieldSkill)
local niohShield = fk.CreateArmor{
diff --git a/packages/test/init.lua b/packages/test/init.lua
index f1e81e1a..01a19ed8 100644
--- a/packages/test/init.lua
+++ b/packages/test/init.lua
@@ -7,12 +7,8 @@ local cheat = fk.CreateActiveSkill{
name = "cheat",
anim_type = "drawcard",
prompt = "#cheat",
- can_use = function(self, player)
- return true
- end,
- card_filter = function(self, card)
- return false
- end,
+ can_use = Util.TrueFunc,
+ card_filter = Util.FalseFunc,
target_num = 0,
on_use = function(self, room, effect)
local from = room:getPlayerById(effect.from)
@@ -64,16 +60,8 @@ local test_filter = fk.CreateFilterSkill{
local control = fk.CreateActiveSkill{
name = "control",
anim_type = "control",
- can_use = function(self, player)
- return true
- end,
- card_filter = function(self, card)
- -- if self.interaction.data == "joy" then
- --local c = Fk:getCardById(card)
- --return Self:getPileNameOfId(card) == self.name and c.color == Card.Red
- return false
- -- end
- end,
+ can_use = Util.TrueFunc,
+ card_filter = Util.FalseFunc,
card_num = 0,
target_filter = function(self, to_select)
return to_select ~= Self.id
@@ -225,12 +213,8 @@ local damage_maker = fk.CreateActiveSkill{
name = "damage_maker",
anim_type = "offensive",
prompt = "#damage_maker",
- can_use = function(self, player)
- return true
- end,
- card_filter = function(self, card)
- return false
- end,
+ can_use = Util.TrueFunc,
+ card_filter = Util.FalseFunc,
card_num = 0,
target_filter = function(self, to_select, selected)
if self.interaction.data == "revive" then return false end
@@ -300,12 +284,8 @@ local damage_maker = fk.CreateActiveSkill{
}
local change_hero = fk.CreateActiveSkill{
name = "change_hero",
- can_use = function(self, player)
- return true
- end,
- card_filter = function(self, card)
- return false
- end,
+ can_use = Util.TrueFunc,
+ card_filter = Util.FalseFunc,
card_num = 0,
target_filter = function(self, to_select, selected)
return #selected < 1
@@ -344,12 +324,8 @@ local test_zhenggong = fk.CreateTriggerSkill{
}
local test_feichu = fk.CreateActiveSkill{
name = "test_feichu",
- can_use = function(self, player)
- return true
- end,
- card_filter = function(self, card)
- return false
- end,
+ can_use = Util.TrueFunc,
+ card_filter = Util.FalseFunc,
card_num = 0,
target_filter = function(self, to_select, selected)
return #selected < 1