Enhancement (#286)

1. 头像界面更换头像和密码增加计时器
2. 武将界面增加设为头像,搜索框文字输入栏微调
3. 整理手牌将不同的pile分开
4. askForCard(s)Chosen自由prompt
5. 开大动画区分主将副将,特化语音
6. 观星框显示卡牌标记
7. 禁止打出修复
8. random_ai禁止技修复
9. 记录器整局游戏修复
10. 禁止亮将修复封杀已亮的将
This commit is contained in:
Nyutanislavsky 2023-11-07 21:14:51 +08:00 committed by GitHub
parent 513fcf36d7
commit 4b1c43f4c9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 144 additions and 44 deletions

View File

@ -19,6 +19,11 @@ ColumnLayout {
} }
} }
Timer {
id: opTimer
interval: 1000
}
RowLayout { RowLayout {
anchors.rightMargin: 8 anchors.rightMargin: 8
spacing: 16 spacing: 16
@ -34,9 +39,10 @@ ColumnLayout {
} }
Button { Button {
text: Backend.translate("Update Avatar") text: Backend.translate("Update Avatar")
enabled: avatarName.text !== "" enabled: avatarName.text !== "" && !opTimer.running
onClicked: { onClicked: {
mainWindow.busy = true; mainWindow.busy = true;
opTimer.start();
ClientInstance.notifyServer( ClientInstance.notifyServer(
"UpdateAvatar", "UpdateAvatar",
JSON.stringify([avatarName.text]) JSON.stringify([avatarName.text])
@ -74,9 +80,10 @@ ColumnLayout {
} }
Button { Button {
text: Backend.translate("Update Password") text: Backend.translate("Update Password")
enabled: oldPassword.text !== "" && newPassword.text !== "" enabled: oldPassword.text !== "" && newPassword.text !== "" && !opTimer.running
onClicked: { onClicked: {
mainWindow.busy = true; mainWindow.busy = true;
opTimer.start();
ClientInstance.notifyServer( ClientInstance.notifyServer(
"UpdatePassword", "UpdatePassword",
JSON.stringify([oldPassword.text, newPassword.text]) JSON.stringify([oldPassword.text, newPassword.text])

View File

@ -359,6 +359,8 @@ Item {
id: word id: word
Layout.fillWidth: true Layout.fillWidth: true
clip: true clip: true
leftPadding: 5
rightPadding: 5
} }
Button { Button {
@ -408,6 +410,24 @@ Item {
config.disabledGeneralsChanged(); config.disabledGeneralsChanged();
} }
} }
Timer {
id: opTimer
interval: 4000
}
Button {
text: Backend.translate("Set as Avatar")
enabled: detailGeneralCard.name !== "" && !opTimer.running && Self.avatar !== detailGeneralCard.name
onClicked: {
mainWindow.busy = true;
opTimer.start();
ClientInstance.notifyServer(
"UpdateAvatar",
JSON.stringify([detailGeneralCard.name])
);
}
}
} }
function loadPackages() { function loadPackages() {

View File

@ -298,25 +298,29 @@ function resortHandcards() {
} }
dashboard.handcardArea.cards.sort((prev, next) => { dashboard.handcardArea.cards.sort((prev, next) => {
if (prev.type === next.type) { if (prev.footnote === next.footnote) {
const prevSubtypeNumber = subtypeString2Number[prev.subtype]; if (prev.type === next.type) {
const nextSubtypeNumber = subtypeString2Number[next.subtype]; const prevSubtypeNumber = subtypeString2Number[prev.subtype];
if (prevSubtypeNumber === nextSubtypeNumber) { const nextSubtypeNumber = subtypeString2Number[next.subtype];
const splitedPrevName = prev.name.split('__'); if (prevSubtypeNumber === nextSubtypeNumber) {
const prevTrueName = splitedPrevName[splitedPrevName.length - 1]; const splitedPrevName = prev.name.split('__');
const prevTrueName = splitedPrevName[splitedPrevName.length - 1];
const splitedNextName = next.name.split('__'); const splitedNextName = next.name.split('__');
const nextTrueName = splitedNextName[splitedNextName.length - 1]; const nextTrueName = splitedNextName[splitedNextName.length - 1];
if (prevTrueName === nextTrueName) { if (prevTrueName === nextTrueName) {
return prev.cid - next.cid; return prev.cid - next.cid;
} else {
return prevTrueName > nextTrueName ? -1 : 1;
}
} else { } else {
return prevTrueName > nextTrueName ? -1 : 1; return prevSubtypeNumber - nextSubtypeNumber;
} }
} else { } else {
return prevSubtypeNumber - nextSubtypeNumber; return prev.type - next.type;
} }
} else { } else {
return prev.type - next.type; return prev.footnote > next.footnote ? 1 : -1;
} }
}); });
@ -1055,13 +1059,18 @@ callbacks["AskForCardChosen"] = (jsonData) => {
// string reason ] // string reason ]
const data = JSON.parse(jsonData); const data = JSON.parse(jsonData);
const reason = data._reason; const reason = data._reason;
const prompt = data._prompt;
roomScene.promptText = Backend.translate("#AskForChooseCard") if (prompt === "") {
.arg(Backend.translate(reason)); roomScene.promptText = Backend.translate("#AskForChooseCard")
.arg(Backend.translate(reason));
} else {
roomScene.setPrompt(processPrompt(prompt), true);
}
roomScene.state = "replying"; roomScene.state = "replying";
roomScene.popupBox.sourceComponent = Qt.createComponent("../RoomElement/PlayerCardBox.qml"); roomScene.popupBox.sourceComponent = Qt.createComponent("../RoomElement/PlayerCardBox.qml");
const box = roomScene.popupBox.item; const box = roomScene.popupBox.item;
box.prompt = prompt;
for (let d of data.card_data) { for (let d of data.card_data) {
const arr = []; const arr = [];
const ids = d[1]; const ids = d[1];
@ -1086,15 +1095,21 @@ callbacks["AskForCardsChosen"] = (jsonData) => {
const min = data._min; const min = data._min;
const max = data._max; const max = data._max;
const reason = data._reason; const reason = data._reason;
const prompt = data._prompt;
roomScene.promptText = Backend.translate("#AskForChooseCards") if (prompt === "") {
roomScene.promptText = Backend.translate("#AskForChooseCards")
.arg(Backend.translate(reason)).arg(min).arg(max); .arg(Backend.translate(reason)).arg(min).arg(max);
} else {
roomScene.setPrompt(processPrompt(prompt), true);
}
roomScene.state = "replying"; roomScene.state = "replying";
roomScene.popupBox.sourceComponent = Qt.createComponent("../RoomElement/PlayerCardBox.qml"); roomScene.popupBox.sourceComponent = Qt.createComponent("../RoomElement/PlayerCardBox.qml");
const box = roomScene.popupBox.item; const box = roomScene.popupBox.item;
box.multiChoose = true; box.multiChoose = true;
box.min = min; box.min = min;
box.max = max; box.max = max;
box.prompt = prompt;
for (let d of data.card_data) { for (let d of data.card_data) {
const arr = []; const arr = [];
const ids = d[1]; const ids = d[1];
@ -1384,7 +1399,7 @@ callbacks["Animate"] = (jsonData) => {
roomScene.bigAnim.source = "../RoomElement/UltSkillAnimation.qml"; roomScene.bigAnim.source = "../RoomElement/UltSkillAnimation.qml";
roomScene.bigAnim.item.loadData({ roomScene.bigAnim.item.loadData({
skill_name: data.name, skill_name: data.name,
general: photo.general, general: data.deputy ? photo.deputyGeneral : photo.general,
}); });
break; break;
} }

View File

@ -100,6 +100,7 @@ GraphicsBox {
name: modelData.name name: modelData.name
suit: modelData.suit suit: modelData.suit
number: modelData.number number: modelData.number
mark: modelData.mark
draggable: true draggable: true
onReleased: arrangeCards(); onReleased: arrangeCards();
} }

View File

@ -6,8 +6,9 @@ import Fk.Pages
GraphicsBox { GraphicsBox {
id: root id: root
property string prompt
title.text: root.multiChoose ? Backend.translate("$ChooseCards").arg(root.min).arg(root.max) : Backend.translate("$ChooseCard") title.text: prompt === "" ? (root.multiChoose ? Backend.translate("$ChooseCards").arg(root.min).arg(root.max) : Backend.translate("$ChooseCard")) : processPrompt(prompt)
// TODO: Adjust the UI design in case there are more than 7 cards // TODO: Adjust the UI design in case there are more than 7 cards
width: 70 + 700 width: 70 + 700
@ -102,6 +103,18 @@ GraphicsBox {
onCardSelected: finished(); onCardSelected: finished();
function processPrompt(prompt) {
const data = prompt.split(":");
let raw = Backend.translate(data[0]);
const src = parseInt(data[1]);
const dest = parseInt(data[2]);
if (raw.match("%src")) raw = raw.replace(/%src/g, Backend.translate(getPhoto(src).general));
if (raw.match("%dest")) raw = raw.replace(/%dest/g, Backend.translate(getPhoto(dest).general));
if (raw.match("%arg2")) raw = raw.replace(/%arg2/g, Backend.translate(data[4]));
if (raw.match("%arg")) raw = raw.replace(/%arg/g, Backend.translate(data[3]));
return raw;
}
function findAreaModel(name) { function findAreaModel(name) {
let ret; let ret;
for (let i = 0; i < cardModel.count; i++) { for (let i = 0; i < cardModel.count; i++) {

View File

@ -27,8 +27,13 @@ Item {
model: 40 model: 40
Text { Text {
text: { text: {
const o = "$" + skillName + (index % 2 + 1); let o = "$" + skillName + "_" + generalName + (index % 2 + 1);
const p = Backend.translate(o); let p = Backend.translate(o);
if (o !== p) {
return p;
}
o = "$" + skillName + (index % 2 + 1);
p = Backend.translate(o);
if (o === p) { if (o === p) {
return "Ultimate Skill Invoked!"; return "Ultimate Skill Invoked!";
} }
@ -53,8 +58,13 @@ Item {
model: 40 model: 40
Text { Text {
text: { text: {
const o = "$" + skillName + ((index + 1) % 2 + 1); let o = "$" + skillName + "_" + generalName + ((index + 1) % 2 + 1);
const p = Backend.translate(o); let p = Backend.translate(o);
if (o !== p) {
return p;
}
o = "$" + skillName + ((index + 1) % 2 + 1);
p = Backend.translate(o);
if (o === p) { if (o === p) {
return "Ultimate Skill Invoked!"; return "Ultimate Skill Invoked!";
} }

View File

@ -370,7 +370,7 @@ end
fk.client_callback["AskForCardChosen"] = function(jsonData) fk.client_callback["AskForCardChosen"] = function(jsonData)
-- jsonData: [ int target_id, string flag, int reason ] -- jsonData: [ int target_id, string flag, int reason ]
local data = json.decode(jsonData) local data = json.decode(jsonData)
local id, flag, reason = data[1], data[2], data[3] local id, flag, reason, prompt = data[1], data[2], data[3], data[4]
local target = ClientInstance:getPlayerById(id) local target = ClientInstance:getPlayerById(id)
local hand = target.player_cards[Player.Hand] local hand = target.player_cards[Player.Hand]
local equip = target.player_cards[Player.Equip] local equip = target.player_cards[Player.Equip]
@ -390,12 +390,14 @@ fk.client_callback["AskForCardChosen"] = function(jsonData)
ui_data = { ui_data = {
_reason = reason, _reason = reason,
card_data = {}, card_data = {},
_prompt = prompt,
} }
if #hand ~= 0 then table.insert(ui_data.card_data, { "$Hand", hand }) end if #hand ~= 0 then table.insert(ui_data.card_data, { "$Hand", hand }) end
if #equip ~= 0 then table.insert(ui_data.card_data, { "$Equip", equip }) end if #equip ~= 0 then table.insert(ui_data.card_data, { "$Equip", equip }) end
if #judge ~= 0 then table.insert(ui_data.card_data, { "$Judge", judge }) end if #judge ~= 0 then table.insert(ui_data.card_data, { "$Judge", judge }) end
else else
ui_data._reason = reason ui_data._reason = reason
ui_data._prompt = prompt
end end
ClientInstance:notifyUI("AskForCardChosen", json.encode(ui_data)) ClientInstance:notifyUI("AskForCardChosen", json.encode(ui_data))
end end
@ -403,7 +405,7 @@ end
fk.client_callback["AskForCardsChosen"] = function(jsonData) fk.client_callback["AskForCardsChosen"] = function(jsonData)
-- jsonData: [ int target_id, int min, int max, string flag, int reason ] -- jsonData: [ int target_id, int min, int max, string flag, int reason ]
local data = json.decode(jsonData) local data = json.decode(jsonData)
local id, min, max, flag, reason = data[1], data[2], data[3], data[4], data[5] local id, min, max, flag, reason, prompt = data[1], data[2], data[3], data[4], data[5], data[6]
local target = ClientInstance:getPlayerById(id) local target = ClientInstance:getPlayerById(id)
local hand = target.player_cards[Player.Hand] local hand = target.player_cards[Player.Hand]
local equip = target.player_cards[Player.Equip] local equip = target.player_cards[Player.Equip]
@ -424,7 +426,8 @@ fk.client_callback["AskForCardsChosen"] = function(jsonData)
_min = min, _min = min,
_max = max, _max = max,
_reason = reason, _reason = reason,
card_data = {} card_data = {},
_prompt = prompt,
} }
if #hand ~= 0 then table.insert(ui_data.card_data, { "$Hand", hand }) end if #hand ~= 0 then table.insert(ui_data.card_data, { "$Hand", hand }) end
if #equip ~= 0 then table.insert(ui_data.card_data, { "$Equip", equip }) end if #equip ~= 0 then table.insert(ui_data.card_data, { "$Equip", equip }) end
@ -433,6 +436,7 @@ fk.client_callback["AskForCardsChosen"] = function(jsonData)
ui_data._min = min ui_data._min = min
ui_data._max = max ui_data._max = max
ui_data._reason = reason ui_data._reason = reason
ui_data._prompt = prompt
end end
ClientInstance:notifyUI("AskForCardsChosen", json.encode(ui_data)) ClientInstance:notifyUI("AskForCardsChosen", json.encode(ui_data))
end end

View File

@ -546,7 +546,7 @@ function CardProhibitedResponse(card)
if c == nil then if c == nil then
return "true" return "true"
else else
ret = Self:prohibitUse(c) ret = Self:prohibitResponse(c)
end end
return json.encode(ret) return json.encode(ret)
end end

View File

@ -25,6 +25,7 @@ Fk:loadTranslationTable{
["Disable message audio"] = "禁用聊天语音", ["Disable message audio"] = "禁用聊天语音",
["Hide unselectable cards"] = "下移不可选卡牌", ["Hide unselectable cards"] = "下移不可选卡牌",
["Ban General Settings"] = "禁将", ["Ban General Settings"] = "禁将",
["Set as Avatar"] = "设为头像",
["Search"] = "搜索", ["Search"] = "搜索",
["Back"] = "返回", ["Back"] = "返回",

View File

@ -15,7 +15,7 @@ local function useActiveSkill(self, skill, card)
filter_func = function() return false end filter_func = function() return false end
end end
if self.command == "PlayCard" and (not skill:canUse(player, card) or player:prohibitUse(card)) then if self.command == "PlayCard" and card and (not player:canUse(card) or player:prohibitUse(card)) then
return "" return ""
end end
@ -31,7 +31,7 @@ local function useActiveSkill(self, skill, card)
local avail_targets = table.filter(self.room:getAlivePlayers(), function(p) local avail_targets = table.filter(self.room:getAlivePlayers(), function(p)
local ret = skill:targetFilter(p.id, selected_targets, selected_cards, card or Fk:cloneCard'zixing') local ret = skill:targetFilter(p.id, selected_targets, selected_cards, card or Fk:cloneCard'zixing')
if ret and card then if ret and card then
if player:prohibitUse(card) then if player:isProhibited(p, card) then
ret = false ret = false
end end
end end
@ -135,8 +135,7 @@ random_cb["AskForUseCard"] = function(self, jsonData)
local cancelable = data[4] or true local cancelable = data[4] or true
local exp = Exppattern:Parse(pattern) local exp = Exppattern:Parse(pattern)
local avail_cards = table.map(player:getCardIds("he"), local avail_cards = table.map(player:getCardIds("he"), Util.Id2CardMapper)
function(id) return Fk:getCardById(id) end)
avail_cards = table.filter(avail_cards, function(c) avail_cards = table.filter(avail_cards, function(c)
return exp:match(c) and not player:prohibitUse(c) return exp:match(c) and not player:prohibitUse(c)
end) end)
@ -187,8 +186,7 @@ random_cb["AskForResponseCard"] = function(self, jsonData)
end end
random_cb["PlayCard"] = function(self, jsonData) random_cb["PlayCard"] = function(self, jsonData)
local cards = table.map(self.player:getCardIds(Player.Hand), local cards = table.map(self.player:getCardIds(Player.Hand), Util.Id2CardMapper)
function(id) return Fk:getCardById(id) end)
local actives = table.filter(self.player:getAllSkills(), function(s) local actives = table.filter(self.player:getAllSkills(), function(s)
return s:isInstanceOf(ActiveSkill) return s:isInstanceOf(ActiveSkill)
end) end)

View File

@ -143,7 +143,7 @@ function GameEvent:searchEvents(eventType, n, func, endEvent)
local events = logic.event_recorder[eventType] or Util.DummyTable local events = logic.event_recorder[eventType] or Util.DummyTable
local from = self.id local from = self.id
local to = endEvent and endEvent.id or self.end_id local to = endEvent and endEvent.id or self.end_id
if to == -1 then to = #logic.all_game_events end if math.abs(to) == 1 then to = #logic.all_game_events end
n = n or 1 n = n or 1
func = func or Util.TrueFunc func = func or Util.TrueFunc

View File

@ -1020,6 +1020,7 @@ function Room:notifySkillInvoked(player, skill_name, skill_type)
self:doAnimate("InvokeUltSkill", { self:doAnimate("InvokeUltSkill", {
name = skill_name, name = skill_name,
player = player.id, player = player.id,
deputy = player.deputyGeneral and player.deputyGeneral ~= "" and table.contains(Fk.generals[player.deputyGeneral]:getSkillNameList(), skill_name),
}) })
self:delay(2000) self:delay(2000)
end end
@ -1520,11 +1521,13 @@ end
---@param target ServerPlayer @ 被选牌的人 ---@param target ServerPlayer @ 被选牌的人
---@param flag any @ 用"hej"三个字母的组合表示能选择哪些区域, h 手牌区, e - 装备区, j - 判定区 ---@param flag any @ 用"hej"三个字母的组合表示能选择哪些区域, h 手牌区, e - 装备区, j - 判定区
---@param reason string @ 原因,一般是技能名 ---@param reason string @ 原因,一般是技能名
---@param prompt string|nil @ 提示信息
---@return integer @ 选择的卡牌id ---@return integer @ 选择的卡牌id
function Room:askForCardChosen(chooser, target, flag, reason) function Room:askForCardChosen(chooser, target, flag, reason, prompt)
local command = "AskForCardChosen" local command = "AskForCardChosen"
prompt = prompt or ""
self:notifyMoveFocus(chooser, command) self:notifyMoveFocus(chooser, command)
local data = {target.id, flag, reason} local data = {target.id, flag, reason, prompt}
local result = self:doRequest(chooser, command, json.encode(data)) local result = self:doRequest(chooser, command, json.encode(data))
if result == "" then if result == "" then
@ -1564,15 +1567,17 @@ end
---@param max integer @ 最大选牌数 ---@param max integer @ 最大选牌数
---@param flag any @ 用"hej"三个字母的组合表示能选择哪些区域, h 手牌区, e - 装备区, j - 判定区 ---@param flag any @ 用"hej"三个字母的组合表示能选择哪些区域, h 手牌区, e - 装备区, j - 判定区
---@param reason string @ 原因,一般是技能名 ---@param reason string @ 原因,一般是技能名
---@param prompt string|nil @ 提示信息
---@return integer[] @ 选择的id ---@return integer[] @ 选择的id
function Room:askForCardsChosen(chooser, target, min, max, flag, reason) function Room:askForCardsChosen(chooser, target, min, max, flag, reason, prompt)
if min == 1 and max == 1 then if min == 1 and max == 1 then
return { self:askForCardChosen(chooser, target, flag, reason) } return { self:askForCardChosen(chooser, target, flag, reason) }
end end
local command = "AskForCardsChosen" local command = "AskForCardsChosen"
prompt = prompt or ""
self:notifyMoveFocus(chooser, command) self:notifyMoveFocus(chooser, command)
local data = {target.id, min, max, flag, reason} local data = {target.id, min, max, flag, reason, prompt}
local result = self:doRequest(chooser, command, json.encode(data)) local result = self:doRequest(chooser, command, json.encode(data))
local ret local ret

View File

@ -920,7 +920,28 @@ function ServerPlayer:revealGeneral(isDeputy, no_trigger)
} }
if not no_trigger then if not no_trigger then
room.logic:trigger(fk.GeneralRevealed, self, generalName) local current_event = room.logic:getCurrentEvent()
if table.contains({GameEvent.Round, GameEvent.Turn, GameEvent.Phase}, current_event.event) then
room.logic:trigger(fk.GeneralRevealed, self, {[isDeputy and "d" or "m"] = generalName})
else
current_event:addExitFunc(function ()
room.logic:trigger(fk.GeneralRevealed, self, {[isDeputy and "d" or "m"] = generalName})
end)
end
end
end
function ServerPlayer:revealGenerals()
self:revealGeneral(false, true)
self:revealGeneral(true, true)
local room = self.room
local current_event = room.logic:getCurrentEvent()
if table.contains({GameEvent.Round, GameEvent.Turn, GameEvent.Phase}, current_event.event) then
room.logic:trigger(fk.GeneralRevealed, self, {["m"] = self:getMark("__heg_general"), ["d"] = self:getMark("__heg_deputy")})
else
current_event:addExitFunc(function ()
room.logic:trigger(fk.GeneralRevealed, self, {["m"] = self:getMark("__heg_general"), ["d"] = self:getMark("__heg_deputy")})
end)
end end
end end

View File

@ -184,8 +184,13 @@ local revealProhibited = fk.CreateInvaliditySkill {
if type(from._fake_skills) == "table" and not table.contains(from._fake_skills, skill) then return false end if type(from._fake_skills) == "table" and not table.contains(from._fake_skills, skill) then return false end
local sname = skill.name local sname = skill.name
for _, g in ipairs(generals) do for _, g in ipairs(generals) do
local ret = g == "m" and from:getMark("__heg_general") or from:getMark("__heg_deputy") if g == "m" then
local general = Fk.generals[ret] if from.general ~= "anjiang" then return false end
else
if from.deputyGeneral ~= "anjiang" then return false end
end
local generalName = g == "m" and from:getMark("__heg_general") or from:getMark("__heg_deputy")
local general = Fk.generals[generalName]
if table.contains(general:getSkillNameList(), sname) then if table.contains(general:getSkillNameList(), sname) then
return true return true
end end