1. 扩容到12人
2. 修复fake技能
3. 国战UI
4. 选将耦合双势力和野心家
5. fake看破修复
6. 重连bug修复
7. 复原武将修复
8. 亮将修复体力上限
9. 修复拼点不可取消

---------

Co-authored-by: notify <notify-ctrl@qq.com>
This commit is contained in:
Nyutanislavsky 2023-09-06 22:16:09 +08:00 committed by GitHub
parent fd856731ae
commit 05f65ef284
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
34 changed files with 220 additions and 23 deletions

View File

@ -43,6 +43,7 @@ QtObject {
property bool serverEnableBot: true property bool serverEnableBot: true
property int roomCapacity: 0 property int roomCapacity: 0
property int roomTimeout: 0 property int roomTimeout: 0
property bool heg: false
property bool enableFreeAssign: false property bool enableFreeAssign: false
property bool observing: false property bool observing: false
property bool replaying: false property bool replaying: false

View File

@ -34,7 +34,7 @@ Flickable {
SpinBox { SpinBox {
id: playerNum id: playerNum
from: 2 from: 2
to: 8 to: 12
value: config.preferedPlayerNum value: config.preferedPlayerNum
onValueChanged: { onValueChanged: {

View File

@ -134,6 +134,7 @@ callbacks["EnterRoom"] = (jsonData) => {
config.roomTimeout = data[1] - 1; config.roomTimeout = data[1] - 1;
const roomSettings = data[2]; const roomSettings = data[2];
config.enableFreeAssign = roomSettings.enableFreeAssign; config.enableFreeAssign = roomSettings.enableFreeAssign;
config.heg = roomSettings.gameMode.includes('heg_mode');
mainStack.push(room); mainStack.push(room);
mainWindow.busy = false; mainWindow.busy = false;
} }

View File

@ -12,7 +12,73 @@ const Card = {
Void : 8 Void : 8
} }
function arrangeManyPhotos() {
/* Layout of photos:
* +----------------+
* | -2 ... 2 |
* | -1 1 |
* | 0 |
* +----------------+
*/
const photoBaseWidth = 175;
const photoMaxWidth = 175 * 0.75;
const verticalSpacing = 32;
// Padding is negative, because photos are scaled.
const roomAreaPadding = -16;
let horizontalSpacing = 8;
let photoWidth = (roomArea.width - horizontalSpacing * playerNum) / (playerNum - 1);
let photoScale = 0.75;
if (photoWidth > photoMaxWidth) {
photoWidth = photoMaxWidth;
horizontalSpacing = (roomArea.width - photoWidth * (playerNum - 1)) / playerNum;
} else {
photoScale = photoWidth / photoBaseWidth;
}
const horizontalPadding = (photoWidth - photoBaseWidth) / 2;
const startX = horizontalPadding + horizontalSpacing;
const padding = photoWidth + horizontalSpacing;
let regions = [
{
x: startX + padding * (playerNum - 2),
y: roomScene.height - 220,
scale: photoScale
},
];
let i;
for (i = 0; i < playerNum - 1; i++) {
regions.push({
x: startX + padding * (playerNum - 2 - i),
y: roomAreaPadding,
scale: photoScale,
});
}
regions[1].y += verticalSpacing * 3;
regions[regions.length - 1].y += verticalSpacing * 3;
regions[2].y += verticalSpacing;
regions[regions.length - 2].y += verticalSpacing;
let item, region;
for (i = 0; i < playerNum; i++) {
item = photos.itemAt(i);
if (!item)
continue;
region = regions[photoModel.get(i).index];
item.x = region.x;
item.y = region.y;
item.scale = region.scale;
}
}
function arrangePhotos() { function arrangePhotos() {
if (playerNum > 8) {
return arrangeManyPhotos();
}
/* Layout of photos: /* Layout of photos:
* +---------------+ * +---------------+
* | 6 5 4 3 2 | * | 6 5 4 3 2 |

View File

@ -4,15 +4,16 @@ import QtQuick
import Fk import Fk
Image { Image {
source: SkinBank.MAGATAMA_DIR + "0" source: SkinBank.MAGATAMA_DIR + "0" + (config.heg ? '-heg' : '')
state: "3" state: "3"
height: 19; fillMode: Image.PreserveAspectFit
states: [ states: [
State { State {
name: "3" name: "3"
PropertyChanges { PropertyChanges {
target: main target: main
source: SkinBank.MAGATAMA_DIR + "3" source: SkinBank.MAGATAMA_DIR + "3" + (config.heg ? '-heg' : '')
opacity: 1 opacity: 1
scale: 1 scale: 1
} }
@ -21,7 +22,7 @@ Image {
name: "2" name: "2"
PropertyChanges { PropertyChanges {
target: main target: main
source: SkinBank.MAGATAMA_DIR + "2" source: SkinBank.MAGATAMA_DIR + "2" + (config.heg ? '-heg' : '')
opacity: 1 opacity: 1
scale: 1 scale: 1
} }
@ -30,7 +31,7 @@ Image {
name: "1" name: "1"
PropertyChanges { PropertyChanges {
target: main target: main
source: SkinBank.MAGATAMA_DIR + "1" source: SkinBank.MAGATAMA_DIR + "1" + (config.heg ? '-heg' : '')
opacity: 1 opacity: 1
scale: 1 scale: 1
} }
@ -39,7 +40,7 @@ Image {
name: "0" name: "0"
PropertyChanges { PropertyChanges {
target: main target: main
source: SkinBank.MAGATAMA_DIR + "0" source: SkinBank.MAGATAMA_DIR + "0" + (config.heg ? '-heg' : '')
opacity: 0 opacity: 0
scale: 4 scale: 4
} }
@ -55,5 +56,6 @@ Image {
Image { Image {
id: main id: main
anchors.centerIn: parent anchors.centerIn: parent
height: 19; fillMode: Image.PreserveAspectFit
} }
} }

View File

@ -174,6 +174,43 @@ GraphicsBox {
updatePosition(); updatePosition();
} }
/*
*/
function isHegPair(gcard1, gcard2) {
if (!gcard1 || gcard1 === gcard2) {
return true;
}
if (gcard2.kingdom == "wild") {
return false;
}
if (gcard1.kingdom == "wild") {
return true;
}
const k1 = gcard1.kingdom;
const k2 = gcard2.kingdom;
const sub1 = gcard1.subkingdom;
const sub2 = gcard2.subkingdom;
if (k1 == k2) {
return true;
}
if (sub1 && (sub1 == k2 || sub1 == sub2)) {
return true;
}
if (sub2 && sub2 == k1) {
return true;
}
return false;
}
function updatePosition() function updatePosition()
{ {
choices = []; choices = [];
@ -195,7 +232,7 @@ GraphicsBox {
for (i = 0; i < generalCardList.count; i++) { for (i = 0; i < generalCardList.count; i++) {
item = generalCardList.itemAt(i); item = generalCardList.itemAt(i);
item.selectable = needSameKingdom ? !(selectedItem[0] && (selectedItem[0].kingdom !== item.kingdom)) : true; item.selectable = needSameKingdom ? isHegPair(selectedItem[0], item) : true;
if (selectedItem.indexOf(item) != -1) if (selectedItem.indexOf(item) != -1)
continue; continue;

View File

@ -32,12 +32,16 @@ CardItem {
card.source: SkinBank.getGeneralPicture(name) card.source: SkinBank.getGeneralPicture(name)
glow.color: "white" //Engine.kingdomColor[kingdom] glow.color: "white" //Engine.kingdomColor[kingdom]
// FIXME:
property bool heg: name.startsWith('hs__') || name.startsWith('ld__') || name.includes('heg__')
Image { Image {
source: SkinBank.GENERALCARD_DIR + "border" source: SkinBank.GENERALCARD_DIR + "border"
} }
Image { Image {
scale: subkingdom ? 0.6 : 1 scale: subkingdom ? 0.6 : 1
width: 34; fillMode: Image.PreserveAspectFit
transformOrigin: Item.TopLeft transformOrigin: Item.TopLeft
source: SkinBank.getGeneralCardDir(kingdom) + kingdom source: SkinBank.getGeneralCardDir(kingdom) + kingdom
visible: detailed visible: detailed
@ -46,6 +50,7 @@ CardItem {
Image { Image {
scale: 0.6; x: 9; y: 12 scale: 0.6; x: 9; y: 12
transformOrigin: Item.TopLeft transformOrigin: Item.TopLeft
width: 34; fillMode: Image.PreserveAspectFit
source: subkingdom ? SkinBank.getGeneralCardDir(subkingdom) + subkingdom : "" source: subkingdom ? SkinBank.getGeneralCardDir(subkingdom) + subkingdom : ""
visible: detailed visible: detailed
} }
@ -54,10 +59,10 @@ CardItem {
x: 34 x: 34
y: 4 y: 4
spacing: 1 spacing: 1
visible: detailed visible: detailed && !heg
Repeater { Repeater {
id: hpRepeater id: hpRepeater
model: (hp > 5 || hp !== maxHp) ? 1 : hp model: (!heg) ? ((hp > 5 || hp !== maxHp) ? 1 : hp) : 0
Item { Item {
width: childrenRect.width width: childrenRect.width
height: childrenRect.height height: childrenRect.height
@ -97,6 +102,43 @@ CardItem {
} }
} }
Row {
x: 34
y: 3
spacing: 0
visible: detailed && heg
Repeater {
id: hegHpRepeater
model: heg ? ((hp > 7 || hp !== maxHp) ? 1 : Math.ceil(hp / 2)) : 0
Item {
width: childrenRect.width
height: childrenRect.height
Image {
height: 12; fillMode: Image.PreserveAspectFit
source: SkinBank.getGeneralCardDir(kingdom) + kingdom + "-magatama-l"
}
Image {
x: 4.4
opacity: (index + 1) * 2 <= hp ? 1 : 0
height: 12; fillMode: Image.PreserveAspectFit
source: {
const k = subkingdom ? subkingdom : kingdom;
SkinBank.getGeneralCardDir(k) + k + "-magatama-r"
}
}
}
}
Text {
visible: hp > 7 || hp !== maxHp
text: hp === maxHp ? ("x" + hp / 2) : (" " + hp / 2 + "/" + maxHp / 2)
color: "white"
font.pixelSize: 14
style: Text.Outline
y: -4
}
}
Shield { Shield {
visible: shieldNum > 0 && detailed visible: shieldNum > 0 && detailed
anchors.right: parent.right anchors.right: parent.right

View File

@ -326,7 +326,7 @@ Item {
Image { Image {
id: turnedOver id: turnedOver
visible: !root.faceup visible: !root.faceup
source: SkinBank.PHOTO_DIR + "faceturned" source: SkinBank.PHOTO_DIR + "faceturned" + (config.heg ? '-heg' : '')
x: 29; y: 5 x: 29; y: 5
} }
@ -505,7 +505,7 @@ Item {
anchors.horizontalCenter: parent.horizontalCenter anchors.horizontalCenter: parent.horizontalCenter
anchors.bottom: parent.bottom anchors.bottom: parent.bottom
anchors.bottomMargin: -32 anchors.bottomMargin: -32
property var seatChr: ["一", "二", "三", "四", "五", "六", "七", "八"] property var seatChr: ["一", "二", "三", "四", "五", "六", "七", "八", "九", "十", "十一", "十二"]
font.family: fontLi2.name font.family: fontLi2.name
font.pixelSize: 32 font.pixelSize: 32
text: seatChr[seatNumber - 1] text: seatChr[seatNumber - 1]

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.0 KiB

After

Width:  |  Height:  |  Size: 8.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

BIN
image/card/general/wild.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 50 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

View File

@ -99,7 +99,7 @@ Fk:loadTranslationTable({
["#AskForNullificationWithoutTo"] = "是否对 %src 使用的 %arg 使用无懈可击?", ["#AskForNullificationWithoutTo"] = "是否对 %src 使用的 %arg 使用无懈可击?",
["#AskForDiscard"] = "请弃置 %arg 张牌,最少 %arg2 张", ["#AskForDiscard"] = "请弃置 %arg 张牌,最少 %arg2 张",
["#askForPindian"] = "请选择一张手牌作为拼点牌", ["#askForPindian"] = "%arg请选择一张手牌作为拼点牌",
-- ["Trust"] = "托管", -- ["Trust"] = "托管",
-- ["Sort Cards"] = "牌序", -- ["Sort Cards"] = "牌序",

View File

@ -235,7 +235,7 @@ FreeKill使用的是libgit2的C API与此同时使用Git完成拓展包的下
["#AskForDiscard"] = "请弃置 %arg 张牌,最少 %arg2 张", ["#AskForDiscard"] = "请弃置 %arg 张牌,最少 %arg2 张",
["#AskForCard"] = "请选择 %arg 张牌,最少 %arg2 张", ["#AskForCard"] = "请选择 %arg 张牌,最少 %arg2 张",
["#askForPindian"] = "请选择一张手牌作为拼点牌", ["#askForPindian"] = "%arg请选择一张手牌作为拼点牌",
["#StartPindianReason"] = "%from 由于 %arg 而发起拼点", ["#StartPindianReason"] = "%from 由于 %arg 而发起拼点",
["#ShowPindianCard"] = "%from 的拼点牌是 %card", ["#ShowPindianCard"] = "%from 的拼点牌是 %card",
["#ShowPindianResult"] = "%from 在 %from 和 %to 之间的拼点中 %arg", ["#ShowPindianResult"] = "%from 在 %from 和 %to 之间的拼点中 %arg",
@ -255,6 +255,10 @@ FreeKill使用的是libgit2的C API与此同时使用Git完成拓展包的下
["seat#6"] = "六号位", ["seat#6"] = "六号位",
["seat#7"] = "七号位", ["seat#7"] = "七号位",
["seat#8"] = "八号位", ["seat#8"] = "八号位",
["seat#9"] = "九号位",
["seat#10"] = "十号位",
["seat#11"] = "十一号位",
["seat#12"] = "十二号位",
["@ControledBy"] = "控制者", ["@ControledBy"] = "控制者",
["Menu"] = "菜单", ["Menu"] = "菜单",

View File

@ -21,7 +21,7 @@ local GameMode = class("GameMode")
function GameMode:initialize(name, min, max) function GameMode:initialize(name, min, max)
self.name = name self.name = name
self.minPlayer = math.max(min, 2) self.minPlayer = math.max(min, 2)
self.maxPlayer = math.min(max, 8) self.maxPlayer = math.min(max, 12)
end end
---@param victim ServerPlayer @ 死者 ---@param victim ServerPlayer @ 死者

View File

@ -62,7 +62,8 @@ function TriggerSkill:doCost(event, target, player, data)
local room = player.room local room = player.room
-- 对于那种cost直接返回true的锁定技如果是预亮技那么还是询问一下好 -- 对于那种cost直接返回true的锁定技如果是预亮技那么还是询问一下好
if ret and player:isFakeSkill(self) and end_time - start_time < 10000 then if ret and player:isFakeSkill(self) and end_time - start_time < 10000 and
(self.main_skill and self.main_skill or self).visible then
ret = room:askForSkillInvoke(player, self.name) ret = room:askForSkillInvoke(player, self.name)
end end

View File

@ -27,12 +27,12 @@ end
---@param player Player ---@param player Player
function ViewAsSkill:enabledAtPlay(player) function ViewAsSkill:enabledAtPlay(player)
return player:hasSkill(self) return self:isEffectable(player)
end end
---@param player Player ---@param player Player
function ViewAsSkill:enabledAtResponse(player, cardResponsing) function ViewAsSkill:enabledAtResponse(player, cardResponsing)
return player:hasSkill(self) return self:isEffectable(player)
end end
---@param player Player ---@param player Player

View File

@ -21,8 +21,8 @@ GameEvent.functions[GameEvent.Pindian] = function(self)
pattern = ".", pattern = ".",
reason = pindianData.reason, reason = pindianData.reason,
} }
local prompt = "#askForPindian" local prompt = "#askForPindian:::" .. pindianData.reason
local data = { "choose_cards_skill", prompt, true, json.encode(extraData) } local data = { "choose_cards_skill", prompt, false, json.encode(extraData) }
local targets = {} local targets = {}
local moveInfos = {} local moveInfos = {}

View File

@ -36,6 +36,20 @@ local function tellRoomToObserver(self, player)
player:doNotify("UpdateDrawPile", #self.draw_pile) player:doNotify("UpdateDrawPile", #self.draw_pile)
player:doNotify("UpdateRoundNum", self:getTag("RoundCount") or 0) player:doNotify("UpdateRoundNum", self:getTag("RoundCount") or 0)
-- send printed_cards
for i = -2, -math.huge, -1 do
local c = Fk.printed_cards[i]
if not c then break end
player:doNotify("PrintCard", json.encode{ c.name, c.suit, c.number })
end
-- send card marks
for id, marks in pairs(room.card_marks) do
for k, v in pairs(marks) do
player:doNotify("SetCardMark", json.encode{ id, k, v })
end
end
table.insert(self.observers, {observee.id, player, player:getId()}) table.insert(self.observers, {observee.id, player, player:getId()})
end end

View File

@ -2480,7 +2480,7 @@ function Room:handleCardEffect(event, cardEffectEvent)
end end
if not table.contains(players, p) then if not table.contains(players, p) then
Self = p -- for enabledAtResponse Self = p -- for enabledAtResponse
for _, s in ipairs(p.player_skills) do for _, s in ipairs(table.connect(p.player_skills, p._fake_skills)) do
if if
s.pattern and s.pattern and
Exppattern:Parse("nullification"):matchExp(s.pattern) and Exppattern:Parse("nullification"):matchExp(s.pattern) and

View File

@ -15,6 +15,7 @@
---@field public phase_index integer ---@field public phase_index integer
---@field public role_shown boolean ---@field public role_shown boolean
---@field private _fake_skills Skill[] ---@field private _fake_skills Skill[]
---@field private _manually_fake_skills Skill[]
---@field public prelighted_skills Skill[] ---@field public prelighted_skills Skill[]
---@field private _timewaste_count integer ---@field private _timewaste_count integer
---@field public ai AI ---@field public ai AI
@ -39,6 +40,7 @@ function ServerPlayer:initialize(_self)
self.skipped_phases = {} self.skipped_phases = {}
self._fake_skills = {} self._fake_skills = {}
self._manually_fake_skills = {}
self.prelighted_skills = {} self.prelighted_skills = {}
self._prelighted_skills = {} self._prelighted_skills = {}
@ -353,6 +355,28 @@ function ServerPlayer:reconnect()
self:doNotify("UpdateDrawPile", #room.draw_pile) self:doNotify("UpdateDrawPile", #room.draw_pile)
self:doNotify("UpdateRoundNum", room:getTag("RoundCount") or 0) self:doNotify("UpdateRoundNum", room:getTag("RoundCount") or 0)
-- send printed_cards
for i = -2, -math.huge, -1 do
local c = Fk.printed_cards[i]
if not c then break end
self:doNotify("PrintCard", json.encode{ c.name, c.suit, c.number })
end
-- send card marks
for id, marks in pairs(room.card_marks) do
for k, v in pairs(marks) do
self:doNotify("SetCardMark", json.encode{ id, k, v })
end
end
-- send fake skills
for _, s in ipairs(self._manually_fake_skills) do
self:doNotify("AddSkill", json.encode{ self.id, s.name, true })
if table.contains(self.prelighted_skills, s) then
self:doNotify("PrelightSkill", json.encode{ s.name, true })
end
end
room:broadcastProperty(self, "state") room:broadcastProperty(self, "state")
end end
@ -702,11 +726,12 @@ function ServerPlayer:reset()
from = self.id, from = self.id,
arg = "reset-general" arg = "reset-general"
} }
self:setChainState(false) if self.chained then self:setChainState(false) end
if not self.faceup then self:turnOver() end if not self.faceup then self:turnOver() end
end end
--@param from ServerPlayer --- 进行拼点。
---@param from ServerPlayer
---@param tos ServerPlayer[] ---@param tos ServerPlayer[]
---@param skillName string ---@param skillName string
---@param initialCard Card|nil ---@param initialCard Card|nil
@ -740,6 +765,8 @@ function ServerPlayer:addFakeSkill(skill)
end end
if table.contains(self._fake_skills, skill) then return end if table.contains(self._fake_skills, skill) then return end
table.insertIfNeed(self._manually_fake_skills, skill)
table.insert(self._fake_skills, skill) table.insert(self._fake_skills, skill)
for _, s in ipairs(skill.related_skills) do for _, s in ipairs(skill.related_skills) do
-- if s.main_skill == skill then -- TODO: need more detailed -- if s.main_skill == skill then -- TODO: need more detailed
@ -759,6 +786,8 @@ function ServerPlayer:loseFakeSkill(skill)
end end
if not table.contains(self._fake_skills, skill) then return end if not table.contains(self._fake_skills, skill) then return end
table.removeOne(self._manually_fake_skills, skill)
table.removeOne(self._fake_skills, skill) table.removeOne(self._fake_skills, skill)
for _, s in ipairs(skill.related_skills) do for _, s in ipairs(skill.related_skills) do
table.removeOne(self._fake_skills, s) table.removeOne(self._fake_skills, s)
@ -839,7 +868,7 @@ function ServerPlayer:revealGeneral(isDeputy, no_trigger)
end end
local oldKingdom = self.kingdom local oldKingdom = self.kingdom
room:changeHero(self, generalName, false, isDeputy) room:changeHero(self, generalName, false, isDeputy, false, false)
if oldKingdom ~= "wild" then if oldKingdom ~= "wild" then
local kingdom = general.kingdom local kingdom = general.kingdom
self.kingdom = kingdom self.kingdom = kingdom