Initial kingdom choosing (#143)

- 实现副势力概念,用于应对双势力机制;
- 完善神将及拥有副势力的武将开局选择势力的机制;
- 完成势力技概念;
- 实现ViewAsSkill在响应时对使用和打出的区分。

---------

Co-authored-by: notify <notify-ctrl@qq.com>
This commit is contained in:
Ho-spair 2023-04-30 18:55:59 +08:00 committed by GitHub
parent 34ce4c9859
commit 19a2cc5ed7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
15 changed files with 175 additions and 29 deletions

View File

@ -13,6 +13,7 @@ function GetGeneralData(name)
package = general.package.name, package = general.package.name,
extension = general.package.extensionName, extension = general.package.extensionName,
kingdom = general.kingdom, kingdom = general.kingdom,
subkingdom = general.subkingdom,
hp = general.hp, hp = general.hp,
maxHp = general.maxHp, maxHp = general.maxHp,
shield = general.shield, shield = general.shield,
@ -323,7 +324,7 @@ function ActiveTargetFilter(skill_name, to_select, selected, selected_cards)
elseif skill:isInstanceOf(ViewAsSkill) then elseif skill:isInstanceOf(ViewAsSkill) then
local card = skill:viewAs(selected_cards) local card = skill:viewAs(selected_cards)
if card then if card then
ret = card.skill:targetFilter(to_select, selected, selected_cards) ret = card.skill:targetFilter(to_select, selected, selected_cards, card)
ret = ret and not Self:isProhibited(Fk:currentRoom():getPlayerById(to_select), card) ret = ret and not Self:isProhibited(Fk:currentRoom():getPlayerById(to_select), card)
end end
end end
@ -402,11 +403,11 @@ function CardProhibitedResponse(cid)
return json.encode(ret) return json.encode(ret)
end end
function SkillCanResponse(skill_name) function SkillCanResponse(skill_name, cardResponsing)
local skill = Fk.skills[skill_name] local skill = Fk.skills[skill_name]
local ret = false local ret = false
if skill and skill:isInstanceOf(ViewAsSkill) then if skill and skill:isInstanceOf(ViewAsSkill) then
ret = skill:enabledAtResponse(Self) ret = skill:enabledAtResponse(Self, cardResponsing)
end end
return json.encode(ret) return json.encode(ret)
end end

View File

@ -139,6 +139,7 @@ FreeKill使用的是libgit2的C API与此同时使用Git完成拓展包的下
["AskForGeneral"] = "选择武将", ["AskForGeneral"] = "选择武将",
["AskForGuanxing"] = "观星", ["AskForGuanxing"] = "观星",
["AskForChoice"] = "选择", ["AskForChoice"] = "选择",
["AskForKingdom"] = "选择势力",
["AskForPindian"] = "拼点", ["AskForPindian"] = "拼点",
["PlayCard"] = "出牌", ["PlayCard"] = "出牌",
@ -164,6 +165,8 @@ FreeKill使用的是libgit2的C API与此同时使用Git完成拓展包的下
["pindianwin"] = "", ["pindianwin"] = "",
["pindiannotwin"] = "没赢", ["pindiannotwin"] = "没赢",
["#ChooseInitialKingdom"] = "请选择初始势力(不可变更)",
["#RevealGeneral"] = "%from 亮出 %arg %arg2", ["#RevealGeneral"] = "%from 亮出 %arg %arg2",
["mainGeneral"] = "主将", ["mainGeneral"] = "主将",
["deputyGeneral"] = "副将", ["deputyGeneral"] = "副将",

View File

@ -50,6 +50,7 @@ function Engine:initialize()
self.translations = {} -- srcText --> translated self.translations = {} -- srcText --> translated
self.game_modes = {} self.game_modes = {}
self.disabled_packs = {} self.disabled_packs = {}
self.kingdoms = {}
self:loadPackages() self:loadPackages()
self:addSkills(AuxSkills) self:addSkills(AuxSkills)
@ -182,6 +183,10 @@ function Engine:addGeneral(general)
end end
self.generals[general.name] = general self.generals[general.name] = general
if general.kingdom ~= "unknown" then
table.insertIfNeed(self.kingdoms, general.kingdom)
end
if general.name ~= general.trueName then if general.name ~= general.trueName then
local tName = general.trueName local tName = general.trueName
self.same_generals[tName] = self.same_generals[tName] or { tName } self.same_generals[tName] = self.same_generals[tName] or { tName }

View File

@ -11,6 +11,7 @@
---@field public name string @ 武将名字 ---@field public name string @ 武将名字
---@field public trueName string @ 武将真名,也许可以分辨标界? ---@field public trueName string @ 武将真名,也许可以分辨标界?
---@field public kingdom string @ 武将所属势力 ---@field public kingdom string @ 武将所属势力
---@field public subkingdom string @ 武将副势力
---@field public hp integer @ 武将初始体力 ---@field public hp integer @ 武将初始体力
---@field public maxHp integer @ 武将初始最大体力 ---@field public maxHp integer @ 武将初始最大体力
---@field public shield integer @ 初始护甲 ---@field public shield integer @ 初始护甲
@ -48,6 +49,7 @@ function General:initialize(package, name, kingdom, hp, maxHp, gender)
self.maxHp = maxHp or hp self.maxHp = maxHp or hp
self.gender = gender or General.Male self.gender = gender or General.Male
self.shield = 0 self.shield = 0
self.subkingdom = nil
self.skills = {} -- skills first added to this general self.skills = {} -- skills first added to this general
self.other_skills = {} -- skill belongs other general, e.g. "mashu" of pangde self.other_skills = {} -- skill belongs other general, e.g. "mashu" of pangde

View File

@ -38,6 +38,7 @@ function Skill:initialize(name, frequency)
self.mute = false self.mute = false
self.anim_type = "" self.anim_type = ""
self.related_skills = {} self.related_skills = {}
self.attachedKingdom = {}
local name_splited = name:split("__") local name_splited = name:split("__")
self.trueName = name_splited[#name_splited] self.trueName = name_splited[#name_splited]
@ -85,4 +86,8 @@ function Skill:isEffectable(player)
return true return true
end end
function Skill:addAttachedKingdom(kingdom)
table.insertIfNeed(self.attachedKingdom, kingdom)
end
return Skill return Skill

View File

@ -31,8 +31,12 @@ function ViewAsSkill:enabledAtPlay(player)
end end
---@param player Player ---@param player Player
function ViewAsSkill:enabledAtResponse(player) function ViewAsSkill:enabledAtResponse(player, cardResponsing)
return player:hasSkill(self) return player:hasSkill(self)
end end
---@param player Player
---@param cardUseStruct CardUseStruct
function ViewAsSkill:beforeUse(player, cardUseStruct) end
return ViewAsSkill return ViewAsSkill

View File

@ -161,7 +161,11 @@ function fk.CreateActiveSkill(spec)
local skill = ActiveSkill:new(spec.name, spec.frequency or Skill.NotFrequent) local skill = ActiveSkill:new(spec.name, spec.frequency or Skill.NotFrequent)
readUsableSpecToSkill(skill, spec) readUsableSpecToSkill(skill, spec)
if spec.can_use then skill.canUse = spec.can_use end if spec.can_use then
skill.canUse = function(curSkill, player)
return spec.can_use(curSkill, player) and curSkill:isEffectable(player)
end
end
if spec.card_filter then skill.cardFilter = spec.card_filter end if spec.card_filter then skill.cardFilter = spec.card_filter end
if spec.target_filter then skill.targetFilter = spec.target_filter end if spec.target_filter then skill.targetFilter = spec.target_filter end
if spec.feasible then if spec.feasible then
@ -193,6 +197,7 @@ end
---@field public pattern string ---@field public pattern string
---@field public enabled_at_play fun(self: ViewAsSkill, player: Player): boolean ---@field public enabled_at_play fun(self: ViewAsSkill, player: Player): boolean
---@field public enabled_at_response fun(self: ViewAsSkill, player: Player): boolean ---@field public enabled_at_response fun(self: ViewAsSkill, player: Player): boolean
---@field public before_use fun(self: ViewAsSkill, player: Player)
---@param spec ViewAsSkillSpec ---@param spec ViewAsSkillSpec
---@return ViewAsSkill ---@return ViewAsSkill
@ -211,10 +216,14 @@ function fk.CreateViewAsSkill(spec)
skill.pattern = spec.pattern skill.pattern = spec.pattern
end end
if type(spec.enabled_at_play) == "function" then if type(spec.enabled_at_play) == "function" then
skill.enabledAtPlay = spec.enabled_at_play skill.enabledAtPlay = function(curSkill, player)
return spec.enabled_at_play(curSkill, player) and curSkill:isEffectable(player)
end
end end
if type(spec.enabled_at_response) == "function" then if type(spec.enabled_at_response) == "function" then
skill.enabledAtResponse = spec.enabled_at_response skill.enabledAtResponse = function(curSkill, player, cardResponsing)
return spec.enabled_at_response(curSkill, player, cardResponsing) and curSkill:isEffectable(player)
end
end end
if spec.interaction then if spec.interaction then
@ -229,6 +238,10 @@ function fk.CreateViewAsSkill(spec)
}) })
end end
if spec.before_use and type(spec.before_use) == "function" then
skill.beforeUse = spec.before_use
end
return skill return skill
end end

