New cards (#84)

火攻
铁锁重铸
This commit is contained in:
notify 2023-03-20 20:15:24 +08:00 committed by GitHub
parent 9ec500f82c
commit 16b6800811
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 179 additions and 21 deletions

View File

@ -466,6 +466,19 @@ fk.client_callback["MoveCards"] = function(jsonData)
ClientInstance:notifyUI("MoveCards", json.encode(merged)) ClientInstance:notifyUI("MoveCards", json.encode(merged))
end end
fk.client_callback["ShowCard"] = function(jsonData)
local data = json.decode(jsonData)
local from = data.from
local cards = data.cards
ClientInstance:notifyUI("MoveCards", json.encode{
{
ids = cards,
fromArea = Card.DrawPile,
toArea = Card.Processing,
}
})
end
fk.client_callback["LoseSkill"] = function(jsonData) fk.client_callback["LoseSkill"] = function(jsonData)
-- jsonData: [ int player_id, string skill_name ] -- jsonData: [ int player_id, string skill_name ]
local data = json.decode(jsonData) local data = json.decode(jsonData)

View File

@ -115,6 +115,10 @@ function GetCards(pack_name)
return json.encode(ret) return json.encode(ret)
end end
function GetCardSpecialSkills(cid)
return json.encode(Fk:getCardById(cid).special_skills or {})
end
function DistanceTo(from, to) function DistanceTo(from, to)
local a = ClientInstance:getPlayerById(from) local a = ClientInstance:getPlayerById(from)
local b = ClientInstance:getPlayerById(to) local b = ClientInstance:getPlayerById(to)

View File

@ -127,6 +127,7 @@ FreeKill使用的是libgit2的C API与此同时使用Git完成拓展包的下
["PlayCard"] = "出牌", ["PlayCard"] = "出牌",
["AskForCardChosen"] = "选牌", ["AskForCardChosen"] = "选牌",
["AskForCardsChosen"] = "选牌",
["#AskForChooseCard"] = "%1请选择其一张卡牌", ["#AskForChooseCard"] = "%1请选择其一张卡牌",
["$ChooseCard"] = "请选择一张卡牌", ["$ChooseCard"] = "请选择一张卡牌",
["$Hand"] = "手牌区", ["$Hand"] = "手牌区",
@ -202,6 +203,8 @@ Fk:loadTranslationTable{
["$InstallEquip"] = "%from 装备了 %card", ["$InstallEquip"] = "%from 装备了 %card",
["$UninstallEquip"] = "%from 卸载了 %card", ["$UninstallEquip"] = "%from 卸载了 %card",
["#ShowCard"] = "%from 展示了牌 %card",
-- phase -- phase
["#PhaseSkipped"] = "%from 跳过了 %arg", ["#PhaseSkipped"] = "%from 跳过了 %arg",
["#GainAnExtraTurn"] = "%from 开始进行一个额外的回合", ["#GainAnExtraTurn"] = "%from 开始进行一个额外的回合",

View File

@ -11,6 +11,8 @@
---@field area CardArea ---@field area CardArea
---@field subcards integer[] ---@field subcards integer[]
---@field skillName string @ for virtual cards ---@field skillName string @ for virtual cards
---@field skill Skill
---@field special_skills string[] | nil
local Card = class("Card") local Card = class("Card")
---@alias Suit integer ---@alias Suit integer
@ -88,6 +90,7 @@ end
function Card:clone(suit, number) function Card:clone(suit, number)
local newCard = self.class:new(self.name, suit, number) local newCard = self.class:new(self.name, suit, number)
newCard.skill = self.skill newCard.skill = self.skill
newCard.special_skills = self.special_skills
newCard.equip_skill = self.equip_skill newCard.equip_skill = self.equip_skill
return newCard return newCard
end end

View File

@ -344,6 +344,7 @@ function fk.CreateBasicCard(spec)
local card = BasicCard:new(spec.name, spec.suit, spec.number) local card = BasicCard:new(spec.name, spec.suit, spec.number)
card.skill = spec.skill or defaultCardSkill card.skill = spec.skill or defaultCardSkill
card.special_skills = spec.special_skills
return card return card
end end
@ -358,6 +359,7 @@ function fk.CreateTrickCard(spec)
local card = TrickCard:new(spec.name, spec.suit, spec.number) local card = TrickCard:new(spec.name, spec.suit, spec.number)
card.skill = spec.skill or defaultCardSkill card.skill = spec.skill or defaultCardSkill
card.special_skills = spec.special_skills
return card return card
end end
@ -372,6 +374,7 @@ function fk.CreateDelayedTrickCard(spec)
local card = DelayedTrickCard:new(spec.name, spec.suit, spec.number) local card = DelayedTrickCard:new(spec.name, spec.suit, spec.number)
card.skill = spec.skill or defaultCardSkill card.skill = spec.skill or defaultCardSkill
card.special_skills = spec.special_skills
return card return card
end end
@ -387,6 +390,7 @@ function fk.CreateWeapon(spec)
local card = Weapon:new(spec.name, spec.suit, spec.number, spec.attack_range) local card = Weapon:new(spec.name, spec.suit, spec.number, spec.attack_range)
card.skill = spec.skill or defaultCardSkill card.skill = spec.skill or defaultCardSkill
card.special_skills = spec.special_skills
card.equip_skill = spec.equip_skill card.equip_skill = spec.equip_skill
if spec.on_install then card.onInstall = spec.on_install end if spec.on_install then card.onInstall = spec.on_install end
@ -406,6 +410,7 @@ function fk.CreateArmor(spec)
local card = Armor:new(spec.name, spec.suit, spec.number) local card = Armor:new(spec.name, spec.suit, spec.number)
card.skill = spec.skill or defaultCardSkill card.skill = spec.skill or defaultCardSkill
card.equip_skill = spec.equip_skill card.equip_skill = spec.equip_skill
card.special_skills = spec.special_skills
if spec.on_install then card.onInstall = spec.on_install end if spec.on_install then card.onInstall = spec.on_install end
if spec.on_uninstall then card.onUninstall = spec.on_uninstall end if spec.on_uninstall then card.onUninstall = spec.on_uninstall end
@ -423,6 +428,7 @@ function fk.CreateDefensiveRide(spec)
local card = DefensiveRide:new(spec.name, spec.suit, spec.number) local card = DefensiveRide:new(spec.name, spec.suit, spec.number)
card.skill = spec.skill or defaultCardSkill card.skill = spec.skill or defaultCardSkill
card.special_skills = spec.special_skills
card.equip_skill = spec.equip_skill card.equip_skill = spec.equip_skill
if spec.on_install then card.onInstall = spec.on_install end if spec.on_install then card.onInstall = spec.on_install end
@ -441,6 +447,7 @@ function fk.CreateOffensiveRide(spec)
local card = OffensiveRide:new(spec.name, spec.suit, spec.number) local card = OffensiveRide:new(spec.name, spec.suit, spec.number)
card.skill = spec.skill or defaultCardSkill card.skill = spec.skill or defaultCardSkill
card.special_skills = spec.special_skills
card.equip_skill = spec.equip_skill card.equip_skill = spec.equip_skill
if spec.on_install then card.onInstall = spec.on_install end if spec.on_install then card.onInstall = spec.on_install end
@ -459,6 +466,7 @@ function fk.CreateTreasure(spec)
local card = Treasure:new(spec.name, spec.suit, spec.number) local card = Treasure:new(spec.name, spec.suit, spec.number)
card.skill = spec.skill or defaultCardSkill card.skill = spec.skill or defaultCardSkill
card.special_skills = spec.special_skills
card.equip_skill = spec.equip_skill card.equip_skill = spec.equip_skill
if spec.on_install then card.onInstall = spec.on_install end if spec.on_install then card.onInstall = spec.on_install end

View File

@ -750,18 +750,21 @@ end
---@param maxNum integer ---@param maxNum integer
---@param includeEquip boolean ---@param includeEquip boolean
---@param skillName string ---@param skillName string
function Room:askForDiscard(player, minNum, maxNum, includeEquip, skillName, cancelable) ---@param pattern string
function Room:askForDiscard(player, minNum, maxNum, includeEquip, skillName, cancelable, pattern)
if minNum < 1 then if minNum < 1 then
return nil return nil
end end
cancelable = cancelable or false cancelable = cancelable or false
pattern = pattern or ""
local toDiscard = {} local toDiscard = {}
local data = { local data = {
num = maxNum, num = maxNum,
min_num = minNum, min_num = minNum,
include_equip = includeEquip, include_equip = includeEquip,
reason = skillName reason = skillName,
pattern = pattern,
} }
local prompt = "#AskForDiscard:::" .. maxNum .. ":" .. minNum local prompt = "#AskForDiscard:::" .. maxNum .. ":" .. minNum
local _, ret = self:askForUseActiveSkill(player, "discard_skill", prompt, cancelable, data) local _, ret = self:askForUseActiveSkill(player, "discard_skill", prompt, cancelable, data)
@ -817,18 +820,21 @@ end
---@param includeEquip boolean ---@param includeEquip boolean
---@param skillName string ---@param skillName string
---@param cancelable boolean ---@param cancelable boolean
function Room:askForCard(player, minNum, maxNum, includeEquip, skillName, cancelable) ---@param pattern string
function Room:askForCard(player, minNum, maxNum, includeEquip, skillName, cancelable, pattern)
if minNum < 1 then if minNum < 1 then
return nil return nil
end end
cancelable = cancelable or false cancelable = cancelable or false
pattern = pattern or ""
local chosenCards = {} local chosenCards = {}
local data = { local data = {
num = maxNum, num = maxNum,
min_num = minNum, min_num = minNum,
include_equip = includeEquip, include_equip = includeEquip,
reason = skillName reason = skillName,
pattern = pattern,
} }
local prompt = "#askForCard:::" .. maxNum .. ":" .. minNum local prompt = "#askForCard:::" .. maxNum .. ":" .. minNum
local _, ret = self:askForUseActiveSkill(player, "choose_cards_skill", prompt, cancelable, data) local _, ret = self:askForUseActiveSkill(player, "choose_cards_skill", prompt, cancelable, data)
@ -1078,6 +1084,16 @@ function Room:handleUseCardReply(player, data)
end end
end end
else else
if data.special_skill then
local skill = Fk.skills[data.special_skill]
assert(skill:isInstanceOf(ActiveSkill))
skill:onUse(self, {
from = player.id,
cards = { card },
tos = targets,
})
return nil
end
local use = {} ---@type CardUseStruct local use = {} ---@type CardUseStruct
use.from = player.id use.from = player.id
use.tos = {} use.tos = {}

View File

@ -290,6 +290,20 @@ function ServerPlayer:turnOver()
self.room.logic:trigger(fk.TurnedOver, self) self.room.logic:trigger(fk.TurnedOver, self)
end end
function ServerPlayer:showCards(cards)
cards = Card:getIdList(cards)
local room = self.room
room:sendLog{
type = "#ShowCard",
from = self.id,
card = cards,
}
room:doBroadcastNotify("ShowCard", json.encode{
from = self.id,
cards = cards,
})
end
---@param from_phase Phase ---@param from_phase Phase
---@param to_phase Phase ---@param to_phase Phase
function ServerPlayer:changePhase(from_phase, to_phase) function ServerPlayer:changePhase(from_phase, to_phase)

View File

@ -199,6 +199,8 @@ local ironChainCardSkill = fk.CreateActiveSkill{
local ironChain = fk.CreateTrickCard{ local ironChain = fk.CreateTrickCard{
name = "iron_chain", name = "iron_chain",
skill = ironChainCardSkill, skill = ironChainCardSkill,
-- FIXME! FIXME! FIXME!
special_skills = { "zhiheng" },
} }
extension:addCards{ extension:addCards{
ironChain:clone(Card.Spade, 11), ironChain:clone(Card.Spade, 11),
@ -209,6 +211,45 @@ extension:addCards{
ironChain:clone(Card.Club, 13), ironChain:clone(Card.Club, 13),
} }
local fireAttackSkill = fk.CreateActiveSkill{
name = "fire_attack_skill",
target_num = 1,
target_filter = function(self, to_select)
return not Fk:currentRoom():getPlayerById(to_select):isKongcheng()
end,
on_effect = function(self, room, cardEffectEvent)
local from = room:getPlayerById(cardEffectEvent.from)
local to = room:getPlayerById(cardEffectEvent.to)
if to:isKongcheng() then return end
local showCard = room:askForCard(to, 1, 1, false, self.name, false)[1]
to:showCards(showCard)
showCard = Fk:getCardById(showCard)
local cards = room:askForDiscard(from, 1, 1, false, self.name, true,
".|.|" .. showCard:getSuitString())
if #cards > 0 then
room:damage({
from = from,
to = to,
card = cardEffectEvent.card,
damage = 1,
damageType = fk.FireDamage,
skillName = self.name
})
end
end,
}
local fireAttack = fk.CreateTrickCard{
name = "fire_attack",
skill = fireAttackSkill,
}
extension:addCards{
fireAttack:clone(Card.Heart, 2),
fireAttack:clone(Card.Heart, 3),
fireAttack:clone(Card.Diamond, 12),
}
local supplyShortageSkill = fk.CreateActiveSkill{ local supplyShortageSkill = fk.CreateActiveSkill{
name = "supply_shortage_skill", name = "supply_shortage_skill",
distance_limit = 1, distance_limit = 1,
@ -390,6 +431,9 @@ Fk:loadTranslationTable{
["fire__slash"] = "火杀", ["fire__slash"] = "火杀",
["analeptic"] = "", ["analeptic"] = "",
["iron_chain"] = "铁锁连环", ["iron_chain"] = "铁锁连环",
["_normal_use"] = "正常使用",
["recast"] = "重铸",
["fire_attack"] = "火攻",
["supply_shortage"] = "兵粮寸断", ["supply_shortage"] = "兵粮寸断",
["guding_blade"] = "古锭刀", ["guding_blade"] = "古锭刀",
["vine"] = "藤甲", ["vine"] = "藤甲",

View File

@ -5,11 +5,15 @@ local discardSkill = fk.CreateActiveSkill{
return false return false
end end
local checkpoint = true
if not self.include_equip then if not self.include_equip then
return Fk:currentRoom():getCardArea(to_select) ~= Player.Equip checkpoint = checkpoint and (Fk:currentRoom():getCardArea(to_select) ~= Player.Equip)
end end
return true if self.pattern ~= "" then
checkpoint = checkpoint and (Exppattern:Parse(self.pattern):match(Fk:getCardById(to_select)))
end
return checkpoint
end, end,
min_card_num = function(self) return self.min_num end, min_card_num = function(self) return self.min_num end,
max_card_num = function(self) return self.num end, max_card_num = function(self) return self.num end,
@ -17,17 +21,7 @@ local discardSkill = fk.CreateActiveSkill{
local chooseCardsSkill = fk.CreateActiveSkill{ local chooseCardsSkill = fk.CreateActiveSkill{
name = "choose_cards_skill", name = "choose_cards_skill",
card_filter = function(self, to_select, selected) card_filter = discardSkill.cardFilter,
if #selected >= self.num then
return false
end
if not self.include_equip then
return Fk:currentRoom():getCardArea(to_select) ~= Player.Equip
end
return true
end,
min_card_num = function(self) return self.min_num end, min_card_num = function(self) return self.min_num end,
max_card_num = function(self) return self.num end, max_card_num = function(self) return self.num end,
} }

View File

@ -41,6 +41,7 @@ Fk:loadTranslationTable{
["god_salvation"] = "桃园结义", ["god_salvation"] = "桃园结义",
["amazing_grace"] = "五谷丰登", ["amazing_grace"] = "五谷丰登",
["amazing_grace_skill"] = "五谷选牌",
["lightning"] = "闪电", ["lightning"] = "闪电",

View File

@ -263,6 +263,14 @@ Item {
onCardSelected: function(card) { onCardSelected: function(card) {
Logic.enableTargets(card); Logic.enableTargets(card);
if (typeof card === "number" && card !== -1 && roomScene.state === "playing") {
let skills = JSON.parse(Backend.callLuaFunction("GetCardSpecialSkills", [card]));
skills.unshift("_normal_use");
specialCardSkills.model = skills;
} else {
specialCardSkills.model = [];
}
} }
} }
@ -332,6 +340,41 @@ Item {
} }
} }
Rectangle {
anchors.bottom: parent.bottom
anchors.bottomMargin: 8
anchors.right: okCancel.left
anchors.rightMargin: 20
color: "#88EEEEEE"
radius: 8
visible: roomScene.state == "playing" && specialCardSkills.count > 1
width: childrenRect.width
height: childrenRect.height - 20
RowLayout {
y: -10
Repeater {
id: specialCardSkills
RadioButton {
property string orig_text: modelData
text: Backend.translate(modelData)
checked: index === 0
onCheckedChanged: {
if (modelData === "_normal_use") {
Logic.enableTargets(dashboard.selected_card);
} else {
Logic.enableTargets(JSON.stringify({
skill: modelData,
subcards: [dashboard.selected_card],
}));
}
}
}
}
}
}
Row { Row {
id: okCancel id: okCancel
anchors.bottom: parent.bottom anchors.bottom: parent.bottom
@ -574,6 +617,17 @@ Item {
onActivated: Logic.doCancelButton(); onActivated: Logic.doCancelButton();
} }
function getCurrentCardUseMethod() {
for (let i = 1; i < specialCardSkills.count; i++) {
let item = specialCardSkills.itemAt(i);
if (item.checked) {
let ret = item.orig_text;
console.log(ret);
return ret;
}
}
}
function addToChat(pid, raw, msg) { function addToChat(pid, raw, msg) {
chat.append(msg); chat.append(msg);
let photo = Logic.getPhotoOrSelf(pid); let photo = Logic.getPhotoOrSelf(pid);

View File

@ -1,4 +1,5 @@
import QtQuick import QtQuick
import QtQuick.Layouts
import ".." import ".."
GraphicsBox { GraphicsBox {
@ -11,18 +12,20 @@ GraphicsBox {
width: Math.max(140, body.width + 20) width: Math.max(140, body.width + 20)
height: body.height + title.height + 20 height: body.height + title.height + 20
Column { GridLayout {
id: body id: body
x: 10 x: 10
y: title.height + 5 y: title.height + 5
spacing: 10 flow: GridLayout.TopToBottom
rows: 8
columnSpacing: 10
Repeater { Repeater {
model: options model: options
MetroButton { MetroButton {
Layout.fillWidth: true
text: Backend.translate(modelData) text: Backend.translate(modelData)
anchors.horizontalCenter: parent.horizontalCenter
onClicked: { onClicked: {
result = index; result = index;

View File

@ -68,7 +68,8 @@ function doOkButton() {
replyToServer(JSON.stringify( replyToServer(JSON.stringify(
{ {
card: dashboard.getSelectedCard(), card: dashboard.getSelectedCard(),
targets: selected_targets targets: selected_targets,
special_skill: roomScene.getCurrentCardUseMethod(),
} }
)); ));
return; return;