Enhancement (#170)

1. 手牌上限显示:体力值为负数和手牌上限为无限时显示优化
2. 观星新增提示,修复观星
3. 增加不计入手牌上限技
4. 修复变更武将
5. 修复奸雄和救援
6. 修复选角色的cancelble
7. 增强谋徐盛

---------

Signed-off-by: Mechanel <nyutanislavsky@qq.com>
This commit is contained in:
Nyutanislavsky 2023-06-04 19:39:20 +08:00 committed by GitHub
parent 9519d1b9a7
commit 23762e1600
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 216 additions and 65 deletions

View File

@ -389,7 +389,7 @@ Item {
anchors.rightMargin: 20
color: "#88EEEEEE"
radius: 8
visible: roomScene.state == "playing" && (specialCardSkills.count > 1
visible: roomScene.state == "playing" && specialCardSkills && (specialCardSkills.count > 1
|| (specialCardSkills.model && specialCardSkills.model[0] !== "_normal_use"))
width: childrenRect.width
height: childrenRect.height - 20

View File

@ -691,6 +691,8 @@ callbacks["AskForGuanxing"] = function(jsonData) {
let max_top_cards = data.max_top_cards;
let min_bottom_cards = data.min_bottom_cards;
let max_bottom_cards = data.max_bottom_cards;
let top_area_name = data.top_area_name;
let bottom_area_name = data.bottom_area_name;
roomScene.state = "replying";
roomScene.popupBox.sourceComponent = Qt.createComponent("../RoomElement/GuanxingBox.qml");
data.cards.forEach(id => {
@ -701,11 +703,11 @@ callbacks["AskForGuanxing"] = function(jsonData) {
if (max_top_cards == 0) {
box.areaCapacities = [max_bottom_cards];
box.areaLimits = [min_bottom_cards];
box.areaNames = ["Bottom"];
box.areaNames = [Backend.translate(bottom_area_name)];
} else {
box.areaCapacities = [max_top_cards, max_bottom_cards];
box.areaLimits = [min_top_cards, min_bottom_cards];
box.areaNames = ["Top", "Bottom"];
box.areaNames = [Backend.translate(top_area_name), Backend.translate(bottom_area_name)];
}
box.cards = cards;
box.arrangeCards();

View File

@ -356,9 +356,9 @@ Item {
x: -6
Text {
text: (root.maxCard === root.hp) ? (root.handcards) : (root.handcards + "/" + root.maxCard)
text: (root.maxCard === root.hp || root.hp < 0 ) ? (root.handcards) : (root.handcards + "/" + (root.maxCard < 900 ? root.maxCard : "∞"))
font.family: fontLibian.name
font.pixelSize: root.maxCard === root.hp ? 32 : 27
font.pixelSize: (root.maxCard === root.hp || root.hp < 0 ) ? 32 : 27
//font.weight: 30
color: "white"
anchors.horizontalCenter: parent.horizontalCenter

View File

@ -131,7 +131,7 @@ FreeKill使用的是libgit2的C API与此同时使用Git完成拓展包的下
["BanGeneral"] = "禁将",
["ResumeGeneral"] = "解禁",
["$WelcomeToLobby"] = "欢迎进入FreeKill游戏大厅!",
["$WelcomeToLobby"] = "欢迎进入新月杀游戏大厅!",
-- Room
["$EnterRoom"] = "成功加入房间。",
@ -250,6 +250,9 @@ Fk:loadTranslationTable{
["chained"] = "横置",
["not-chained"] = "重置",
["Top"] = "牌堆顶",
["Bottom"] = "牌堆底",
}
-- related to sendLog

View File

@ -4,17 +4,22 @@
local MaxCardsSkill = StatusSkill:subclass("MaxCardsSkill")
---@param from Player
---@param to Player
---@return integer
function MaxCardsSkill:getFixed(player)
return nil
end
---@param from Player
---@param to Player
---@return integer
function MaxCardsSkill:getCorrect(player)
return 0
end
---@param from Player
---@param card Card
---@return boolean
function MaxCardsSkill:excludeFrom(player, card)
return false
end
return MaxCardsSkill

View File

@ -13,18 +13,21 @@ end
---@param player Player
---@param card Card
---@return boolean
function ProhibitSkill:prohibitUse(player, card)
return false
end
---@param player Player
---@param card Card
---@return boolean
function ProhibitSkill:prohibitResponse(player, card)
return false
end
---@param player Player
---@param card Card
---@return boolean
function ProhibitSkill:prohibitDiscard(player, card)
return false
end

View File

@ -323,13 +323,14 @@ end
---@class MaxCardsSpec: StatusSkillSpec
---@field public correct_func fun(self: MaxCardsSkill, player: Player)
---@field public fixed_func fun(self: MaxCardsSkill, from: Player)
---@field public fixed_func fun(self: MaxCardsSkill, player: Player)
---@field public exclude_from fun(self: MaxCardsSkill, player: Player, card: Card)
---@param spec MaxCardsSpec
---@return MaxCardsSkill
function fk.CreateMaxCardsSkill(spec)
assert(type(spec.name) == "string")
assert(type(spec.correct_func) == "function" or type(spec.fixed_func) == "function")
assert(type(spec.correct_func) == "function" or type(spec.fixed_func) == "function" or type(spec.exclude_from) == "function")
local skill = MaxCardsSkill:new(spec.name)
readStatusSpecToSkill(skill, spec)
@ -339,6 +340,7 @@ function fk.CreateMaxCardsSkill(spec)
if spec.fixed_func then
skill.getFixed = spec.fixed_func
end
skill.excludeFrom = spec.exclude_from or skill.excludeFrom
return skill
end

View File

@ -285,9 +285,16 @@ GameEvent.functions[GameEvent.Phase] = function(self)
end
end,
[Player.Discard] = function()
local discardNum = #player:getCardIds(Player.Hand) - player:getMaxCards()
local discardNum = #table.filter(
player:getCardIds(Player.Hand), function(id)
local card = Fk:getCardById(id)
return table.every(room.status_skills[MaxCardsSkill] or {}, function(skill)
return not skill:excludeFrom(player, card)
end)
end
) - player:getMaxCards()
if discardNum > 0 then
room:askForDiscard(player, discardNum, discardNum, false, self.name)
room:askForDiscard(player, discardNum, discardNum, false, "game_rule")
end
end,
[Player.Finish] = function()

View File

@ -499,10 +499,11 @@ function Room:changeHero(player, new_general, full, isDeputy, sendLog)
self:broadcastProperty(player, "general")
end
if player.kingdom == "unknown" then
player.kingdom = new.kingdom
self:broadcastProperty(player, "kingdom")
end
player.gender = new.gender
self:broadcastProperty(player, "gender")
player.kingdom = new.kingdom
self:broadcastProperty(player, "kingdom")
player.maxHp = player:getGeneralMaxHp()
self:broadcastProperty(player, "maxHp")
@ -917,7 +918,7 @@ Room.askForUseViewAsSkill = Room.askForUseActiveSkill
--- 询问一名角色弃牌。
---
--- 在这个函数里面牌已经被弃掉了
--- 在这个函数里面牌已经被弃掉了除非skipDiscard为true
---@param player ServerPlayer @ 弃牌角色
---@param minNum integer @ 最小值
---@param maxNum integer @ 最大值
@ -926,6 +927,7 @@ Room.askForUseViewAsSkill = Room.askForUseActiveSkill
---@param cancelable boolean @ 能不能点取消?
---@param pattern string @ 弃牌需要符合的规则
---@param prompt string @ 提示信息
---@param skipDiscard boolean @ 是否跳过弃牌(即只询问选择可以弃置的牌)
---@return integer[] @ 弃掉的牌的id列表可能是空的
function Room:askForDiscard(player, minNum, maxNum, includeEquip, skillName, cancelable, pattern, prompt, skipDiscard)
cancelable = cancelable or false
@ -942,6 +944,14 @@ function Room:askForDiscard(player, minNum, maxNum, includeEquip, skillName, can
return false
end
end
if skillName == "game_rule" then
status_skills = Fk:currentRoom().status_skills[MaxCardsSkill] or {}
for _, skill in ipairs(status_skills) do
if skill:excludeFrom(player, card) then
return false
end
end
end
if pattern ~= "" then
checkpoint = checkpoint and (Exppattern:Parse(pattern):match(card))
@ -989,12 +999,13 @@ end
---@param maxNum integer @ 最大值
---@param prompt string @ 提示信息
---@param skillName string @ 技能名
---@param cancelable boolean @ 能否点取消
---@return integer[] @ 选择的玩家id列表可能为空
function Room:askForChoosePlayers(player, targets, minNum, maxNum, prompt, skillName, cancelable)
if maxNum < 1 then
return {}
end
cancelable = (not cancelable) and false or true
cancelable = cancelable or false
local data = {
targets = targets,
@ -1023,7 +1034,7 @@ end
---@param maxNum integer @ 最大值
---@param includeEquip boolean @ 能不能选装备
---@param skillName string @ 技能名
---@param cancelable boolean @ 能不能点取消
---@param cancelable boolean @ 能点取消
---@param pattern string @ 选牌规则
---@param prompt string @ 提示信息
---@param expand_pile string @ 可选私人牌堆名称
@ -1079,6 +1090,7 @@ function Room:askForChooseCardAndPlayers(player, targets, minNum, maxNum, patter
if maxNum < 1 then
return {}
end
cancelable = cancelable or false
pattern = pattern or "."
local pcards = table.filter(player:getCardIds({ Player.Hand, Player.Equip }), function(id)
@ -1094,7 +1106,7 @@ function Room:askForChooseCardAndPlayers(player, targets, minNum, maxNum, patter
pattern = pattern,
skillName = skillName
}
local _, ret = self:askForUseActiveSkill(player, "choose_players_skill", prompt or "", true, data)
local _, ret = self:askForUseActiveSkill(player, "choose_players_skill", prompt or "", cancelable, data)
if ret then
return ret.targets, ret.cards[1]
else
@ -1109,6 +1121,7 @@ end
--- 询问玩家选择一名武将。
---@param player ServerPlayer @ 询问目标
---@param generals string[] @ 可选武将
---@param n integer @ 可选数量默认为1
---@return string @ 选择的武将
function Room:askForGeneral(player, generals, n)
local command = "AskForGeneral"
@ -1248,11 +1261,12 @@ end
---@param player ServerPlayer @ 要询问的玩家
---@param cards integer[] @ 可以被观星的卡牌id列表
---@param top_limit integer[] @ 置于牌堆顶的牌的限制(下限,上限),不填写则不限
---@param bottom_limit integer[] @ 置于牌堆的牌的限制(下限,上限),不填写则不限
---@param bottom_limit integer[] @ 置于牌堆的牌的限制(下限,上限),不填写则不限
---@param customNotify string|null @ 自定义读条操作提示
---@param noPut boolean|null @ 是否进行放置牌操作
---@param areaNames string[]|null @ 左侧提示信息
---@return table<top|bottom, cardId[]>
function Room:askForGuanxing(player, cards, top_limit, bottom_limit, customNotify, noPut)
function Room:askForGuanxing(player, cards, top_limit, bottom_limit, customNotify, noPut, areaNames)
-- 这一大堆都是来提前报错的
top_limit = top_limit or {}
bottom_limit = bottom_limit or {}
@ -1267,6 +1281,9 @@ function Room:askForGuanxing(player, cards, top_limit, bottom_limit, customNotif
if #top_limit > 0 and #bottom_limit > 0 then
assert(#cards >= top_limit[1] + bottom_limit[1] and #cards <= top_limit[2] + bottom_limit[2], "限定区间设置错误:可用空间不能容纳所有牌。")
end
if areaNames then
assert(#areaNames > 1, "左侧提示信息设置错误应有Top和Bottom两个提示信息")
end
local command = "AskForGuanxing"
self:notifyMoveFocus(player, customNotify or command)
local data = {
@ -1275,13 +1292,15 @@ function Room:askForGuanxing(player, cards, top_limit, bottom_limit, customNotif
max_top_cards = top_limit and top_limit[2] or #cards,
min_bottom_cards = bottom_limit and bottom_limit[1] or 0,
max_bottom_cards = bottom_limit and bottom_limit[2] or #cards,
top_area_name = areaNames and areaNames[1] or "Top",
bottom_area_name = areaNames and areaNames[2] or "Bottom",
}
local result = self:doRequest(player, command, json.encode(data))
local top, bottom
if result ~= "" then
local d = json.decode(result)
if #top_limit > 0 and top_limit[1] == 0 then
if #top_limit > 0 and top_limit[2] == 0 then
top = {}
bottom = d[1]
else
@ -1289,16 +1308,16 @@ function Room:askForGuanxing(player, cards, top_limit, bottom_limit, customNotif
bottom = d[2]
end
else
top = cards
bottom = {}
top = table.random(cards, top_limit and top_limit[2] or #cards)
bottom = table.shuffle(table.filter(cards, function(id) return not table.contains(top, id) end))
end
if not noPut then
for i = #top, 1, -1 do
table.insert(self.draw_pile, 1, top[i])
end
for _, id in ipairs(bottom) do
table.insert(self.draw_pile, id)
for i = #bottom, 1, -1 do
table.insert(self.draw_pile, bottom[i])
end
self:sendLog{
@ -1597,12 +1616,12 @@ function Room:askForCustomDialog(player, focustxt, qmlPath, extra_data)
})
end
---@field player ServerPlayer
---@field targetOne ServerPlayer
---@field targetTwo ServerPlayer
---@field skillName string
---@field flag string|null
---@field moveFrom ServerPlayer|null
---@param player ServerPlayer @ 移动的操作
---@param targetOne ServerPlayer @ 移动的目标1玩家
---@param targetTwo ServerPlayer @ 移动的目标2玩家
---@param skillName string @ 技能名
---@param flag string|null @ 限定可移动的区域值为nil装备区和判定区ej
---@param moveFrom ServerPlayer|null @ 是否只是目标1移动给目标2
---@return cardId
function Room:askForMoveCardInBoard(player, targetOne, targetTwo, skillName, flag, moveFrom)
if flag then
@ -1692,12 +1711,12 @@ function Room:askForMoveCardInBoard(player, targetOne, targetTwo, skillName, fla
)
end
--- 询问一名玩家从targets中选择若干名玩家来。
--- 询问一名玩家从targets中选择若干名玩家来移动场上的牌
---@param player ServerPlayer @ 要做选择的玩家
---@param prompt string @ 提示信息
---@param skillName string @ 技能名
---@param cancelable boolean|null @ 是否可以取消选择
---@param flag string|null @ 限定可移动的区域,值为ej
---@param flag string|null @ 限定可移动的区域,值为nil装备区和判定区ej
---@return integer[] @ 选择的玩家id列表可能为空
function Room:askForChooseToMoveCardInBoard(player, prompt, skillName, cancelable, flag)
if flag then
@ -2739,7 +2758,7 @@ function Room:getCardsFromPileByRule(pattern, num, fromPile)
end
until curIndex == randomIndex
if #cardPack < num then
if #cardPack == 0 then
break
end
end

View File

@ -16,6 +16,14 @@ local discardSkill = fk.CreateActiveSkill{
return false
end
end
if Fk.currentResponseReason == "game_rule" then
status_skills = Fk:currentRoom().status_skills[MaxCardsSkill] or {}
for _, skill in ipairs(status_skills) do
if skill:excludeFrom(Self, card) then
return false
end
end
end
if not self.include_equip then
checkpoint = checkpoint and (Fk:currentRoom():getCardArea(to_select) ~= Player.Equip)

View File

@ -13,13 +13,14 @@ local jianxiong = fk.CreateTriggerSkill{
local room = target.room
return data.card ~= nil and
target == player and
target:hasSkill(self.name) and
room:getCardArea(data.card) == Card.Processing and
not target.dead
target:hasSkill(self.name) and not target.dead and
table.find(data.card:isVirtual() and data.card.subcards or {data.card.id}, function(id) return room:getCardArea(id) == Card.Processing end)
end,
on_use = function(self, event, target, player, data)
local room = player.room
room:obtainCard(player.id, data.card, false)
local dummy = Fk:cloneCard("jueying")
dummy:addSubcards(table.filter(data.card:isVirtual() and data.card.subcards or {data.card.id}, function(id) return room:getCardArea(id) == Card.Processing end))
room:obtainCard(player.id, dummy, false)
end,
}
@ -683,7 +684,7 @@ local zhiheng = fk.CreateActiveSkill{
local jiuyuan = fk.CreateTriggerSkill{
name = "jiuyuan$",
anim_type = "support",
frequency = fk.Compulsory,
frequency = Skill.Compulsory,
events = {fk.PreHpRecover},
can_trigger = function(self, event, target, player, data)
return

View File

@ -523,6 +523,7 @@ local godSalvationSkill = fk.CreateActiveSkill{
room:recover({
who = room:getPlayerById(effect.to),
num = 1,
recoverBy = room:getPlayerById(effect.from),
skillName = self.name,
})
end
@ -974,7 +975,7 @@ extension:addCards({
local axeProhibit = fk.CreateProhibitSkill{
name = "#axe_prohibit",
prohibit_discard = function(self, player, card)
return player:hasSkill(self.name) and card.name == "axe" and
return player:hasSkill(self.name) and card and card.name == "axe" and
Fk.currentResponseReason == "#axe_skill" and
Fk:currentRoom():getCardArea(card.id) == Player.Equip
end,

View File

@ -56,8 +56,8 @@ local test_filter = fk.CreateFilterSkill{
return Fk:cloneCard("crossbow", card.suit, card.number)
end,
}
local test_active = fk.CreateActiveSkill{
name = "test_active",
local control = fk.CreateActiveSkill{
name = "control",
can_use = function(self, player)
return true
end,
@ -65,31 +65,42 @@ local test_active = fk.CreateActiveSkill{
-- if self.interaction.data == "joy" then
--local c = Fk:getCardById(card)
--return Self:getPileNameOfId(card) == self.name and c.color == Card.Red
return true
return false
-- end
end,
card_num = 2,
target_filter = function() return true end,
interaction = function()return UI.Spin {
card_num = 0,
target_filter = function(self, to_select)
return to_select ~= Self.id
end,
min_target_num = 1,
--interaction = function()return UI.Spin {
--choices = Fk.package_names,
from=2,to=8,
--from=2,to=8,
-- default = "guanyu",
}end,
--}end,
on_use = function(self, room, effect)
--room:doSuperLightBox("packages/test/qml/Test.qml")
local from = room:getPlayerById(effect.from)
--local to = room:getPlayerById(effect.tos[1])
-- room:swapSeat(from, to)
--from:control(to)
local success, dat = room:askForUseViewAsSkill(from, "test_vs", nil, true)
if success then
local card = Fk.skills["test_vs"]:viewAs(dat.cards)
room:useCard{
from = from.id,
tos = table.map(dat.targets, function(e) return {e} end),
card = card,
}
for _, pid in ipairs(effect.tos) do
local to = room:getPlayerById(pid)
if to:getMark("mouxushengcontrolled") == 0 then
room:addPlayerMark(to, "mouxushengcontrolled")
from:control(to)
else
room:setPlayerMark(to, "mouxushengcontrolled", 0)
to:control(to)
end
end
--local success, dat = room:askForUseViewAsSkill(from, "test_vs", nil, true)
--if success then
--local card = Fk.skills["test_vs"]:viewAs(dat.cards)
--room:useCard{
--from = from.id,
--tos = table.map(dat.targets, function(e) return {e} end),
--card = card,
--}
--end
-- from:pindian({to})
-- local result = room:askForCustomDialog(from, "simayi", "packages/test/qml/TestDialog.qml", "Hello, world. FROM LUA")
-- print(result)
@ -140,19 +151,104 @@ local test_vs = fk.CreateViewAsSkill{
}
local test_trig = fk.CreateTriggerSkill{
name = "test_trig",
events = {fk.Damage},
events = {fk.EventPhaseEnd},
can_trigger = function(self, event, target, player, data)
return target == player and player:hasSkill(self.name) and player.phase == Player.Discard
end,
on_cost = function(self, event, target, player, data)
local cards = player.room:askForDiscard(player, 1, 1, false, self.name, true, nil, "#test_trig-ask", true)
if #cards > 0 then
self.cost_data = cards
return true
end
end,
on_use = function(self, event, target, player, data)
player:drawCards(1, self.name)
player.room.logic:breakTurn()
player.room:throwCard(self.cost_data, self.name, player, player)
end,
}
local damage_maker = fk.CreateActiveSkill{
name = "damage_maker",
can_use = function(self, player)
return true
end,
card_filter = function(self, card)
return false
end,
card_num = 0,
target_filter = function(self)
return true
end,
target_num = 1,
interaction = function()return UI.ComboBox {
choices = {"normal_damage", "fire_damage", "thunder_damage", "ice_damage", "lose_hp", "heal_hp", "lose_max_hp", "heal_max_hp"}
}end,
on_use = function(self, room, effect)
local from = room:getPlayerById(effect.from)
local target = room:getPlayerById(effect.tos[1])
local choice = self.interaction.data
local choices = {}
for i = 1, 99 do
table.insert(choices, tostring(i))
end
local number = tonumber(room:askForChoice(from, choices, self.name, nil))
if choice == "heal_hp" then
room:recover{
who = target,
num = number,
recoverBy = from,
skillName = self.name
}
elseif choice == "heal_max_hp" then
room:changeMaxHp(target, number)
elseif choice == "lose_max_hp" then
room:changeMaxHp(target, -number)
elseif choice == "lose_hp" then
room:loseHp(target, number, self.name)
else
choices = {"normal_damage", "fire_damage", "thunder_damage", "ice_damage"}
room:damage({
from = from,
to = target,
damage = number,
damageType = table.indexOf(choices, choice),
skillName = self.name
})
end
end,
}
local change_hero = fk.CreateActiveSkill{
name = "change_hero",
can_use = function(self, player)
return true
end,
card_filter = function(self, card)
return false
end,
card_num = 0,
target_filter = function(self, to_select, selected)
return #selected < 1
end,
target_num = 1,
on_use = function(self, room, effect)
local from = room:getPlayerById(effect.from)
local target = room:getPlayerById(effect.tos[1])
local generals = table.map(Fk:getGeneralsRandomly(8, Fk:getAllGenerals()), function(p) return p.name end)
local general = room:askForGeneral(from, generals, 1)
if general == nil then
general = table.random(generals)
end
room:changeHero(target, general, false, false, true)
end,
}
local test2 = General(extension, "mouxusheng", "wu", 4, 4, General.Female)
test2.shield = 4
test2:addSkill("rende")
test2:addSkill(cheat)
test2:addSkill(test_active)
test2:addSkill(control)
test2:addSkill(test_vs)
test2:addSkill(test_trig)
--test2:addSkill(test_trig)
test2:addSkill(damage_maker)
test2:addSkill(change_hero)
Fk:loadTranslationTable{
["test_p_0"] = "测试包",
@ -162,6 +258,10 @@ Fk:loadTranslationTable{
["mouxusheng"] = "谋徐盛",
--["cheat"] = "开挂",
[":cheat"] = "出牌阶段,你可以获得一张想要的牌。",
--["#test_trig-ask"] = "你可弃置一张手牌",
["control"] = "控制",
["damage_maker"] = "制伤器",
["change_hero"] = "变更",
}
return { extension }