View File

@ -8,7 +8,7 @@
fk.NonTrigger = 1 fk.NonTrigger = 1
fk.GameStart = 2 fk.GameStart = 2
fk.TurnStart = 3 fk.TurnStart = 3
fk.TurnEnd = 72 fk.TurnEnd = 73
fk.EventPhaseStart = 4 fk.EventPhaseStart = 4
fk.EventPhaseProceeding = 5 fk.EventPhaseProceeding = 5
fk.EventPhaseEnd = 6 fk.EventPhaseEnd = 6
@ -81,18 +81,19 @@ fk.AskForPeaches = 59
fk.AskForPeachesDone = 60 fk.AskForPeachesDone = 60
fk.Death = 61 fk.Death = 61
fk.BuryVictim = 62 fk.BuryVictim = 62
fk.BeforeGameOverJudge = 63 fk.Deathed = 63
fk.GameOverJudge = 64 fk.BeforeGameOverJudge = 64
fk.GameFinished = 65 fk.GameOverJudge = 65
fk.GameFinished = 66
fk.AskForCardUse = 66 fk.AskForCardUse = 67
fk.AskForCardResponse = 67 fk.AskForCardResponse = 68
fk.StartPindian = 68 fk.StartPindian = 69
fk.PindianCardsDisplayed = 69 fk.PindianCardsDisplayed = 70
fk.PindianResultConfirmed = 70 fk.PindianResultConfirmed = 71
fk.PindianFinished = 71 fk.PindianFinished = 72
-- 72 = TurnEnd -- 73 = TurnEnd
fk.NumOfEvents = 72 fk.NumOfEvents = 74

