Heg (#262)
1. 扩容到12人 2. 修复fake技能 3. 国战UI 4. 选将耦合双势力和野心家 5. fake看破修复 6. 重连bug修复 7. 复原武将修复 8. 亮将修复体力上限 9. 修复拼点不可取消 --------- Co-authored-by: notify <notify-ctrl@qq.com>
|
@ -43,6 +43,7 @@ QtObject {
|
|||
property bool serverEnableBot: true
|
||||
property int roomCapacity: 0
|
||||
property int roomTimeout: 0
|
||||
property bool heg: false
|
||||
property bool enableFreeAssign: false
|
||||
property bool observing: false
|
||||
property bool replaying: false
|
||||
|
|
|
@ -34,7 +34,7 @@ Flickable {
|
|||
SpinBox {
|
||||
id: playerNum
|
||||
from: 2
|
||||
to: 8
|
||||
to: 12
|
||||
value: config.preferedPlayerNum
|
||||
|
||||
onValueChanged: {
|
||||
|
|
|
@ -134,6 +134,7 @@ callbacks["EnterRoom"] = (jsonData) => {
|
|||
config.roomTimeout = data[1] - 1;
|
||||
const roomSettings = data[2];
|
||||
config.enableFreeAssign = roomSettings.enableFreeAssign;
|
||||
config.heg = roomSettings.gameMode.includes('heg_mode');
|
||||
mainStack.push(room);
|
||||
mainWindow.busy = false;
|
||||
}
|
||||
|
|
|
@ -12,7 +12,73 @@ const Card = {
|
|||
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() {
|
||||
if (playerNum > 8) {
|
||||
return arrangeManyPhotos();
|
||||
}
|
||||
|
||||
/* Layout of photos:
|
||||
* +---------------+
|
||||
* | 6 5 4 3 2 |
|
||||
|
|
|
@ -4,15 +4,16 @@ import QtQuick
|
|||
import Fk
|
||||
|
||||
Image {
|
||||
source: SkinBank.MAGATAMA_DIR + "0"
|
||||
source: SkinBank.MAGATAMA_DIR + "0" + (config.heg ? '-heg' : '')
|
||||
state: "3"
|
||||
height: 19; fillMode: Image.PreserveAspectFit
|
||||
|
||||
states: [
|
||||
State {
|
||||
name: "3"
|
||||
PropertyChanges {
|
||||
target: main
|
||||
source: SkinBank.MAGATAMA_DIR + "3"
|
||||
source: SkinBank.MAGATAMA_DIR + "3" + (config.heg ? '-heg' : '')
|
||||
opacity: 1
|
||||
scale: 1
|
||||
}
|
||||
|
@ -21,7 +22,7 @@ Image {
|
|||
name: "2"
|
||||
PropertyChanges {
|
||||
target: main
|
||||
source: SkinBank.MAGATAMA_DIR + "2"
|
||||
source: SkinBank.MAGATAMA_DIR + "2" + (config.heg ? '-heg' : '')
|
||||
opacity: 1
|
||||
scale: 1
|
||||
}
|
||||
|
@ -30,7 +31,7 @@ Image {
|
|||
name: "1"
|
||||
PropertyChanges {
|
||||
target: main
|
||||
source: SkinBank.MAGATAMA_DIR + "1"
|
||||
source: SkinBank.MAGATAMA_DIR + "1" + (config.heg ? '-heg' : '')
|
||||
opacity: 1
|
||||
scale: 1
|
||||
}
|
||||
|
@ -39,7 +40,7 @@ Image {
|
|||
name: "0"
|
||||
PropertyChanges {
|
||||
target: main
|
||||
source: SkinBank.MAGATAMA_DIR + "0"
|
||||
source: SkinBank.MAGATAMA_DIR + "0" + (config.heg ? '-heg' : '')
|
||||
opacity: 0
|
||||
scale: 4
|
||||
}
|
||||
|
@ -55,5 +56,6 @@ Image {
|
|||
Image {
|
||||
id: main
|
||||
anchors.centerIn: parent
|
||||
height: 19; fillMode: Image.PreserveAspectFit
|
||||
}
|
||||
}
|
||||
|
|
|
@ -174,6 +174,43 @@ GraphicsBox {
|
|||
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()
|
||||
{
|
||||
choices = [];
|
||||
|
@ -195,7 +232,7 @@ GraphicsBox {
|
|||
|
||||
for (i = 0; i < generalCardList.count; 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)
|
||||
continue;
|
||||
|
||||
|
|
|
@ -32,12 +32,16 @@ CardItem {
|
|||
card.source: SkinBank.getGeneralPicture(name)
|
||||
glow.color: "white" //Engine.kingdomColor[kingdom]
|
||||
|
||||
// FIXME: 藕!!
|
||||
property bool heg: name.startsWith('hs__') || name.startsWith('ld__') || name.includes('heg__')
|
||||
|
||||
Image {
|
||||
source: SkinBank.GENERALCARD_DIR + "border"
|
||||
}
|
||||
|
||||
Image {
|
||||
scale: subkingdom ? 0.6 : 1
|
||||
width: 34; fillMode: Image.PreserveAspectFit
|
||||
transformOrigin: Item.TopLeft
|
||||
source: SkinBank.getGeneralCardDir(kingdom) + kingdom
|
||||
visible: detailed
|
||||
|
@ -46,6 +50,7 @@ CardItem {
|
|||
Image {
|
||||
scale: 0.6; x: 9; y: 12
|
||||
transformOrigin: Item.TopLeft
|
||||
width: 34; fillMode: Image.PreserveAspectFit
|
||||
source: subkingdom ? SkinBank.getGeneralCardDir(subkingdom) + subkingdom : ""
|
||||
visible: detailed
|
||||
}
|
||||
|
@ -54,10 +59,10 @@ CardItem {
|
|||
x: 34
|
||||
y: 4
|
||||
spacing: 1
|
||||
visible: detailed
|
||||
visible: detailed && !heg
|
||||
Repeater {
|
||||
id: hpRepeater
|
||||
model: (hp > 5 || hp !== maxHp) ? 1 : hp
|
||||
model: (!heg) ? ((hp > 5 || hp !== maxHp) ? 1 : hp) : 0
|
||||
Item {
|
||||
width: childrenRect.width
|
||||
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 {
|
||||
visible: shieldNum > 0 && detailed
|
||||
anchors.right: parent.right
|
||||
|
|
|
@ -326,7 +326,7 @@ Item {
|
|||
Image {
|
||||
id: turnedOver
|
||||
visible: !root.faceup
|
||||
source: SkinBank.PHOTO_DIR + "faceturned"
|
||||
source: SkinBank.PHOTO_DIR + "faceturned" + (config.heg ? '-heg' : '')
|
||||
x: 29; y: 5
|
||||
}
|
||||
|
||||
|
@ -505,7 +505,7 @@ Item {
|
|||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.bottomMargin: -32
|
||||
property var seatChr: ["一", "二", "三", "四", "五", "六", "七", "八"]
|
||||
property var seatChr: ["一", "二", "三", "四", "五", "六", "七", "八", "九", "十", "十一", "十二"]
|
||||
font.family: fontLi2.name
|
||||
font.pixelSize: 32
|
||||
text: seatChr[seatNumber - 1]
|
||||
|
|
Before Width: | Height: | Size: 6.0 KiB After Width: | Height: | Size: 8.0 KiB |
BIN
image/card/general/qun-magatama-l.png
Normal file
After Width: | Height: | Size: 1.2 KiB |
BIN
image/card/general/qun-magatama-r.png
Normal file
After Width: | Height: | Size: 1.2 KiB |
BIN
image/card/general/shu-magatama-l.png
Normal file
After Width: | Height: | Size: 1.2 KiB |
BIN
image/card/general/shu-magatama-r.png
Normal file
After Width: | Height: | Size: 1.2 KiB |
BIN
image/card/general/wei-magatama-l.png
Normal file
After Width: | Height: | Size: 1.2 KiB |
BIN
image/card/general/wei-magatama-r.png
Normal file
After Width: | Height: | Size: 1.2 KiB |
BIN
image/card/general/wild-magatama-l.png
Normal file
After Width: | Height: | Size: 1.2 KiB |
BIN
image/card/general/wild-magatama-r.png
Normal file
After Width: | Height: | Size: 1.2 KiB |
BIN
image/card/general/wild.png
Normal file
After Width: | Height: | Size: 9.7 KiB |
BIN
image/card/general/wu-magatama-l.png
Normal file
After Width: | Height: | Size: 1.2 KiB |
BIN
image/card/general/wu-magatama-r.png
Normal file
After Width: | Height: | Size: 1.2 KiB |
BIN
image/photo/faceturned-heg.png
Normal file
After Width: | Height: | Size: 50 KiB |
BIN
image/photo/magatama/0-heg.png
Normal file
After Width: | Height: | Size: 1.6 KiB |
BIN
image/photo/magatama/1-heg.png
Normal file
After Width: | Height: | Size: 2.1 KiB |
BIN
image/photo/magatama/2-heg.png
Normal file
After Width: | Height: | Size: 2.1 KiB |
BIN
image/photo/magatama/3-heg.png
Normal file
After Width: | Height: | Size: 2.4 KiB |
|
@ -99,7 +99,7 @@ Fk:loadTranslationTable({
|
|||
["#AskForNullificationWithoutTo"] = "是否对 %src 使用的 %arg 使用无懈可击?",
|
||||
|
||||
["#AskForDiscard"] = "请弃置 %arg 张牌,最少 %arg2 张",
|
||||
["#askForPindian"] = "请选择一张手牌作为拼点牌",
|
||||
["#askForPindian"] = "%arg:请选择一张手牌作为拼点牌",
|
||||
|
||||
-- ["Trust"] = "托管",
|
||||
-- ["Sort Cards"] = "牌序",
|
||||
|
|
|
@ -235,7 +235,7 @@ FreeKill使用的是libgit2的C API,与此同时使用Git完成拓展包的下
|
|||
|
||||
["#AskForDiscard"] = "请弃置 %arg 张牌,最少 %arg2 张",
|
||||
["#AskForCard"] = "请选择 %arg 张牌,最少 %arg2 张",
|
||||
["#askForPindian"] = "请选择一张手牌作为拼点牌",
|
||||
["#askForPindian"] = "%arg:请选择一张手牌作为拼点牌",
|
||||
["#StartPindianReason"] = "%from 由于 %arg 而发起拼点",
|
||||
["#ShowPindianCard"] = "%from 的拼点牌是 %card",
|
||||
["#ShowPindianResult"] = "%from 在 %from 和 %to 之间的拼点中 %arg",
|
||||
|
@ -255,6 +255,10 @@ FreeKill使用的是libgit2的C API,与此同时使用Git完成拓展包的下
|
|||
["seat#6"] = "六号位",
|
||||
["seat#7"] = "七号位",
|
||||
["seat#8"] = "八号位",
|
||||
["seat#9"] = "九号位",
|
||||
["seat#10"] = "十号位",
|
||||
["seat#11"] = "十一号位",
|
||||
["seat#12"] = "十二号位",
|
||||
["@ControledBy"] = "控制者",
|
||||
|
||||
["Menu"] = "菜单",
|
||||
|
|
|
@ -21,7 +21,7 @@ local GameMode = class("GameMode")
|
|||
function GameMode:initialize(name, min, max)
|
||||
self.name = name
|
||||
self.minPlayer = math.max(min, 2)
|
||||
self.maxPlayer = math.min(max, 8)
|
||||
self.maxPlayer = math.min(max, 12)
|
||||
end
|
||||
|
||||
---@param victim ServerPlayer @ 死者
|
||||
|
|
|
@ -62,7 +62,8 @@ function TriggerSkill:doCost(event, target, player, data)
|
|||
|
||||
local room = player.room
|
||||
-- 对于那种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)
|
||||
end
|
||||
|
||||
|
|
|
@ -27,12 +27,12 @@ end
|
|||
|
||||
---@param player Player
|
||||
function ViewAsSkill:enabledAtPlay(player)
|
||||
return player:hasSkill(self)
|
||||
return self:isEffectable(player)
|
||||
end
|
||||
|
||||
---@param player Player
|
||||
function ViewAsSkill:enabledAtResponse(player, cardResponsing)
|
||||
return player:hasSkill(self)
|
||||
return self:isEffectable(player)
|
||||
end
|
||||
|
||||
---@param player Player
|
||||
|
|
|
@ -21,8 +21,8 @@ GameEvent.functions[GameEvent.Pindian] = function(self)
|
|||
pattern = ".",
|
||||
reason = pindianData.reason,
|
||||
}
|
||||
local prompt = "#askForPindian"
|
||||
local data = { "choose_cards_skill", prompt, true, json.encode(extraData) }
|
||||
local prompt = "#askForPindian:::" .. pindianData.reason
|
||||
local data = { "choose_cards_skill", prompt, false, json.encode(extraData) }
|
||||
|
||||
local targets = {}
|
||||
local moveInfos = {}
|
||||
|
|
|
@ -36,6 +36,20 @@ local function tellRoomToObserver(self, player)
|
|||
player:doNotify("UpdateDrawPile", #self.draw_pile)
|
||||
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()})
|
||||
end
|
||||
|
||||
|
|
|
@ -2480,7 +2480,7 @@ function Room:handleCardEffect(event, cardEffectEvent)
|
|||
end
|
||||
if not table.contains(players, p) then
|
||||
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
|
||||
s.pattern and
|
||||
Exppattern:Parse("nullification"):matchExp(s.pattern) and
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
---@field public phase_index integer
|
||||
---@field public role_shown boolean
|
||||
---@field private _fake_skills Skill[]
|
||||
---@field private _manually_fake_skills Skill[]
|
||||
---@field public prelighted_skills Skill[]
|
||||
---@field private _timewaste_count integer
|
||||
---@field public ai AI
|
||||
|
@ -39,6 +40,7 @@ function ServerPlayer:initialize(_self)
|
|||
self.skipped_phases = {}
|
||||
|
||||
self._fake_skills = {}
|
||||
self._manually_fake_skills = {}
|
||||
self.prelighted_skills = {}
|
||||
self._prelighted_skills = {}
|
||||
|
||||
|
@ -353,6 +355,28 @@ function ServerPlayer:reconnect()
|
|||
self:doNotify("UpdateDrawPile", #room.draw_pile)
|
||||
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")
|
||||
end
|
||||
|
||||
|
@ -702,11 +726,12 @@ function ServerPlayer:reset()
|
|||
from = self.id,
|
||||
arg = "reset-general"
|
||||
}
|
||||
self:setChainState(false)
|
||||
if self.chained then self:setChainState(false) end
|
||||
if not self.faceup then self:turnOver() end
|
||||
end
|
||||
|
||||
--@param from ServerPlayer
|
||||
--- 进行拼点。
|
||||
---@param from ServerPlayer
|
||||
---@param tos ServerPlayer[]
|
||||
---@param skillName string
|
||||
---@param initialCard Card|nil
|
||||
|
@ -740,6 +765,8 @@ function ServerPlayer:addFakeSkill(skill)
|
|||
end
|
||||
if table.contains(self._fake_skills, skill) then return end
|
||||
|
||||
table.insertIfNeed(self._manually_fake_skills, skill)
|
||||
|
||||
table.insert(self._fake_skills, skill)
|
||||
for _, s in ipairs(skill.related_skills) do
|
||||
-- if s.main_skill == skill then -- TODO: need more detailed
|
||||
|
@ -759,6 +786,8 @@ function ServerPlayer:loseFakeSkill(skill)
|
|||
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)
|
||||
for _, s in ipairs(skill.related_skills) do
|
||||
table.removeOne(self._fake_skills, s)
|
||||
|
@ -839,7 +868,7 @@ function ServerPlayer:revealGeneral(isDeputy, no_trigger)
|
|||
end
|
||||
|
||||
local oldKingdom = self.kingdom
|
||||
room:changeHero(self, generalName, false, isDeputy)
|
||||
room:changeHero(self, generalName, false, isDeputy, false, false)
|
||||
if oldKingdom ~= "wild" then
|
||||
local kingdom = general.kingdom
|
||||
self.kingdom = kingdom
|
||||
|
|