This commit is contained in:
notify 2023-08-12 00:50:17 +08:00 committed by GitHub
parent 32844bc394
commit 00578c2ad3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 280 additions and 47 deletions

View File

@ -54,6 +54,13 @@ Item {
enabled: root.parent.state != "candidate" || !root.parent.selectable
onTapped: {
const params = { name: mark_name };
if (mark_name.startsWith('@&')) {
params.cardNames = mark_extra.split(',');
roomScene.startCheat("../RoomElement/ViewGeneralPile", params);
return;
}
if (mark_name.startsWith('@$')) {
params.cardNames = mark_extra.split(',');
} else {
@ -88,7 +95,7 @@ Item {
}
let special_value = '';
if (mark.startsWith('@$')) {
if (mark.startsWith('@$') || mark.startsWith('@&')) {
special_value += data.length;
data = data.join(',');
} else {

View File

@ -160,9 +160,7 @@ RowLayout {
}
const pile_data = JSON.parse(Backend.callLuaFunction("GetAllPiles", [self.playerid]));
for (let name in pile_data) {
if (name.endsWith("&")) expandPile(name);
}
extractWoodenOx();
if (cname) {
const ids = [];
@ -288,6 +286,15 @@ RowLayout {
return raw;
}
function extractWoodenOx() {
const pile_data = JSON.parse(Backend.callLuaFunction("GetAllPiles", [self.playerid]));
if (!roomScene.autoPending) { // AskForUseActiveSkill使
for (let name in pile_data) {
if (name.endsWith("&")) expandPile(name);
}
}
}
function updatePending() {
roomScene.resetPrompt();
if (pending_skill === "") return;
@ -358,6 +365,8 @@ RowLayout {
pending_skill = skill_name;
pendings = [];
handcardAreaItem.unselectAll();
retractAllPiles();
for (let i = 0; i < skillButtons.count; i++) {
const item = skillButtons.itemAt(i);
item.enabled = item.pressed;
@ -378,6 +387,9 @@ RowLayout {
retractAllPiles();
if (roomScene.state == "playing")
extractWoodenOx();
pendings = [];
handcardAreaItem.adjustCards();
handcardAreaItem.unselectAll();

View File

@ -418,6 +418,22 @@ Item {
anchors.bottomMargin: 4
style: Text.Outline
}
TapHandler {
enabled: (root.state != "candidate" || !root.selectable) && root.playerid !== Self.id
onTapped: {
const params = { name: "hand_card" };
let data = JSON.parse(Backend.callLuaFunction("GetPlayerHandcards", [root.playerid]));
data = data.filter((e) => e !== -1);
if (data.length === 0)
return;
params.ids = data;
// Just for using room's right drawer
roomScene.startCheat("../RoomElement/ViewPile", params);
}
}
}
TapHandler {

View File

@ -0,0 +1,88 @@
// SPDX-License-Identifier: GPL-3.0-or-later
import QtQuick
import QtQuick.Layouts
import QtQuick.Controls
import Qt5Compat.GraphicalEffects
Item {
id: root
anchors.fill: parent
property var extra_data: ({})
signal finish()
Rectangle {
anchors.fill: parent
color: "black"
GlowText {
id: pileName
text: Backend.translate(extra_data.name)
width: parent.width
anchors.topMargin: 10
horizontalAlignment: Text.AlignHCenter
font.family: fontLibian.name
color: "#E4D5A0"
font.pixelSize: 30
font.weight: Font.Medium
glow.color: "black"
glow.spread: 0.3
glow.radius: 5
}
LinearGradient {
anchors.fill: pileName
source: pileName
gradient: Gradient {
GradientStop {
position: 0
color: "#FEF7C2"
}
GradientStop {
position: 0.5
color: "#D2AD4A"
}
GradientStop {
position: 1
color: "#BE9878"
}
}
}
Flickable {
id: flickableContainer
ScrollBar.vertical: ScrollBar {}
anchors.horizontalCenter: parent.horizontalCenter
anchors.top: parent.top
anchors.topMargin: 40
flickableDirection: Flickable.VerticalFlick
width: parent.width - 20
height: parent.height - 40
contentWidth: cardsList.width
contentHeight: cardsList.height
clip: true
ColumnLayout {
id: cardsList
GridLayout {
columns: Math.floor(flickableContainer.width / 100)
Repeater {
model: extra_data.ids || extra_data.cardNames
GeneralCardItem {
id: cardItem
// width: (flickableContainer.width - 15) / 4
// height: cardItem.width * 1.4
autoBack: false
name: modelData
}
}
}
}
}
}
}

View File

@ -125,7 +125,7 @@ function Client:moveCards(moves)
pcardMax = self:getPlayerById(move.to):getMaxCards(),
id = move.to,
})
if (move.to ~= Self.id and move.toArea == Card.PlayerHand) or table.contains(ids, -1) then
if (not Self:isBuddy(self:getPlayerById(move.to)) and move.toArea == Card.PlayerHand) or table.contains(ids, -1) then
ids = table.map(ids, function() return -1 end)
end
self:getPlayerById(move.to):addCards(move.toArea, ids, move.specialName)
@ -940,6 +940,23 @@ fk.client_callback["PrintCard"] = function(j)
Fk:_addPrintedCard(cd)
end
fk.client_callback["AddBuddy"] = function(j)
local c = ClientInstance
local data = json.decode(j)
local id, hand = table.unpack(data)
local to = c:getPlayerById(id)
Self:addBuddy(to)
to.player_cards[Player.Hand] = hand
end
fk.client_callback["RmBuddy"] = function(j)
local c = ClientInstance
local id = tonumber(j)
local to = c:getPlayerById(id)
Self:removeBuddy(to)
to.player_cards[Player.Hand] = table.map(to.player_cards, function() return -1 end)
end
-- Create ClientInstance (used by Lua)
ClientInstance = Client:new()
dofile "lua/client/client_util.lua"

View File

@ -34,6 +34,7 @@
---@field public cardUsedHistory table<string, integer[]> @ 用牌次数历史记录
---@field public skillUsedHistory table<string, integer[]> @ 发动技能次数的历史记录
---@field public fixedDistance table<Player, integer> @ 与其他玩家的固定距离列表
---@field public buddy_list integer[] @ 队友列表,或者说自己可以观看别人手牌的那些玩家的列表
local Player = class("Player")
---@alias Phase integer
@ -62,7 +63,7 @@ Player.HistoryGame = 4
--- 构造函数。总之这不是随便调用的函数
function Player:initialize()
self.id = 114514
self.id = 0
self.hp = 0
self.maxHp = 0
self.kingdom = "qun"
@ -95,6 +96,7 @@ function Player:initialize()
self.cardUsedHistory = {}
self.skillUsedHistory = {}
self.fixedDistance = {}
self.buddy_list = {}
end
function Player:__tostring()
@ -163,6 +165,8 @@ end
-- 'xxx': invisible mark
-- '@mark': mark with extra data (maybe string or number)
-- '@@mark': mark without data
-- '@$mark': mark with card_name[] data
-- '@&mark': mark with general_name[] data
function Player:addMark(mark, count)
count = count or 1
local num = self.mark[mark]
@ -906,4 +910,17 @@ function Player:getQuestSkillState(skillName)
return type(questSkillState) == "string" and questSkillState or nil
end
function Player:addBuddy(other)
table.insert(self.buddy_list, other.id)
end
function Player:removeBuddy(other)
table.removeOne(self.buddy_list, other.id)
end
function Player:isBuddy(other)
local id = type(other) == "number" and other or other.id
return self.id == id or table.contains(self.buddy_list, id)
end
return Player

View File

@ -8,11 +8,14 @@
fk.NonTrigger = 1
fk.GamePrepared = 78
fk.GameStart = 2
fk.BeforeTurnStart = 83
fk.TurnStart = 3
fk.TurnEnd = 73
fk.AfterTurnEnd = 84
fk.EventPhaseStart = 4
fk.EventPhaseProceeding = 5
fk.EventPhaseEnd = 6
fk.AfterPhaseEnd = 86
fk.EventPhaseChanging = 7
fk.EventPhaseSkipping = 8
@ -114,4 +117,9 @@ fk.CardShown = 77
fk.SkillEffect = 81
fk.AfterSkillEffect = 82
-- 83 = PreTurnStart
-- 84 = AfterTurnEnd
-- 85 = xxx
-- 86 = AfterPhaseEnd
fk.NumOfEvents = 83

View File

@ -31,6 +31,15 @@ GameEvent.functions[GameEvent.Dying] = function(self)
logic:trigger(fk.AfterDying, dyingPlayer, dyingStruct)
end
GameEvent.prepare_funcs[GameEvent.Death] = function(self)
local deathStruct = table.unpack(self.data)
local room = self.room
local victim = room:getPlayerById(deathStruct.who)
if victim.dead then
return true
end
end
GameEvent.functions[GameEvent.Death] = function(self)
local deathStruct = table.unpack(self.data)
local room = self.room

View File

@ -182,9 +182,23 @@ GameEvent.cleaners[GameEvent.Round] = function(self)
end
end
GameEvent.prepare_funcs[GameEvent.Turn] = function(self)
local room = self.room
local logic = room.logic
local player = room.current
local ret
if player.faceup then
ret = logic:trigger(fk.BeforeTurnStart, player)
end
return ret
end
GameEvent.functions[GameEvent.Turn] = function(self)
local room = self.room
room.logic:trigger(fk.TurnStart, room.current)
local logic = room.logic
room:sendLog{ type = "$AppendSeparator" }
@ -192,15 +206,35 @@ GameEvent.functions[GameEvent.Turn] = function(self)
if not player.faceup then
player:turnOver()
elseif not player.dead then
logic:trigger(fk.TurnStart, room.current)
player:play()
logic:trigger(fk.TurnEnd, room.current)
logic:trigger(fk.AfterTurnEnd, room.current, nil, true)
end
room.logic:trigger(fk.TurnEnd, room.current)
end
GameEvent.cleaners[GameEvent.Turn] = function(self)
local room = self.room
local current = room.current
local logic = room.logic
if self.interrupted then
if current.phase ~= Player.NotActive then
local current_phase = current.phase
current.phase = Player.PhaseNone
logic:trigger(fk.EventPhaseChanging, current,
{ from = current_phase, to = Player.NotActive }, true)
current.phase = Player.NotActive
room:broadcastProperty(current, "phase")
logic:trigger(fk.EventPhaseStart, current, nil, true)
end
current.skipped_phases = {}
logic:trigger(fk.TurnEnd, current, nil, true)
logic:trigger(fk.AfterTurnEnd, room.current, nil, true)
end
for _, p in ipairs(room.players) do
p:setCardUseHistory("", 0, Player.HistoryTurn)
p:setSkillUseHistory("", 0, Player.HistoryTurn)
@ -218,24 +252,6 @@ GameEvent.cleaners[GameEvent.Turn] = function(self)
end
end
end
local current = room.current
local logic = room.logic
if self.interrupted then
current.phase = Player.Finish
logic:trigger(fk.EventPhaseStart, current, nil, true)
logic:trigger(fk.EventPhaseEnd, current, nil, true)
current.phase = Player.NotActive
room:broadcastProperty(current, "phase")
logic:trigger(fk.EventPhaseChanging, current,
{ from = Player.Finish, to = Player.NotActive }, true)
logic:trigger(fk.EventPhaseStart, current, nil, true)
current.skipped_phases = {}
logic:trigger(fk.TurnEnd, current, nil, true)
end
end
GameEvent.functions[GameEvent.Phase] = function(self)
@ -288,6 +304,7 @@ GameEvent.functions[GameEvent.Phase] = function(self)
room.logic:trigger(fk.AfterDrawNCards, player, data)
end,
[Player.Play] = function()
player._play_phase_end = false
while not player.dead do
room:notifyMoveFocus(player, "PlayCard")
local result = room:doRequest(player, "PlayCard", player.id)
@ -297,6 +314,11 @@ GameEvent.functions[GameEvent.Phase] = function(self)
if use then
room:useCard(use)
end
if player._play_phase_end then
player._play_phase_end = false
break
end
end
end,
[Player.Discard] = function()
@ -330,6 +352,12 @@ GameEvent.cleaners[GameEvent.Phase] = function(self)
local room = self.room
local player = self.data[1]
--[[
if self.interrupted then
room.logic:trigger(fk.EventPhaseEnd, player, nil, true)
end
--]]
for _, p in ipairs(room.players) do
p:setCardUseHistory("", 0, Player.HistoryPhase)
p:setSkillUseHistory("", 0, Player.HistoryPhase)
@ -347,8 +375,4 @@ GameEvent.cleaners[GameEvent.Phase] = function(self)
end
end
end
if self.interrupted then
room.logic:trigger(fk.EventPhaseEnd, player, nil, true)
end
end

View File

@ -7,6 +7,7 @@
---@field public event integer @ 该事件对应的EventType
---@field public data any @ 事件的附加数据,视类型而定
---@field public parent GameEvent @ 事件的父事件(栈中的上一层事件)
---@field public prepare_func fun(self: GameEvent) @ 事件即将开始时执行的函数
---@field public main_func fun(self: GameEvent) @ 事件的主函数
---@field public clear_func fun(self: GameEvent) @ 事件结束时执行的函数
---@field public extra_clear_funcs fun(self:GameEvent)[] @ 事件结束时执行的自定义函数列表
@ -15,6 +16,9 @@
---@field public interrupted boolean @ 事件是否是因为被强行中断而结束的
local GameEvent = class("GameEvent")
---@type (fun(self: GameEvent): bool)[]
GameEvent.prepare_funcs = {}
---@type (fun(self: GameEvent): bool)[]
GameEvent.functions = {}
@ -37,6 +41,7 @@ function GameEvent:initialize(event, ...)
self.room = RoomInstance
self.event = event
self.data = { ... }
self.prepare_func = GameEvent.prepare_funcs[event] or dummyFunc
self.main_func = wrapCoFunc(GameEvent.functions[event], self) or dummyFunc
self.clear_func = GameEvent.cleaners[event] or dummyFunc
self.extra_clear_funcs = Util.DummyTable
@ -131,7 +136,7 @@ function GameEvent:searchEvents(eventType, n, func, endEvent)
local to = endEvent and endEvent.id or self.end_id
if to == -1 then to = #logic.all_game_events end
n = n or 1
func = func or function() return true end
func = func or Util.TrueFunc
local ret
if #events < 6 then
@ -201,6 +206,7 @@ end
local function breakEvent(self, extra_yield_result)
local cancelEvent = GameEvent:new(GameEvent.BreakEvent, self)
cancelEvent.toId = self.id
local notcanceled = cancelEvent:exec()
local ret, extra_ret = false, nil
if not notcanceled then
@ -218,6 +224,9 @@ function GameEvent:exec()
local ret = false -- false or nil means this event is running normally
local extra_ret
self.parent = logic:getCurrentEvent()
if self:prepare_func() then return true end
logic.game_event_stack:push(self)
logic.current_event_id = logic.current_event_id + 1

View File

@ -794,7 +794,7 @@ function Room:notifyMoveCards(players, card_moves, forceVisible)
local function containArea(area, relevant) --处理区的处理?
local areas = relevant
and {Card.PlayerEquip, Card.PlayerJudge, Card.DiscardPile, Card.Processing, Card.PlayerHand}
and {Card.PlayerEquip, Card.PlayerJudge, Card.DiscardPile, Card.Processing, Card.PlayerHand, Card.PlayerSpecial}
or {Card.PlayerEquip, Card.PlayerJudge, Card.DiscardPile, Card.Processing}
return table.contains(areas, area)
end
@ -803,7 +803,7 @@ function Room:notifyMoveCards(players, card_moves, forceVisible)
-- if move is relevant to player's hands or equips, it should be open
-- cards move from/to equip/judge/discard/processing should be open
if not (move.moveVisible or forceVisible or containArea(move.toArea, move.to == p.id)) then
if not (move.moveVisible or forceVisible or containArea(move.toArea, move.to and p:isBuddy(move.to))) then
for _, info in ipairs(move.moveInfo) do
if not containArea(info.fromArea, move.from == p.id) then
info.cardId = -1

View File

@ -551,6 +551,11 @@ function ServerPlayer:skip(phase)
end
end
function ServerPlayer:endPlayPhase()
self._play_phase_end = true
-- TODO: send log
end
function ServerPlayer:gainAnExtraTurn(delay)
local room = self.room
delay = (delay == nil) and true or delay
@ -851,4 +856,22 @@ function ServerPlayer:control(p)
p.serverplayer = self._splayer
end
-- 22
function ServerPlayer:addBuddy(other)
if type(other) == "number" then
other = self.room:getPlayerById(other)
end
Player.addBuddy(self, other)
self:doNotify("AddBuddy", json.encode{ other.id, other.player_cards[Player.Hand] })
end
function ServerPlayer:removeBuddy(other)
if type(other) == "number" then
other = self.room:getPlayerById(other)
end
Player.removeBuddy(self, other)
self:doNotify("RmBuddy", tostring(other.id))
end
return ServerPlayer

View File

@ -51,7 +51,7 @@ local discardSkill = fk.CreateActiveSkill{
local chooseCardsSkill = fk.CreateActiveSkill{
name = "choose_cards_skill",
expand_pile = function(self) return self.expand_pile end,
-- expand_pile = function(self) return self.expand_pile end,
card_filter = function(self, to_select, selected)
if #selected >= self.num then
return false

View File

@ -418,22 +418,25 @@ void Server::handleNameAndPassword(ClientSocket *client, const QString &name,
passed = true;
} else {
obj = result[0].toObject();
// check if this username already login
// check ban account
int id = obj["id"].toString().toInt();
passed = obj["banned"].toString().toInt() == 0;
if (!passed) {
error_msg = "you have been banned!";
} else if (!players.value(id)) {
// check if password is the same
auto salt = obj["salt"].toString().toLatin1();
decrypted_pw.append(salt);
auto passwordHash =
QCryptographicHash::hash(decrypted_pw, QCryptographicHash::Sha256)
.toHex();
passed = (passwordHash == obj["password"].toString());
if (!passed)
error_msg = "username or password error";
} else {
}
// check if password is the same
auto salt = obj["salt"].toString().toLatin1();
decrypted_pw.append(salt);
auto passwordHash =
QCryptographicHash::hash(decrypted_pw, QCryptographicHash::Sha256)
.toHex();
passed = (passwordHash == obj["password"].toString());
if (!passed) {
error_msg = "username or password error";
} else if (players.value(id)) {
auto player = players.value(id);
// 顶号机制,如果在线的话就让他变成不在线
if (player->getState() == Player::Online) {