View File

@ -66,4 +66,6 @@ GameEvent.functions[GameEvent.Death] = function(self)
logic:trigger(fk.GameOverJudge, victim, deathStruct) logic:trigger(fk.GameOverJudge, victim, deathStruct)
logic:trigger(fk.Death, victim, deathStruct) logic:trigger(fk.Death, victim, deathStruct)
logic:trigger(fk.BuryVictim, victim, deathStruct) logic:trigger(fk.BuryVictim, victim, deathStruct)
logic:trigger(fk.Deathed, victim, deathStruct)
end end

View File

@ -83,6 +83,7 @@ function GameLogic:chooseGenerals()
local n = room.settings.enableDeputy and 2 or 1 local n = room.settings.enableDeputy and 2 or 1
local lord = room:getLord() local lord = room:getLord()
local lord_general = nil local lord_general = nil
if lord ~= nil then if lord ~= nil then
room.current = lord room.current = lord
local generals = Fk:getGeneralsRandomly(generalNum) local generals = Fk:getGeneralsRandomly(generalNum)
@ -97,6 +98,23 @@ function GameLogic:chooseGenerals()
end end
room:setPlayerGeneral(lord, lord_general, true) room:setPlayerGeneral(lord, lord_general, true)
if lord.kingdom == "god" or Fk.generals[lord_general].subkingdom then
local allKingdoms = {}
if lord.kingdom == "god" then
allKingdoms = table.simpleClone(Fk.kingdoms)
local exceptedKingdoms = { "god" }
for _, kingdom in ipairs(exceptedKingdoms) do
table.removeOne(allKingdoms, kingdom)
end
else
local curGeneral = Fk.generals[lord_general]
allKingdoms = { curGeneral.kingdom, curGeneral.subkingdom }
end
lord.kingdom = room:askForChoice(lord, allKingdoms, "AskForKingdom", "#ChooseInitialKingdom")
room:broadcastProperty(lord, "kingdom")
end
room:broadcastProperty(lord, "general") room:broadcastProperty(lord, "general")
room:setDeputyGeneral(lord, deputy) room:setDeputyGeneral(lord, deputy)
room:broadcastProperty(lord, "deputyGeneral") room:broadcastProperty(lord, "deputyGeneral")
@ -116,19 +134,62 @@ function GameLogic:chooseGenerals()
room:notifyMoveFocus(nonlord, "AskForGeneral") room:notifyMoveFocus(nonlord, "AskForGeneral")
room:doBroadcastRequest("AskForGeneral", nonlord) room:doBroadcastRequest("AskForGeneral", nonlord)
for _, p in ipairs(nonlord) do for _, p in ipairs(nonlord) do
if p.general == "" and p.reply_ready then if p.general == "" and p.reply_ready then
local generals = json.decode(p.client_reply) local generals = json.decode(p.client_reply)
local general = generals[1] local general = generals[1]
local deputy = generals[2] local deputy = generals[2]
room:setPlayerGeneral(p, general, true) room:setPlayerGeneral(p, general, true, true)
room:setDeputyGeneral(p, deputy) room:setDeputyGeneral(p, deputy)
else else
room:setPlayerGeneral(p, p.default_reply[1], true) room:setPlayerGeneral(p, p.default_reply[1], true, true)
room:setDeputyGeneral(p, p.default_reply[2]) room:setDeputyGeneral(p, p.default_reply[2])
end end
p.default_reply = "" p.default_reply = ""
end end
local specialKingdomPlayers = table.filter(nonlord, function(p)
return p.kingdom == "god" or Fk.generals[p.general].subkingdom
end)
if #specialKingdomPlayers > 0 then
local choiceMap = {}
for _, p in ipairs(specialKingdomPlayers) do
local allKingdoms = {}
if p.kingdom == "god" then
allKingdoms = table.simpleClone(Fk.kingdoms)
local exceptedKingdoms = { "god" }
for _, kingdom in ipairs(exceptedKingdoms) do
table.removeOne(allKingdoms, kingdom)
end
else
local curGeneral = Fk.generals[p.general]
allKingdoms = { curGeneral.kingdom, curGeneral.subkingdom }
end
choiceMap[p.id] = allKingdoms
local data = json.encode({ allKingdoms, "AskForKingdom", "#ChooseInitialKingdom" })
p.request_data = data
end
room:notifyMoveFocus(nonlord, "AskForKingdom")
room:doBroadcastRequest("AskForChoice", specialKingdomPlayers)
for _, p in ipairs(specialKingdomPlayers) do
local kingdomChosen
if p.reply_ready then
kingdomChosen = p.client_reply
else
kingdomChosen = choiceMap[p.id][1]
end
p.kingdom = kingdomChosen
room:notifyProperty(p, p, "kingdom")
end
end
end end
function GameLogic:buildPlayerCircle() function GameLogic:buildPlayerCircle()
@ -157,6 +218,7 @@ function GameLogic:broadcastGeneral()
if p.role ~= "lord" then if p.role ~= "lord" then
room:broadcastProperty(p, "general") room:broadcastProperty(p, "general")
room:broadcastProperty(p, "kingdom")
room:broadcastProperty(p, "deputyGeneral") room:broadcastProperty(p, "deputyGeneral")
elseif #players >= 5 then elseif #players >= 5 then
p.maxHp = p.maxHp + 1 p.maxHp = p.maxHp + 1
@ -188,6 +250,10 @@ function GameLogic:attachSkillToPlayers()
return return
end end
if #skill.attachedKingdom > 0 and not table.contains(skill.attachedKingdom, player.kingdom) then
return
end
room:handleAddLoseSkills(player, skillName, nil, false) room:handleAddLoseSkills(player, skillName, nil, false)
end end
for _, p in ipairs(room.alive_players) do for _, p in ipairs(room.alive_players) do

View File

@ -427,7 +427,8 @@ end
---@param player ServerPlayer ---@param player ServerPlayer
---@param general string ---@param general string
---@param changeKingdom boolean ---@param changeKingdom boolean
function Room:setPlayerGeneral(player, general, changeKingdom) ---@param noBroadcast boolean|null
function Room:setPlayerGeneral(player, general, changeKingdom, noBroadcast)
if Fk.generals[general] == nil then return end if Fk.generals[general] == nil then return end
player.general = general player.general = general
player.gender = Fk.generals[general].gender player.gender = Fk.generals[general].gender
@ -436,7 +437,11 @@ function Room:setPlayerGeneral(player, general, changeKingdom)
if changeKingdom then if changeKingdom then
player.kingdom = Fk.generals[general].kingdom player.kingdom = Fk.generals[general].kingdom
self:broadcastProperty(player, "kingdom") if noBroadcast then
self:notifyProperty(player, player, "kingdom")
else
self:broadcastProperty(player, "kingdom")
end
end end
end end
@ -1303,6 +1308,7 @@ function Room:handleUseCardReply(player, data)
local c = skill:viewAs(selected_cards) local c = skill:viewAs(selected_cards)
if c then if c then
self:useSkill(player, skill) self:useSkill(player, skill)
local use = {} ---@type CardUseStruct local use = {} ---@type CardUseStruct
use.from = player.id use.from = player.id
use.tos = {} use.tos = {}
@ -1313,6 +1319,8 @@ function Room:handleUseCardReply(player, data)
use.tos = nil use.tos = nil
end end
use.card = c use.card = c
skill:beforeUse(player, use)
return use return use
end end
end end
@ -2399,6 +2407,7 @@ function Room:useSkill(player, skill, effect_cb)
end end
end end
player:addSkillUseHistory(skill.name) player:addSkillUseHistory(skill.name)
if effect_cb then if effect_cb then
return execGameEvent(GameEvent.SkillEffect, effect_cb) return execGameEvent(GameEvent.SkillEffect, effect_cb)
end end

View File

@ -601,7 +601,7 @@ end
---@param initialCard Card ---@param initialCard Card
---@return PindianStruct ---@return PindianStruct
function ServerPlayer:pindian(tos, skillName, initialCard) function ServerPlayer:pindian(tos, skillName, initialCard)
local pindianData = { from = self, tos = tos, reson = skillName, fromCard = initialCard, results = {} } local pindianData = { from = self, tos = tos, reason = skillName, fromCard = initialCard, results = {} }
self.room:pindian(pindianData) self.room:pindian(pindianData)
return pindianData return pindianData
end end

View File

@ -145,7 +145,7 @@ Item {
script: { script: {
skillInteraction.source = ""; skillInteraction.source = "";
dashboard.enableCards(responding_card); dashboard.enableCards(responding_card);
dashboard.enableSkills(responding_card); dashboard.enableSkills(responding_card, respond_play);
autoPending = false; autoPending = false;
progress.visible = true; progress.visible = true;
okCancel.visible = true; okCancel.visible = true;

View File

@ -346,13 +346,13 @@ RowLayout {
} }
} }
function enableSkills(cname) { function enableSkills(cname, cardResponsing) {
if (cname) { if (cname) {
// if cname is presented, we are responding use or play. // if cname is presented, we are responding use or play.
for (let i = 0; i < skillButtons.count; i++) { for (let i = 0; i < skillButtons.count; i++) {
let item = skillButtons.itemAt(i); let item = skillButtons.itemAt(i);
let fitpattern = JSON.parse(Backend.callLuaFunction("SkillFitPattern", [item.orig, cname])); let fitpattern = JSON.parse(Backend.callLuaFunction("SkillFitPattern", [item.orig, cname]));
let canresp = JSON.parse(Backend.callLuaFunction("SkillCanResponse", [item.orig])); let canresp = JSON.parse(Backend.callLuaFunction("SkillCanResponse", [item.orig, cardResponsing]));
item.enabled = fitpattern && canresp; item.enabled = fitpattern && canresp;
} }
return; return;

View File

@ -1,6 +1,7 @@
// SPDX-License-Identifier: GPL-3.0-or-later // SPDX-License-Identifier: GPL-3.0-or-later
import QtQuick import QtQuick
import Qt5Compat.GraphicalEffects
import "PhotoElement" import "PhotoElement"
import "../skin-bank.js" as SkinBank import "../skin-bank.js" as SkinBank
@ -17,6 +18,7 @@ import "../skin-bank.js" as SkinBank
CardItem { CardItem {
property string kingdom property string kingdom
property string subkingdom: "wei"
property int hp property int hp
property int maxHp property int maxHp
property int shieldNum property int shieldNum
@ -34,9 +36,17 @@ CardItem {
} }
Image { Image {
scale: subkingdom ? 0.6 : 1
transformOrigin: Item.TopLeft
source: SkinBank.getGeneralCardDir(kingdom) + kingdom source: SkinBank.getGeneralCardDir(kingdom) + kingdom
} }
Image {
scale: 0.6; x: 9; y: 12
transformOrigin: Item.TopLeft
source: subkingdom ? SkinBank.getGeneralCardDir(subkingdom) + subkingdom : ""
}
Row { Row {
x: 34 x: 34
y: 4 y: 4
@ -44,8 +54,32 @@ CardItem {
Repeater { Repeater {
id: hpRepeater id: hpRepeater
model: (hp > 5 || hp !== maxHp) ? 1 : hp model: (hp > 5 || hp !== maxHp) ? 1 : hp
Image { Item {
source: SkinBank.getGeneralCardDir(kingdom) + kingdom + "-magatama" width: childrenRect.width
height: childrenRect.height
Image {
source: SkinBank.getGeneralCardDir(kingdom) + kingdom + "-magatama"
}
Image {
id: subkingdomMagatama
visible: false
source: subkingdom ? SkinBank.getGeneralCardDir(subkingdom) + subkingdom + "-magatama" : ""
}
LinearGradient {
id: subkingdomMask
visible: false
anchors.fill: subkingdomMagatama
gradient: Gradient {
GradientStop { position: 0.35; color: "transparent" }
GradientStop { position: 0.50; color: "white" }
}
}
OpacityMask {
anchors.fill: subkingdomMagatama
source: subkingdomMagatama
maskSource: subkingdomMask
visible: subkingdom
}
} }
} }
@ -123,6 +157,7 @@ CardItem {
onNameChanged: { onNameChanged: {
let data = JSON.parse(Backend.callLuaFunction("GetGeneralData", [name])); let data = JSON.parse(Backend.callLuaFunction("GetGeneralData", [name]));
kingdom = data.kingdom; kingdom = data.kingdom;
subkingdom = (data.subkingdom !== kingdom && data.subkingdom) || "";
hp = data.hp; hp = data.hp;
maxHp = data.maxHp; maxHp = data.maxHp;
shieldNum = data.shield; shieldNum = data.shield;