mirror of
https://github.com/Qsgs-Fans/FreeKill.git
synced 2024-11-15 19:22:25 +08:00
Use card (#19)
* the process of using card (uncompleted) * code style: tab is 2 spaces(not \t or 4 space) * update lua54.dll to MinGW version(no cygwin1.dll required) * basic ui logic * ActiveSkill * modidy ActiveSkill defaults * todo: defaultEquipSkill * client * send use card to server * playing phase, equip Co-authored-by: Ho-spair <linyuy@163.com>
This commit is contained in:
parent
fd2d7b4d10
commit
dedde94643
|
@ -3,10 +3,10 @@ cmake_minimum_required(VERSION 3.16)
|
||||||
project(FreeKill VERSION 0.0.1)
|
project(FreeKill VERSION 0.0.1)
|
||||||
|
|
||||||
find_package(Qt5 REQUIRED COMPONENTS
|
find_package(Qt5 REQUIRED COMPONENTS
|
||||||
Gui
|
Gui
|
||||||
Qml
|
Qml
|
||||||
Network
|
Network
|
||||||
Multimedia
|
Multimedia
|
||||||
)
|
)
|
||||||
|
|
||||||
find_package(Lua)
|
find_package(Lua)
|
||||||
|
|
Before Width: | Height: | Size: 624 B After Width: | Height: | Size: 624 B |
Before Width: | Height: | Size: 371 B After Width: | Height: | Size: 371 B |
BIN
lib/win/lua54.dll
Normal file → Executable file
BIN
lib/win/lua54.dll
Normal file → Executable file
Binary file not shown.
|
@ -5,140 +5,140 @@ Client = class('Client')
|
||||||
|
|
||||||
-- load client classes
|
-- load client classes
|
||||||
ClientPlayer = require "client.clientplayer"
|
ClientPlayer = require "client.clientplayer"
|
||||||
dofile "lua/client/client_util.lua"
|
|
||||||
|
|
||||||
fk.client_callback = {}
|
fk.client_callback = {}
|
||||||
|
|
||||||
function Client:initialize()
|
function Client:initialize()
|
||||||
self.client = fk.ClientInstance
|
self.client = fk.ClientInstance
|
||||||
self.notifyUI = function(self, command, jsonData)
|
self.notifyUI = function(self, command, jsonData)
|
||||||
fk.Backend:emitNotifyUI(command, jsonData)
|
fk.Backend:emitNotifyUI(command, jsonData)
|
||||||
end
|
end
|
||||||
self.client.callback = function(_self, command, jsonData)
|
self.client.callback = function(_self, command, jsonData)
|
||||||
local cb = fk.client_callback[command]
|
local cb = fk.client_callback[command]
|
||||||
if (type(cb) == "function") then
|
if (type(cb) == "function") then
|
||||||
cb(jsonData)
|
cb(jsonData)
|
||||||
else
|
else
|
||||||
self:notifyUI(command, jsonData);
|
self:notifyUI(command, jsonData);
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
end
|
||||||
|
|
||||||
self.players = {} -- ClientPlayer[]
|
self.players = {} -- ClientPlayer[]
|
||||||
end
|
end
|
||||||
|
|
||||||
---@param id integer
|
---@param id integer
|
||||||
---@return ClientPlayer
|
---@return ClientPlayer
|
||||||
function Client:findPlayer(id)
|
function Client:findPlayer(id)
|
||||||
for _, p in ipairs(self.players) do
|
for _, p in ipairs(self.players) do
|
||||||
if p.player:getId() == id then return p end
|
if p.player:getId() == id then return p end
|
||||||
end
|
end
|
||||||
return nil
|
return nil
|
||||||
end
|
end
|
||||||
|
|
||||||
fk.client_callback["Setup"] = function(jsonData)
|
fk.client_callback["Setup"] = function(jsonData)
|
||||||
-- jsonData: [ int id, string screenName, string avatar ]
|
-- jsonData: [ int id, string screenName, string avatar ]
|
||||||
local data = json.decode(jsonData)
|
local data = json.decode(jsonData)
|
||||||
local id, name, avatar = data[1], data[2], data[3]
|
local id, name, avatar = data[1], data[2], data[3]
|
||||||
local self = fk.Self
|
local self = fk.Self
|
||||||
self:setId(id)
|
self:setId(id)
|
||||||
self:setScreenName(name)
|
self:setScreenName(name)
|
||||||
self:setAvatar(avatar)
|
self:setAvatar(avatar)
|
||||||
Self = ClientPlayer:new(fk.Self)
|
Self = ClientPlayer:new(fk.Self)
|
||||||
table.insert(ClientInstance.players, Self)
|
table.insert(ClientInstance.players, Self)
|
||||||
end
|
end
|
||||||
|
|
||||||
fk.client_callback["AddPlayer"] = function(jsonData)
|
fk.client_callback["AddPlayer"] = function(jsonData)
|
||||||
-- jsonData: [ int id, string screenName, string avatar ]
|
-- jsonData: [ int id, string screenName, string avatar ]
|
||||||
-- when other player enter the room, we create clientplayer(C and lua) for them
|
-- when other player enter the room, we create clientplayer(C and lua) for them
|
||||||
local data = json.decode(jsonData)
|
local data = json.decode(jsonData)
|
||||||
local id, name, avatar = data[1], data[2], data[3]
|
local id, name, avatar = data[1], data[2], data[3]
|
||||||
local player = fk.ClientInstance:addPlayer(id, name, avatar)
|
local player = fk.ClientInstance:addPlayer(id, name, avatar)
|
||||||
table.insert(ClientInstance.players, ClientPlayer:new(player))
|
table.insert(ClientInstance.players, ClientPlayer:new(player))
|
||||||
ClientInstance:notifyUI("AddPlayer", jsonData)
|
ClientInstance:notifyUI("AddPlayer", jsonData)
|
||||||
end
|
end
|
||||||
|
|
||||||
fk.client_callback["RemovePlayer"] = function(jsonData)
|
fk.client_callback["RemovePlayer"] = function(jsonData)
|
||||||
-- jsonData: [ int id ]
|
-- jsonData: [ int id ]
|
||||||
local data = json.decode(jsonData)
|
local data = json.decode(jsonData)
|
||||||
local id = data[1]
|
local id = data[1]
|
||||||
for _, p in ipairs(ClientInstance.players) do
|
for _, p in ipairs(ClientInstance.players) do
|
||||||
if p.player:getId() == id then
|
if p.player:getId() == id then
|
||||||
table.removeOne(ClientInstance.players, p)
|
table.removeOne(ClientInstance.players, p)
|
||||||
break
|
break
|
||||||
end
|
|
||||||
end
|
end
|
||||||
fk.ClientInstance:removePlayer(id)
|
end
|
||||||
ClientInstance:notifyUI("RemovePlayer", jsonData)
|
fk.ClientInstance:removePlayer(id)
|
||||||
|
ClientInstance:notifyUI("RemovePlayer", jsonData)
|
||||||
end
|
end
|
||||||
|
|
||||||
fk.client_callback["ArrangeSeats"] = function(jsonData)
|
fk.client_callback["ArrangeSeats"] = function(jsonData)
|
||||||
local data = json.decode(jsonData)
|
local data = json.decode(jsonData)
|
||||||
local n = #ClientInstance.players
|
local n = #ClientInstance.players
|
||||||
local players = {}
|
local players = {}
|
||||||
|
|
||||||
for i = 1, n do
|
for i = 1, n do
|
||||||
table.insert(players, ClientInstance:findPlayer(data[i]))
|
table.insert(players, ClientInstance:findPlayer(data[i]))
|
||||||
end
|
end
|
||||||
ClientInstance.players = players
|
ClientInstance.players = players
|
||||||
|
|
||||||
ClientInstance:notifyUI("ArrangeSeats", jsonData)
|
ClientInstance:notifyUI("ArrangeSeats", jsonData)
|
||||||
end
|
end
|
||||||
|
|
||||||
fk.client_callback["PropertyUpdate"] = function(jsonData)
|
fk.client_callback["PropertyUpdate"] = function(jsonData)
|
||||||
-- jsonData: [ int id, string property_name, value ]
|
-- jsonData: [ int id, string property_name, value ]
|
||||||
local data = json.decode(jsonData)
|
local data = json.decode(jsonData)
|
||||||
local id, name, value = data[1], data[2], data[3]
|
local id, name, value = data[1], data[2], data[3]
|
||||||
ClientInstance:findPlayer(id)[name] = value
|
ClientInstance:findPlayer(id)[name] = value
|
||||||
ClientInstance:notifyUI("PropertyUpdate", jsonData)
|
ClientInstance:notifyUI("PropertyUpdate", jsonData)
|
||||||
end
|
end
|
||||||
|
|
||||||
--- separated moves to many moves(one card per move)
|
--- separated moves to many moves(one card per move)
|
||||||
---@param moves CardsMoveStruct[]
|
---@param moves CardsMoveStruct[]
|
||||||
local function separateMoves(moves)
|
local function separateMoves(moves)
|
||||||
local ret = {} ---@type CardsMoveInfo[]
|
local ret = {} ---@type CardsMoveInfo[]
|
||||||
for _, move in ipairs(moves) do
|
for _, move in ipairs(moves) do
|
||||||
for _, info in ipairs(move.moveInfo) do
|
for _, info in ipairs(move.moveInfo) do
|
||||||
table.insert(ret, {
|
table.insert(ret, {
|
||||||
ids = {info.cardId},
|
ids = {info.cardId},
|
||||||
from = move.from,
|
from = move.from,
|
||||||
to = move.to,
|
to = move.to,
|
||||||
toArea = move.toArea,
|
toArea = move.toArea,
|
||||||
fromArea = info.fromArea,
|
fromArea = info.fromArea,
|
||||||
})
|
})
|
||||||
end
|
|
||||||
end
|
end
|
||||||
return ret
|
end
|
||||||
|
return ret
|
||||||
end
|
end
|
||||||
|
|
||||||
--- merge separated moves (one fromArea per move)
|
--- merge separated moves (one fromArea per move)
|
||||||
local function mergeMoves(moves)
|
local function mergeMoves(moves)
|
||||||
local ret = {}
|
local ret = {}
|
||||||
local temp = {}
|
local temp = {}
|
||||||
for _, move in ipairs(moves) do
|
for _, move in ipairs(moves) do
|
||||||
if temp[move.fromArea] == nil then
|
if temp[move.fromArea] == nil then
|
||||||
temp[move.fromArea] = {
|
temp[move.fromArea] = {
|
||||||
ids = {},
|
ids = {},
|
||||||
from = move.from,
|
from = move.from,
|
||||||
to = move.to,
|
to = move.to,
|
||||||
fromArea = move.fromArea,
|
fromArea = move.fromArea,
|
||||||
toArea = move.toArea
|
toArea = move.toArea
|
||||||
}
|
}
|
||||||
end
|
|
||||||
table.insert(temp[move.fromArea].ids, move.ids[1])
|
|
||||||
end
|
end
|
||||||
for _, v in pairs(temp) do
|
table.insert(temp[move.fromArea].ids, move.ids[1])
|
||||||
table.insert(ret, v)
|
end
|
||||||
end
|
for _, v in pairs(temp) do
|
||||||
return ret
|
table.insert(ret, v)
|
||||||
|
end
|
||||||
|
return ret
|
||||||
end
|
end
|
||||||
|
|
||||||
fk.client_callback["MoveCards"] = function(jsonData)
|
fk.client_callback["MoveCards"] = function(jsonData)
|
||||||
-- jsonData: CardsMoveStruct[]
|
-- jsonData: CardsMoveStruct[]
|
||||||
local raw_moves = json.decode(jsonData)
|
local raw_moves = json.decode(jsonData)
|
||||||
local separated = separateMoves(raw_moves)
|
local separated = separateMoves(raw_moves)
|
||||||
local merged = mergeMoves(separated)
|
local merged = mergeMoves(separated)
|
||||||
ClientInstance:notifyUI("MoveCards", json.encode(merged))
|
ClientInstance:notifyUI("MoveCards", json.encode(merged))
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Create ClientInstance (used by Lua)
|
-- Create ClientInstance (used by Lua)
|
||||||
ClientInstance = Client:new()
|
ClientInstance = Client:new()
|
||||||
|
dofile "lua/client/client_util.lua"
|
||||||
|
|
|
@ -1,64 +1,145 @@
|
||||||
|
-- All functions in this file are used by Qml
|
||||||
|
|
||||||
function Translate(src)
|
function Translate(src)
|
||||||
return Fk.translations[src]
|
return Fk.translations[src]
|
||||||
end
|
end
|
||||||
|
|
||||||
function GetGeneralData(name)
|
function GetGeneralData(name)
|
||||||
local general = Fk.generals[name]
|
local general = Fk.generals[name]
|
||||||
if general == nil then general = Fk.generals["diaochan"] end
|
if general == nil then general = Fk.generals["diaochan"] end
|
||||||
return json.encode {
|
return json.encode {
|
||||||
kingdom = general.kingdom,
|
kingdom = general.kingdom,
|
||||||
hp = general.hp,
|
hp = general.hp,
|
||||||
maxHp = general.maxHp
|
maxHp = general.maxHp
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
local cardSubtypeStrings = {
|
||||||
|
[Card.SubtypeNone] = "none",
|
||||||
|
[Card.SubtypeDelayedTrick] = "delayed_trick",
|
||||||
|
[Card.SubtypeWeapon] = "weapon",
|
||||||
|
[Card.SubtypeArmor] = "armor",
|
||||||
|
[Card.SubtypeDefensiveRide] = "defensive_horse",
|
||||||
|
[Card.SubtypeOffensiveRide] = "offensive_horse",
|
||||||
|
[Card.SubtypeTreasure] = "treasure",
|
||||||
|
}
|
||||||
|
|
||||||
function GetCardData(id)
|
function GetCardData(id)
|
||||||
local card = Fk.cards[id]
|
local card = Fk.cards[id]
|
||||||
if card == nil then return json.encode{
|
if card == nil then return json.encode{
|
||||||
cid = id,
|
cid = id,
|
||||||
known = false
|
known = false
|
||||||
} end
|
} end
|
||||||
return json.encode{
|
local ret = {
|
||||||
cid = id,
|
cid = id,
|
||||||
name = card.name,
|
name = card.name,
|
||||||
number = card.number,
|
number = card.number,
|
||||||
suit = card:getSuitString(),
|
suit = card:getSuitString(),
|
||||||
color = card.color,
|
color = card.color,
|
||||||
}
|
subtype = cardSubtypeStrings[card.sub_type]
|
||||||
|
}
|
||||||
|
return json.encode(ret)
|
||||||
end
|
end
|
||||||
|
|
||||||
function GetAllGeneralPack()
|
function GetAllGeneralPack()
|
||||||
local ret = {}
|
local ret = {}
|
||||||
for _, name in ipairs(Fk.package_names) do
|
for _, name in ipairs(Fk.package_names) do
|
||||||
if Fk.packages[name].type == Package.GeneralPack then
|
if Fk.packages[name].type == Package.GeneralPack then
|
||||||
table.insert(ret, name)
|
table.insert(ret, name)
|
||||||
end
|
|
||||||
end
|
end
|
||||||
return json.encode(ret)
|
end
|
||||||
|
return json.encode(ret)
|
||||||
end
|
end
|
||||||
|
|
||||||
function GetGenerals(pack_name)
|
function GetGenerals(pack_name)
|
||||||
local ret = {}
|
local ret = {}
|
||||||
for _, g in ipairs(Fk.packages[pack_name].generals) do
|
for _, g in ipairs(Fk.packages[pack_name].generals) do
|
||||||
table.insert(ret, g.name)
|
table.insert(ret, g.name)
|
||||||
end
|
end
|
||||||
return json.encode(ret)
|
return json.encode(ret)
|
||||||
end
|
end
|
||||||
|
|
||||||
function GetAllCardPack()
|
function GetAllCardPack()
|
||||||
local ret = {}
|
local ret = {}
|
||||||
for _, name in ipairs(Fk.package_names) do
|
for _, name in ipairs(Fk.package_names) do
|
||||||
if Fk.packages[name].type == Package.CardPack then
|
if Fk.packages[name].type == Package.CardPack then
|
||||||
table.insert(ret, name)
|
table.insert(ret, name)
|
||||||
end
|
|
||||||
end
|
end
|
||||||
return json.encode(ret)
|
end
|
||||||
|
return json.encode(ret)
|
||||||
end
|
end
|
||||||
|
|
||||||
function GetCards(pack_name)
|
function GetCards(pack_name)
|
||||||
local ret = {}
|
local ret = {}
|
||||||
for _, c in ipairs(Fk.packages[pack_name].cards) do
|
for _, c in ipairs(Fk.packages[pack_name].cards) do
|
||||||
table.insert(ret, c.id)
|
table.insert(ret, c.id)
|
||||||
end
|
end
|
||||||
return json.encode(ret)
|
return json.encode(ret)
|
||||||
|
end
|
||||||
|
|
||||||
|
---@param card string | integer
|
||||||
|
---@param player integer
|
||||||
|
function CanUseCard(card, player)
|
||||||
|
local c ---@type Card
|
||||||
|
if type(card) == "number" then
|
||||||
|
c = Fk:getCardById(card)
|
||||||
|
else
|
||||||
|
error()
|
||||||
|
end
|
||||||
|
|
||||||
|
local ret = c.skill:canUse(ClientInstance:findPlayer(player))
|
||||||
|
return json.encode(ret)
|
||||||
|
end
|
||||||
|
|
||||||
|
---@param card string | integer
|
||||||
|
---@param to_select integer @ id of the target
|
||||||
|
---@param selected integer[] @ ids of selected targets
|
||||||
|
---@param selected_cards integer[] @ ids of selected cards
|
||||||
|
function CanUseCardToTarget(card, to_select, selected)
|
||||||
|
local c ---@type Card
|
||||||
|
local selected_cards
|
||||||
|
if type(card) == "number" then
|
||||||
|
c = Fk:getCardById(card)
|
||||||
|
selected_cards = {card}
|
||||||
|
else
|
||||||
|
error()
|
||||||
|
end
|
||||||
|
|
||||||
|
local ret = c.skill:targetFilter(to_select, selected, selected_cards)
|
||||||
|
return json.encode(ret)
|
||||||
|
end
|
||||||
|
|
||||||
|
---@param card string | integer
|
||||||
|
---@param to_select integer @ id of a card not selected
|
||||||
|
---@param selected integer[] @ ids of selected cards
|
||||||
|
---@param selected_targets integer[] @ ids of selected players
|
||||||
|
function CanSelectCardForSkill(card, to_select, selected_targets)
|
||||||
|
local c ---@type Card
|
||||||
|
local selected_cards
|
||||||
|
if type(card) == "number" then
|
||||||
|
c = Fk:getCardById(card)
|
||||||
|
selected_cards = {card}
|
||||||
|
else
|
||||||
|
error()
|
||||||
|
end
|
||||||
|
|
||||||
|
local ret = c.skill:cardFilter(to_select, selected_cards, selected_targets)
|
||||||
|
return json.encode(ret)
|
||||||
|
end
|
||||||
|
|
||||||
|
---@param card string | integer
|
||||||
|
---@param selected integer[] @ ids of selected cards
|
||||||
|
---@param selected_targets integer[] @ ids of selected players
|
||||||
|
function CardFeasible(card, selected_targets)
|
||||||
|
local c ---@type Card
|
||||||
|
local selected_cards
|
||||||
|
if type(card) == "number" then
|
||||||
|
c = Fk:getCardById(card)
|
||||||
|
selected_cards = {card}
|
||||||
|
else
|
||||||
|
error()
|
||||||
|
end
|
||||||
|
|
||||||
|
local ret = c.skill:feasible(selected_cards, selected_targets)
|
||||||
|
return json.encode(ret)
|
||||||
end
|
end
|
||||||
|
|
|
@ -5,9 +5,9 @@
|
||||||
local ClientPlayer = Player:subclass("ClientPlayer")
|
local ClientPlayer = Player:subclass("ClientPlayer")
|
||||||
|
|
||||||
function ClientPlayer:initialize(cp)
|
function ClientPlayer:initialize(cp)
|
||||||
self.player = cp
|
self.player = cp
|
||||||
self.handcardNum = 0
|
self.handcardNum = 0
|
||||||
self.known_cards = {}
|
self.known_cards = {}
|
||||||
end
|
end
|
||||||
|
|
||||||
return ClientPlayer
|
return ClientPlayer
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
---@field name string
|
---@field name string
|
||||||
---@field suit Suit
|
---@field suit Suit
|
||||||
---@field number integer
|
---@field number integer
|
||||||
|
---@field trueName string
|
||||||
---@field color Color
|
---@field color Color
|
||||||
---@field id integer
|
---@field id integer
|
||||||
---@field type CardType
|
---@field type CardType
|
||||||
|
@ -26,10 +27,9 @@ Card.NoColor = 3
|
||||||
|
|
||||||
---@alias CardType integer
|
---@alias CardType integer
|
||||||
|
|
||||||
Card.TypeSkill = 1
|
Card.TypeBasic = 1
|
||||||
Card.TypeBasic = 2
|
Card.TypeTrick = 2
|
||||||
Card.TypeTrick = 3
|
Card.TypeEquip = 3
|
||||||
Card.TypeEquip = 4
|
|
||||||
|
|
||||||
---@alias CardSubtype integer
|
---@alias CardSubtype integer
|
||||||
|
|
||||||
|
@ -54,39 +54,41 @@ Card.DiscardPile = 7
|
||||||
Card.Void = 8
|
Card.Void = 8
|
||||||
|
|
||||||
function Card:initialize(name, suit, number, color)
|
function Card:initialize(name, suit, number, color)
|
||||||
self.name = name
|
self.name = name
|
||||||
self.suit = suit or Card.NoSuit
|
self.suit = suit or Card.NoSuit
|
||||||
self.number = number or 0
|
self.number = number or 0
|
||||||
|
self.trueName = name
|
||||||
|
|
||||||
if suit == Card.Spade or suit == Card.Club then
|
if suit == Card.Spade or suit == Card.Club then
|
||||||
self.color = Card.Black
|
self.color = Card.Black
|
||||||
elseif suit == Card.Heart or suit == Card.Diamond then
|
elseif suit == Card.Heart or suit == Card.Diamond then
|
||||||
self.color = Card.Red
|
self.color = Card.Red
|
||||||
elseif color ~= nil then
|
elseif color ~= nil then
|
||||||
self.color = color
|
self.color = color
|
||||||
else
|
else
|
||||||
self.color = Card.NoColor
|
self.color = Card.NoColor
|
||||||
end
|
end
|
||||||
|
|
||||||
self.package = nil
|
self.package = nil
|
||||||
self.id = 0
|
self.id = 0
|
||||||
self.type = 0
|
self.type = 0
|
||||||
self.sub_type = Card.SubTypeNone
|
self.sub_type = Card.SubTypeNone
|
||||||
|
self.skill = nil
|
||||||
end
|
end
|
||||||
|
|
||||||
function Card:getSuitString()
|
function Card:getSuitString()
|
||||||
local suit = self.suit
|
local suit = self.suit
|
||||||
if suit == Card.Spade then
|
if suit == Card.Spade then
|
||||||
return "spade"
|
return "spade"
|
||||||
elseif suit == Card.Heart then
|
elseif suit == Card.Heart then
|
||||||
return "heart"
|
return "heart"
|
||||||
elseif suit == Card.Club then
|
elseif suit == Card.Club then
|
||||||
return "club"
|
return "club"
|
||||||
elseif suit == Card.Diamond then
|
elseif suit == Card.Diamond then
|
||||||
return "diamond"
|
return "diamond"
|
||||||
else
|
else
|
||||||
return "unknown"
|
return "unknown"
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
return Card
|
return Card
|
||||||
|
|
|
@ -2,16 +2,17 @@
|
||||||
local BasicCard = Card:subclass("BasicCard")
|
local BasicCard = Card:subclass("BasicCard")
|
||||||
|
|
||||||
function BasicCard:initialize(name, suit, number)
|
function BasicCard:initialize(name, suit, number)
|
||||||
Card.initialize(self, name, suit, number)
|
Card.initialize(self, name, suit, number)
|
||||||
self.type = Card.TypeBasic
|
self.type = Card.TypeBasic
|
||||||
end
|
end
|
||||||
|
|
||||||
---@param suit Suit
|
---@param suit Suit
|
||||||
---@param number integer
|
---@param number integer
|
||||||
---@return BasicCard
|
---@return BasicCard
|
||||||
function BasicCard:clone(suit, number)
|
function BasicCard:clone(suit, number)
|
||||||
local newCard = BasicCard:new(self.name, suit, number)
|
local newCard = BasicCard:new(self.name, suit, number)
|
||||||
return newCard
|
newCard.skill = self.skill
|
||||||
|
return newCard
|
||||||
end
|
end
|
||||||
|
|
||||||
return BasicCard
|
return BasicCard
|
||||||
|
|
|
@ -1,90 +1,97 @@
|
||||||
---@class EquipCard : Card
|
---@class EquipCard : Card
|
||||||
|
---@field equipSkill Skill
|
||||||
local EquipCard = Card:subclass("EquipCard")
|
local EquipCard = Card:subclass("EquipCard")
|
||||||
|
|
||||||
function EquipCard:initialize(name, suit, number)
|
function EquipCard:initialize(name, suit, number)
|
||||||
Card.initialize(self, name, suit, number)
|
Card.initialize(self, name, suit, number)
|
||||||
self.type = Card.TypeEquip
|
self.type = Card.TypeEquip
|
||||||
|
self.equipSkill = nil
|
||||||
end
|
end
|
||||||
|
|
||||||
---@class Weapon : EquipCard
|
---@class Weapon : EquipCard
|
||||||
local Weapon = EquipCard:subclass("Weapon")
|
local Weapon = EquipCard:subclass("Weapon")
|
||||||
|
|
||||||
function Weapon:initialize(name, suit, number, attackRange)
|
function Weapon:initialize(name, suit, number, attackRange)
|
||||||
EquipCard.initialize(self, name, suit, number)
|
EquipCard.initialize(self, name, suit, number)
|
||||||
self.sub_type = Card.SubtypeWeapon
|
self.sub_type = Card.SubtypeWeapon
|
||||||
self.attack_range = attackRange or 1
|
self.attack_range = attackRange or 1
|
||||||
end
|
end
|
||||||
|
|
||||||
---@param suit Suit
|
---@param suit Suit
|
||||||
---@param number integer
|
---@param number integer
|
||||||
---@return Weapon
|
---@return Weapon
|
||||||
function Weapon:clone(suit, number)
|
function Weapon:clone(suit, number)
|
||||||
local newCard = Weapon:new(self.name, suit, number, self.attack_range)
|
local newCard = Weapon:new(self.name, suit, number, self.attack_range)
|
||||||
return newCard
|
newCard.skill = self.skill
|
||||||
|
return newCard
|
||||||
end
|
end
|
||||||
|
|
||||||
---@class Armor : EquipCard
|
---@class Armor : EquipCard
|
||||||
local Armor = EquipCard:subclass("armor")
|
local Armor = EquipCard:subclass("armor")
|
||||||
|
|
||||||
function Armor:initialize(name, suit, number)
|
function Armor:initialize(name, suit, number)
|
||||||
EquipCard.initialize(self, name, suit, number)
|
EquipCard.initialize(self, name, suit, number)
|
||||||
self.sub_type = Card.SubtypeArmor
|
self.sub_type = Card.SubtypeArmor
|
||||||
end
|
end
|
||||||
|
|
||||||
---@param suit Suit
|
---@param suit Suit
|
||||||
---@param number integer
|
---@param number integer
|
||||||
---@return Armor
|
---@return Armor
|
||||||
function Armor:clone(suit, number)
|
function Armor:clone(suit, number)
|
||||||
local newCard = Armor:new(self.name, suit, number)
|
local newCard = Armor:new(self.name, suit, number)
|
||||||
return newCard
|
newCard.skill = self.skill
|
||||||
|
return newCard
|
||||||
end
|
end
|
||||||
|
|
||||||
---@class DefensiveRide : EquipCard
|
---@class DefensiveRide : EquipCard
|
||||||
local DefensiveRide = EquipCard:subclass("DefensiveRide")
|
local DefensiveRide = EquipCard:subclass("DefensiveRide")
|
||||||
|
|
||||||
function DefensiveRide:initialize(name, suit, number)
|
function DefensiveRide:initialize(name, suit, number)
|
||||||
EquipCard.initialize(self, name, suit, number)
|
EquipCard.initialize(self, name, suit, number)
|
||||||
self.sub_type = Card.SubtypeDefensiveRide
|
self.sub_type = Card.SubtypeDefensiveRide
|
||||||
end
|
end
|
||||||
|
|
||||||
---@param suit Suit
|
---@param suit Suit
|
||||||
---@param number integer
|
---@param number integer
|
||||||
---@return DefensiveRide
|
---@return DefensiveRide
|
||||||
function DefensiveRide:clone(suit, number)
|
function DefensiveRide:clone(suit, number)
|
||||||
local newCard = DefensiveRide:new(self.name, suit, number)
|
local newCard = DefensiveRide:new(self.name, suit, number)
|
||||||
return newCard
|
newCard.skill = self.skill
|
||||||
|
return newCard
|
||||||
end
|
end
|
||||||
|
|
||||||
---@class OffensiveRide : EquipCard
|
---@class OffensiveRide : EquipCard
|
||||||
local OffensiveRide = EquipCard:subclass("OffensiveRide")
|
local OffensiveRide = EquipCard:subclass("OffensiveRide")
|
||||||
|
|
||||||
function OffensiveRide:initialize(name, suit, number)
|
function OffensiveRide:initialize(name, suit, number)
|
||||||
EquipCard.initialize(self, name, suit, number)
|
EquipCard.initialize(self, name, suit, number)
|
||||||
self.sub_type = Card.SubtypeOffensiveRide
|
self.sub_type = Card.SubtypeOffensiveRide
|
||||||
end
|
end
|
||||||
|
|
||||||
---@param suit Suit
|
---@param suit Suit
|
||||||
---@param number integer
|
---@param number integer
|
||||||
---@return OffensiveRide
|
---@return OffensiveRide
|
||||||
function OffensiveRide:clone(suit, number)
|
function OffensiveRide:clone(suit, number)
|
||||||
local newCard = OffensiveRide:new(self.name, suit, number)
|
local newCard = OffensiveRide:new(self.name, suit, number)
|
||||||
return newCard
|
newCard.skill = self.skill
|
||||||
|
return newCard
|
||||||
end
|
end
|
||||||
|
|
||||||
---@class Treasure : EquipCard
|
---@class Treasure : EquipCard
|
||||||
local Treasure = EquipCard:subclass("Treasure")
|
local Treasure = EquipCard:subclass("Treasure")
|
||||||
|
|
||||||
function Treasure:initialize(name, suit, number)
|
function Treasure:initialize(name, suit, number)
|
||||||
EquipCard.initialize(self, name, suit, number)
|
EquipCard.initialize(self, name, suit, number)
|
||||||
self.sub_type = Card.SubtypeTreasure
|
self.sub_type = Card.SubtypeTreasure
|
||||||
end
|
end
|
||||||
|
|
||||||
---@param suit Suit
|
---@param suit Suit
|
||||||
---@param number integer
|
---@param number integer
|
||||||
---@return Treasure
|
---@return Treasure
|
||||||
function Treasure:clone(suit, number)
|
function Treasure:clone(suit, number)
|
||||||
local newCard = Treasure:new(self.name, suit, number)
|
local newCard = Treasure:new(self.name, suit, number)
|
||||||
return newCard
|
newCard.skill = self.skill
|
||||||
|
return newCard
|
||||||
end
|
end
|
||||||
|
|
||||||
return { EquipCard, Weapon, Armor, DefensiveRide, OffensiveRide, Treasure }
|
return { EquipCard, Weapon, Armor, DefensiveRide, OffensiveRide, Treasure }
|
||||||
|
|
|
@ -1,9 +0,0 @@
|
||||||
---@class SkillCard : Card
|
|
||||||
local SkillCard = Card:subclass("SkillCard")
|
|
||||||
|
|
||||||
function SkillCard:initialize(name)
|
|
||||||
Card.initialize(self, name, Card.NoSuit, 0)
|
|
||||||
self.type = Card.TypeSkill
|
|
||||||
end
|
|
||||||
|
|
||||||
return SkillCard
|
|
|
@ -2,32 +2,36 @@
|
||||||
local TrickCard = Card:subclass("TrickCard")
|
local TrickCard = Card:subclass("TrickCard")
|
||||||
|
|
||||||
function TrickCard:initialize(name, suit, number)
|
function TrickCard:initialize(name, suit, number)
|
||||||
Card.initialize(self, name, suit, number)
|
Card.initialize(self, name, suit, number)
|
||||||
self.type = Card.TypeTrick
|
self.type = Card.TypeTrick
|
||||||
end
|
end
|
||||||
|
|
||||||
---@param suit Suit
|
---@param suit Suit
|
||||||
---@param number integer
|
---@param number integer
|
||||||
---@return TrickCard
|
---@return TrickCard
|
||||||
function TrickCard:clone(suit, number)
|
function TrickCard:clone(suit, number)
|
||||||
local newCard = TrickCard:new(self.name, suit, number)
|
local newCard = TrickCard:new(self.name, suit, number)
|
||||||
return newCard
|
|
||||||
|
newCard.skill = self.skill
|
||||||
|
|
||||||
|
return newCard
|
||||||
end
|
end
|
||||||
|
|
||||||
---@class DelayedTrickCard : TrickCard
|
---@class DelayedTrickCard : TrickCard
|
||||||
local DelayedTrickCard = TrickCard:subclass("DelayedTrickCard")
|
local DelayedTrickCard = TrickCard:subclass("DelayedTrickCard")
|
||||||
|
|
||||||
function DelayedTrickCard:initialize(name, suit, number)
|
function DelayedTrickCard:initialize(name, suit, number)
|
||||||
TrickCard.initialize(self, name, suit, number)
|
TrickCard.initialize(self, name, suit, number)
|
||||||
self.sub_type = Card.SubtypeDelayedTrick
|
self.sub_type = Card.SubtypeDelayedTrick
|
||||||
end
|
end
|
||||||
|
|
||||||
---@param suit Suit
|
---@param suit Suit
|
||||||
---@param number integer
|
---@param number integer
|
||||||
---@return DelayedTrickCard
|
---@return DelayedTrickCard
|
||||||
function DelayedTrickCard:clone(suit, number)
|
function DelayedTrickCard:clone(suit, number)
|
||||||
local newCard = DelayedTrickCard:new(self.name, suit, number)
|
local newCard = DelayedTrickCard:new(self.name, suit, number)
|
||||||
return newCard
|
newCard.skill = self.skill
|
||||||
|
return newCard
|
||||||
end
|
end
|
||||||
|
|
||||||
return { TrickCard, DelayedTrickCard }
|
return { TrickCard, DelayedTrickCard }
|
||||||
|
|
|
@ -3,16 +3,16 @@ inspect = require "inspect"
|
||||||
|
|
||||||
DebugMode = true
|
DebugMode = true
|
||||||
function PrintWhenMethodCall()
|
function PrintWhenMethodCall()
|
||||||
local info = debug.getinfo(2)
|
local info = debug.getinfo(2)
|
||||||
local name = info.name
|
local name = info.name
|
||||||
local line = info.currentline
|
local line = info.currentline
|
||||||
local namewhat = info.namewhat
|
local namewhat = info.namewhat
|
||||||
local shortsrc = info.short_src
|
local shortsrc = info.short_src
|
||||||
if (namewhat == "method") and
|
if (namewhat == "method") and
|
||||||
(shortsrc ~= "[C]") and
|
(shortsrc ~= "[C]") and
|
||||||
(not string.find(shortsrc, "/lib")) then
|
(not string.find(shortsrc, "/lib")) then
|
||||||
print(shortsrc .. ":" .. line .. ": " .. name)
|
print(shortsrc .. ":" .. line .. ": " .. name)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
--debug.sethook(PrintWhenMethodCall, "c")
|
--debug.sethook(PrintWhenMethodCall, "c")
|
||||||
|
|
||||||
|
|
|
@ -11,126 +11,126 @@
|
||||||
local Engine = class("Engine")
|
local Engine = class("Engine")
|
||||||
|
|
||||||
function Engine:initialize()
|
function Engine:initialize()
|
||||||
-- Engine should be singleton
|
-- Engine should be singleton
|
||||||
if Fk ~= nil then
|
if Fk ~= nil then
|
||||||
error("Engine has been initialized")
|
error("Engine has been initialized")
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
Fk = self
|
Fk = self
|
||||||
|
|
||||||
self.packages = {} -- name --> Package
|
self.packages = {} -- name --> Package
|
||||||
self.package_names = {}
|
self.package_names = {}
|
||||||
self.skills = {} -- name --> Skill
|
self.skills = {} -- name --> Skill
|
||||||
self.related_skills = {} -- skillName --> relatedSkill[]
|
self.related_skills = {} -- skillName --> relatedSkill[]
|
||||||
self.global_trigger = {}
|
self.global_trigger = {}
|
||||||
self.generals = {} -- name --> General
|
self.generals = {} -- name --> General
|
||||||
self.lords = {} -- lordName[]
|
self.lords = {} -- lordName[]
|
||||||
self.cards = {} -- Card[]
|
self.cards = {} -- Card[]
|
||||||
self.translations = {} -- srcText --> translated
|
self.translations = {} -- srcText --> translated
|
||||||
|
|
||||||
self:loadPackages()
|
self:loadPackages()
|
||||||
end
|
end
|
||||||
|
|
||||||
---@param pack Package
|
---@param pack Package
|
||||||
function Engine:loadPackage(pack)
|
function Engine:loadPackage(pack)
|
||||||
assert(pack:isInstanceOf(Package))
|
assert(pack:isInstanceOf(Package))
|
||||||
if self.packages[pack.name] ~= nil then
|
if self.packages[pack.name] ~= nil then
|
||||||
error(string.format("Duplicate package %s detected", pack.name))
|
error(string.format("Duplicate package %s detected", pack.name))
|
||||||
end
|
end
|
||||||
self.packages[pack.name] = pack
|
self.packages[pack.name] = pack
|
||||||
table.insert(self.package_names, pack.name)
|
table.insert(self.package_names, pack.name)
|
||||||
|
|
||||||
-- add cards, generals and skills to Engine
|
-- add cards, generals and skills to Engine
|
||||||
if pack.type == Package.CardPack then
|
if pack.type == Package.CardPack then
|
||||||
self:addCards(pack.cards)
|
self:addCards(pack.cards)
|
||||||
elseif pack.type == Package.GeneralPack then
|
elseif pack.type == Package.GeneralPack then
|
||||||
self:addGenerals(pack.generals)
|
self:addGenerals(pack.generals)
|
||||||
end
|
end
|
||||||
self:addSkills(pack:getSkills())
|
self:addSkills(pack:getSkills())
|
||||||
end
|
end
|
||||||
|
|
||||||
function Engine:loadPackages()
|
function Engine:loadPackages()
|
||||||
local directories = FileIO.ls("packages")
|
local directories = FileIO.ls("packages")
|
||||||
|
|
||||||
-- load standard & standard_cards first
|
-- load standard & standard_cards first
|
||||||
self:loadPackage(require("packages.standard"))
|
self:loadPackage(require("packages.standard"))
|
||||||
self:loadPackage(require("packages.standard_cards"))
|
self:loadPackage(require("packages.standard_cards"))
|
||||||
table.removeOne(directories, "standard")
|
table.removeOne(directories, "standard")
|
||||||
table.removeOne(directories, "standard_cards")
|
table.removeOne(directories, "standard_cards")
|
||||||
|
|
||||||
for _, dir in ipairs(directories) do
|
for _, dir in ipairs(directories) do
|
||||||
if FileIO.isDir("packages/" .. dir) then
|
if FileIO.isDir("packages/" .. dir) then
|
||||||
local pack = require(string.format("packages.%s", dir))
|
local pack = require(string.format("packages.%s", dir))
|
||||||
-- Note that instance of Package is a table too
|
-- Note that instance of Package is a table too
|
||||||
-- so dont use type(pack) == "table" here
|
-- so dont use type(pack) == "table" here
|
||||||
if pack[1] ~= nil then
|
if pack[1] ~= nil then
|
||||||
for _, p in ipairs(pack) do
|
for _, p in ipairs(pack) do
|
||||||
self:loadPackage(p)
|
self:loadPackage(p)
|
||||||
end
|
|
||||||
else
|
|
||||||
self:loadPackage(pack)
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
else
|
||||||
|
self:loadPackage(pack)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
---@param t table
|
---@param t table
|
||||||
function Engine:loadTranslationTable(t)
|
function Engine:loadTranslationTable(t)
|
||||||
assert(type(t) == "table")
|
assert(type(t) == "table")
|
||||||
for k, v in pairs(t) do
|
for k, v in pairs(t) do
|
||||||
self.translations[k] = v
|
self.translations[k] = v
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
---@param skill Skill
|
---@param skill Skill
|
||||||
function Engine:addSkill(skill)
|
function Engine:addSkill(skill)
|
||||||
assert(skill.class:isSubclassOf(Skill))
|
assert(skill.class:isSubclassOf(Skill))
|
||||||
if self.skills[skill.name] ~= nil then
|
if self.skills[skill.name] ~= nil then
|
||||||
error(string.format("Duplicate skill %s detected", skill.name))
|
error(string.format("Duplicate skill %s detected", skill.name))
|
||||||
end
|
end
|
||||||
self.skills[skill.name] = skill
|
self.skills[skill.name] = skill
|
||||||
end
|
end
|
||||||
|
|
||||||
---@param skills Skill[]
|
---@param skills Skill[]
|
||||||
function Engine:addSkills(skills)
|
function Engine:addSkills(skills)
|
||||||
assert(type(skills) == "table")
|
assert(type(skills) == "table")
|
||||||
for _, skill in ipairs(skills) do
|
for _, skill in ipairs(skills) do
|
||||||
self:addSkill(skill)
|
self:addSkill(skill)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
---@param general General
|
---@param general General
|
||||||
function Engine:addGeneral(general)
|
function Engine:addGeneral(general)
|
||||||
assert(general:isInstanceOf(General))
|
assert(general:isInstanceOf(General))
|
||||||
if self.generals[general.name] ~= nil then
|
if self.generals[general.name] ~= nil then
|
||||||
error(string.format("Duplicate general %s detected", general.name))
|
error(string.format("Duplicate general %s detected", general.name))
|
||||||
end
|
end
|
||||||
self.generals[general.name] = general
|
self.generals[general.name] = general
|
||||||
end
|
end
|
||||||
|
|
||||||
---@param generals General[]
|
---@param generals General[]
|
||||||
function Engine:addGenerals(generals)
|
function Engine:addGenerals(generals)
|
||||||
assert(type(generals) == "table")
|
assert(type(generals) == "table")
|
||||||
for _, general in ipairs(generals) do
|
for _, general in ipairs(generals) do
|
||||||
self:addGeneral(general)
|
self:addGeneral(general)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
local cardId = 1
|
local cardId = 1
|
||||||
---@param card Card
|
---@param card Card
|
||||||
function Engine:addCard(card)
|
function Engine:addCard(card)
|
||||||
assert(card.class:isSubclassOf(Card))
|
assert(card.class:isSubclassOf(Card))
|
||||||
card.id = cardId
|
card.id = cardId
|
||||||
cardId = cardId + 1
|
cardId = cardId + 1
|
||||||
table.insert(self.cards, card)
|
table.insert(self.cards, card)
|
||||||
end
|
end
|
||||||
|
|
||||||
---@param cards Card[]
|
---@param cards Card[]
|
||||||
function Engine:addCards(cards)
|
function Engine:addCards(cards)
|
||||||
for _, card in ipairs(cards) do
|
for _, card in ipairs(cards) do
|
||||||
self:addCard(card)
|
self:addCard(card)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
---@param num integer
|
---@param num integer
|
||||||
|
@ -139,68 +139,68 @@ end
|
||||||
---@param filter function
|
---@param filter function
|
||||||
---@return General[]
|
---@return General[]
|
||||||
function Engine:getGeneralsRandomly(num, generalPool, except, filter)
|
function Engine:getGeneralsRandomly(num, generalPool, except, filter)
|
||||||
if filter then
|
if filter then
|
||||||
assert(type(filter) == "function")
|
assert(type(filter) == "function")
|
||||||
end
|
end
|
||||||
|
|
||||||
generalPool = generalPool or self.generals
|
generalPool = generalPool or self.generals
|
||||||
except = except or {}
|
except = except or {}
|
||||||
|
|
||||||
local availableGenerals = {}
|
local availableGenerals = {}
|
||||||
for _, general in pairs(generalPool) do
|
for _, general in pairs(generalPool) do
|
||||||
if not table.contains(except, general.name) and not (filter and filter(general)) then
|
if not table.contains(except, general.name) and not (filter and filter(general)) then
|
||||||
table.insert(availableGenerals, general)
|
table.insert(availableGenerals, general)
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if #availableGenerals == 0 then
|
||||||
|
return {}
|
||||||
|
end
|
||||||
|
|
||||||
|
local result = {}
|
||||||
|
for i = 1, num do
|
||||||
|
local randomGeneral = math.random(1, #availableGenerals)
|
||||||
|
table.insert(result, availableGenerals[randomGeneral])
|
||||||
|
table.remove(availableGenerals, randomGeneral)
|
||||||
|
|
||||||
if #availableGenerals == 0 then
|
if #availableGenerals == 0 then
|
||||||
return {}
|
break
|
||||||
end
|
end
|
||||||
|
end
|
||||||
|
|
||||||
local result = {}
|
return result
|
||||||
for i = 1, num do
|
|
||||||
local randomGeneral = math.random(1, #availableGenerals)
|
|
||||||
table.insert(result, availableGenerals[randomGeneral])
|
|
||||||
table.remove(availableGenerals, randomGeneral)
|
|
||||||
|
|
||||||
if #availableGenerals == 0 then
|
|
||||||
break
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
return result
|
|
||||||
end
|
end
|
||||||
|
|
||||||
---@param except General[]
|
---@param except General[]
|
||||||
---@return General[]
|
---@return General[]
|
||||||
function Engine:getAllGenerals(except)
|
function Engine:getAllGenerals(except)
|
||||||
local result = {}
|
local result = {}
|
||||||
for _, general in ipairs(self.generals) do
|
for _, general in ipairs(self.generals) do
|
||||||
if not (except and table.contains(except, general)) then
|
if not (except and table.contains(except, general)) then
|
||||||
table.insert(result, general)
|
table.insert(result, general)
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
end
|
||||||
|
|
||||||
return result
|
return result
|
||||||
end
|
end
|
||||||
|
|
||||||
---@param except integer[]
|
---@param except integer[]
|
||||||
---@return integer[]
|
---@return integer[]
|
||||||
function Engine:getAllCardIds(except)
|
function Engine:getAllCardIds(except)
|
||||||
local result = {}
|
local result = {}
|
||||||
for _, card in ipairs(self.cards) do
|
for _, card in ipairs(self.cards) do
|
||||||
if not (except and table.contains(except, card.id)) then
|
if not (except and table.contains(except, card.id)) then
|
||||||
table.insert(result, card.id)
|
table.insert(result, card.id)
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
end
|
||||||
|
|
||||||
return result
|
return result
|
||||||
end
|
end
|
||||||
|
|
||||||
---@param id integer
|
---@param id integer
|
||||||
---@return Card
|
---@return Card
|
||||||
function Engine:getCardById(id)
|
function Engine:getCardById(id)
|
||||||
return self.cards[id]
|
return self.cards[id]
|
||||||
end
|
end
|
||||||
|
|
||||||
return Engine
|
return Engine
|
||||||
|
|
|
@ -15,24 +15,24 @@ General.Male = 1
|
||||||
General.Female = 2
|
General.Female = 2
|
||||||
|
|
||||||
function General:initialize(package, name, kingdom, hp, maxHp, gender)
|
function General:initialize(package, name, kingdom, hp, maxHp, gender)
|
||||||
self.package = package
|
self.package = package
|
||||||
self.name = name
|
self.name = name
|
||||||
self.kingdom = kingdom
|
self.kingdom = kingdom
|
||||||
self.hp = hp
|
self.hp = hp
|
||||||
self.maxHp = maxHp or hp
|
self.maxHp = maxHp or hp
|
||||||
self.gender = gender or General.Male
|
self.gender = gender or General.Male
|
||||||
|
|
||||||
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
|
||||||
end
|
end
|
||||||
|
|
||||||
---@param skill Skill
|
---@param skill Skill
|
||||||
function General:addSkill(skill)
|
function General:addSkill(skill)
|
||||||
if (type(skill) == "string") then
|
if (type(skill) == "string") then
|
||||||
table.insert(self.other_skills, skill)
|
table.insert(self.other_skills, skill)
|
||||||
elseif (skill.class and skill.class:isSubclassOf(Skill)) then
|
elseif (skill.class and skill.class:isSubclassOf(Skill)) then
|
||||||
table.insert(self.skills, skill)
|
table.insert(self.skills, skill)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
return General
|
return General
|
||||||
|
|
|
@ -14,48 +14,48 @@ Package.CardPack = 2
|
||||||
Package.SpecialPack = 3
|
Package.SpecialPack = 3
|
||||||
|
|
||||||
function Package:initialize(name, _type)
|
function Package:initialize(name, _type)
|
||||||
assert(type(name) == "string")
|
assert(type(name) == "string")
|
||||||
assert(type(_type) == "nil" or type(_type) == "number")
|
assert(type(_type) == "nil" or type(_type) == "number")
|
||||||
self.name = name
|
self.name = name
|
||||||
self.type = _type or Package.GeneralPack
|
self.type = _type or Package.GeneralPack
|
||||||
|
|
||||||
self.generals = {}
|
self.generals = {}
|
||||||
self.extra_skills = {} -- skill not belongs to any generals, like "jixi"
|
self.extra_skills = {} -- skill not belongs to any generals, like "jixi"
|
||||||
self.related_skills = {}
|
self.related_skills = {}
|
||||||
self.cards = {}
|
self.cards = {}
|
||||||
end
|
end
|
||||||
|
|
||||||
---@return Skill[]
|
---@return Skill[]
|
||||||
function Package:getSkills()
|
function Package:getSkills()
|
||||||
local ret = {table.unpack(self.related_skills)}
|
local ret = {table.unpack(self.related_skills)}
|
||||||
if self.type == Package.GeneralPack then
|
if self.type == Package.GeneralPack then
|
||||||
for _, g in ipairs(self.generals) do
|
for _, g in ipairs(self.generals) do
|
||||||
for _, s in ipairs(g.skills) do
|
for _, s in ipairs(g.skills) do
|
||||||
table.insert(ret, s)
|
table.insert(ret, s)
|
||||||
end
|
end
|
||||||
end
|
|
||||||
end
|
end
|
||||||
return ret
|
end
|
||||||
|
return ret
|
||||||
end
|
end
|
||||||
|
|
||||||
---@param general General
|
---@param general General
|
||||||
function Package:addGeneral(general)
|
function Package:addGeneral(general)
|
||||||
assert(general.class and general:isInstanceOf(General))
|
assert(general.class and general:isInstanceOf(General))
|
||||||
table.insert(self.generals, general)
|
table.insert(self.generals, general)
|
||||||
end
|
end
|
||||||
|
|
||||||
---@param card Card
|
---@param card Card
|
||||||
function Package:addCard(card)
|
function Package:addCard(card)
|
||||||
assert(card.class and card:isInstanceOf(Card))
|
assert(card.class and card:isInstanceOf(Card))
|
||||||
card.package = self
|
card.package = self
|
||||||
table.insert(self.cards, card)
|
table.insert(self.cards, card)
|
||||||
end
|
end
|
||||||
|
|
||||||
---@param cards Card[]
|
---@param cards Card[]
|
||||||
function Package:addCards(cards)
|
function Package:addCards(cards)
|
||||||
for _, card in ipairs(cards) do
|
for _, card in ipairs(cards) do
|
||||||
self:addCard(card)
|
self:addCard(card)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
return Package
|
return Package
|
||||||
|
|
|
@ -19,6 +19,7 @@
|
||||||
---@field mark table<string, integer>
|
---@field mark table<string, integer>
|
||||||
---@field player_cards table<integer, integer[]>
|
---@field player_cards table<integer, integer[]>
|
||||||
---@field special_cards table<string, integer[]>
|
---@field special_cards table<string, integer[]>
|
||||||
|
---@field cardUsedHistory table<string, integer>
|
||||||
local Player = class("Player")
|
local Player = class("Player")
|
||||||
|
|
||||||
---@alias Phase integer
|
---@alias Phase integer
|
||||||
|
@ -41,184 +42,211 @@ Player.Judge = 3
|
||||||
Player.Special = 4
|
Player.Special = 4
|
||||||
|
|
||||||
function Player:initialize()
|
function Player:initialize()
|
||||||
self.id = 114514
|
self.id = 114514
|
||||||
self.hp = 0
|
self.hp = 0
|
||||||
self.maxHp = 0
|
self.maxHp = 0
|
||||||
self.kingdom = "qun"
|
self.kingdom = "qun"
|
||||||
self.role = ""
|
self.role = ""
|
||||||
self.general = ""
|
self.general = ""
|
||||||
self.seat = 0
|
self.seat = 0
|
||||||
self.phase = Player.PhaseNone
|
self.phase = Player.PhaseNone
|
||||||
self.faceup = true
|
self.faceup = true
|
||||||
self.chained = false
|
self.chained = false
|
||||||
self.dying = false
|
self.dying = false
|
||||||
self.dead = false
|
self.dead = false
|
||||||
self.state = ""
|
self.state = ""
|
||||||
|
|
||||||
self.player_skills = {}
|
self.player_skills = {}
|
||||||
self.flag = {}
|
self.flag = {}
|
||||||
self.tag = {}
|
self.tag = {}
|
||||||
self.mark = {}
|
self.mark = {}
|
||||||
self.player_cards = {
|
self.player_cards = {
|
||||||
[Player.Hand] = {},
|
[Player.Hand] = {},
|
||||||
[Player.Equip] = {},
|
[Player.Equip] = {},
|
||||||
[Player.Judge] = {},
|
[Player.Judge] = {},
|
||||||
}
|
}
|
||||||
self.special_cards = {}
|
self.special_cards = {}
|
||||||
|
|
||||||
|
self.cardUsedHistory = {}
|
||||||
end
|
end
|
||||||
|
|
||||||
---@param general General
|
---@param general General
|
||||||
---@param setHp boolean
|
---@param setHp boolean
|
||||||
---@param addSkills boolean
|
---@param addSkills boolean
|
||||||
function Player:setGeneral(general, setHp, addSkills)
|
function Player:setGeneral(general, setHp, addSkills)
|
||||||
self.general = general
|
self.general = general
|
||||||
if setHp then
|
if setHp then
|
||||||
self.maxHp = general.maxHp
|
self.maxHp = general.maxHp
|
||||||
self.hp = general.hp
|
self.hp = general.hp
|
||||||
end
|
end
|
||||||
|
|
||||||
if addSkills then
|
if addSkills then
|
||||||
table.insertTable(self.player_skills, general.skills)
|
table.insertTable(self.player_skills, general.skills)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
---@param flag string
|
---@param flag string
|
||||||
function Player:hasFlag(flag)
|
function Player:hasFlag(flag)
|
||||||
return table.contains(self.flag, flag)
|
return table.contains(self.flag, flag)
|
||||||
end
|
end
|
||||||
|
|
||||||
---@param flag string
|
---@param flag string
|
||||||
function Player:setFlag(flag)
|
function Player:setFlag(flag)
|
||||||
if flag == "." then
|
if flag == "." then
|
||||||
self:clearFlags()
|
self:clearFlags()
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
if flag:sub(1, 1) == "-" then
|
if flag:sub(1, 1) == "-" then
|
||||||
flag = flag:sub(2, #flag)
|
flag = flag:sub(2, #flag)
|
||||||
table.removeOne(self.flag, flag)
|
table.removeOne(self.flag, flag)
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
if not self:hasFlag(flag) then
|
if not self:hasFlag(flag) then
|
||||||
table.insert(self.flag, flag)
|
table.insert(self.flag, flag)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
function Player:clearFlags()
|
function Player:clearFlags()
|
||||||
self.flag = {}
|
self.flag = {}
|
||||||
end
|
end
|
||||||
|
|
||||||
function Player:addMark(mark, count)
|
function Player:addMark(mark, count)
|
||||||
count = count or 1
|
count = count or 1
|
||||||
local num = self.mark[mark]
|
local num = self.mark[mark]
|
||||||
num = num or 0
|
num = num or 0
|
||||||
self:setMark(mark, math.max(num + count, 0))
|
self:setMark(mark, math.max(num + count, 0))
|
||||||
end
|
end
|
||||||
|
|
||||||
function Player:removeMark(mark, count)
|
function Player:removeMark(mark, count)
|
||||||
count = count or 1
|
count = count or 1
|
||||||
local num = self.mark[mark]
|
local num = self.mark[mark]
|
||||||
num = num or 0
|
num = num or 0
|
||||||
self:setMark(mark, math.max(num - count, 0))
|
self:setMark(mark, math.max(num - count, 0))
|
||||||
end
|
end
|
||||||
|
|
||||||
function Player:setMark(mark, count)
|
function Player:setMark(mark, count)
|
||||||
if self.mark[mark] ~= count then
|
if self.mark[mark] ~= count then
|
||||||
self.mark[mark] = count
|
self.mark[mark] = count
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
function Player:getMark(mark)
|
function Player:getMark(mark)
|
||||||
return (self.mark[mark] or 0)
|
return (self.mark[mark] or 0)
|
||||||
end
|
end
|
||||||
|
|
||||||
function Player:getMarkNames()
|
function Player:getMarkNames()
|
||||||
local ret = {}
|
local ret = {}
|
||||||
for k, _ in pairs(self.mark) do
|
for k, _ in pairs(self.mark) do
|
||||||
table.insert(ret, k)
|
table.insert(ret, k)
|
||||||
end
|
end
|
||||||
return ret
|
return ret
|
||||||
end
|
end
|
||||||
|
|
||||||
---@param playerArea PlayerCardArea
|
---@param playerArea PlayerCardArea
|
||||||
---@param cardIds integer[]
|
---@param cardIds integer[]
|
||||||
---@param specialName string
|
---@param specialName string
|
||||||
function Player:addCards(playerArea, cardIds, specialName)
|
function Player:addCards(playerArea, cardIds, specialName)
|
||||||
assert(table.contains({ Player.Hand, Player.Equip, Player.Judge, Player.Special }, playerArea))
|
assert(table.contains({ Player.Hand, Player.Equip, Player.Judge, Player.Special }, playerArea))
|
||||||
assert(playerArea ~= Player.Special or type(specialName) == "string")
|
assert(playerArea ~= Player.Special or type(specialName) == "string")
|
||||||
|
|
||||||
if playerArea == Player.Special then
|
if playerArea == Player.Special then
|
||||||
self.special_cards[specialName] = self.special_cards[specialName] or {}
|
self.special_cards[specialName] = self.special_cards[specialName] or {}
|
||||||
table.insertTable(self.special_cards[specialName], cardIds)
|
table.insertTable(self.special_cards[specialName], cardIds)
|
||||||
else
|
else
|
||||||
table.insertTable(self.player_cards[playerArea], cardIds)
|
table.insertTable(self.player_cards[playerArea], cardIds)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
---@param playerArea PlayerCardArea
|
---@param playerArea PlayerCardArea
|
||||||
---@param cardIds integer[]
|
---@param cardIds integer[]
|
||||||
---@param specialName string
|
---@param specialName string
|
||||||
function Player:removeCards(playerArea, cardIds, specialName)
|
function Player:removeCards(playerArea, cardIds, specialName)
|
||||||
assert(table.contains({ Player.Hand, Player.Equip, Player.Judge, Player.Special }, playerArea))
|
assert(table.contains({ Player.Hand, Player.Equip, Player.Judge, Player.Special }, playerArea))
|
||||||
assert(playerArea ~= Player.Special or type(specialName) == "string")
|
assert(playerArea ~= Player.Special or type(specialName) == "string")
|
||||||
|
|
||||||
local fromAreaIds = playerArea == Player.Special and self.special_cards[specialName] or self.player_cards[playerArea]
|
local fromAreaIds = playerArea == Player.Special and self.special_cards[specialName] or self.player_cards[playerArea]
|
||||||
if fromAreaIds then
|
if fromAreaIds then
|
||||||
for _, id in ipairs(cardIds) do
|
for _, id in ipairs(cardIds) do
|
||||||
if #fromAreaIds == 0 then
|
if #fromAreaIds == 0 then
|
||||||
break
|
break
|
||||||
end
|
end
|
||||||
|
|
||||||
table.removeOne(fromAreaIds, id)
|
table.removeOne(fromAreaIds, id)
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
---@param playerAreas PlayerCardArea
|
---@param playerAreas PlayerCardArea
|
||||||
---@param specialName string
|
---@param specialName string
|
||||||
---@return integer[]
|
---@return integer[]
|
||||||
function Player:getCardIds(playerAreas, specialName)
|
function Player:getCardIds(playerAreas, specialName)
|
||||||
local rightAreas = { Player.Hand, Player.Equip, Player.Judge }
|
local rightAreas = { Player.Hand, Player.Equip, Player.Judge }
|
||||||
playerAreas = playerAreas or rightAreas
|
playerAreas = playerAreas or rightAreas
|
||||||
assert(type(playerAreas) == "number" or type(playerAreas) == "table")
|
assert(type(playerAreas) == "number" or type(playerAreas) == "table")
|
||||||
local areas = type(playerAreas) == "table" and playerAreas or { playerAreas }
|
local areas = type(playerAreas) == "table" and playerAreas or { playerAreas }
|
||||||
|
|
||||||
local rightAreas = { Player.Hand, Player.Equip, Player.Judge, Player.Special }
|
local rightAreas = { Player.Hand, Player.Equip, Player.Judge, Player.Special }
|
||||||
local cardIds = {}
|
local cardIds = {}
|
||||||
for _, area in ipairs(areas) do
|
for _, area in ipairs(areas) do
|
||||||
assert(table.contains(rightAreas, area))
|
assert(table.contains(rightAreas, area))
|
||||||
assert(area ~= Player.Special or type(specialName) == "string")
|
assert(area ~= Player.Special or type(specialName) == "string")
|
||||||
local currentCardIds = area == Player.Special and self.special_cards[specialName] or self.player_cards[area]
|
local currentCardIds = area == Player.Special and self.special_cards[specialName] or self.player_cards[area]
|
||||||
table.insertTable(cardIds, currentCardIds)
|
table.insertTable(cardIds, currentCardIds)
|
||||||
|
end
|
||||||
|
|
||||||
|
return cardIds
|
||||||
|
end
|
||||||
|
|
||||||
|
---@param cardSubtype CardSubtype
|
||||||
|
---@return integer|null
|
||||||
|
function Player:getEquipment(cardSubtype)
|
||||||
|
for _, cardId in ipairs(self.player_cards[Player.Equip]) do
|
||||||
|
if Fk:getCardById(cardId).sub_type == cardSubtype then
|
||||||
|
return cardId
|
||||||
end
|
end
|
||||||
|
end
|
||||||
|
|
||||||
return cardIds
|
return nil
|
||||||
end
|
end
|
||||||
|
|
||||||
function Player:getMaxCards()
|
function Player:getMaxCards()
|
||||||
local baseValue = math.max(self.hp, 0)
|
local baseValue = math.max(self.hp, 0)
|
||||||
|
|
||||||
return baseValue
|
return baseValue
|
||||||
end
|
end
|
||||||
|
|
||||||
---@param subtype CardSubtype
|
---@param subtype CardSubtype
|
||||||
---@return integer|null
|
---@return integer|null
|
||||||
function Player:getEquipBySubtype(subtype)
|
function Player:getEquipBySubtype(subtype)
|
||||||
local equipId = nil
|
local equipId = nil
|
||||||
for _, id in ipairs(self.player_cards[Player.Equip]) do
|
for _, id in ipairs(self.player_cards[Player.Equip]) do
|
||||||
if Fk.getCardById(id).sub_type == subtype then
|
if Fk:getCardById(id).sub_type == subtype then
|
||||||
equipId = id
|
equipId = id
|
||||||
break
|
break
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
end
|
||||||
|
|
||||||
return equipId
|
return equipId
|
||||||
end
|
end
|
||||||
|
|
||||||
function Player:getAttackRange()
|
function Player:getAttackRange()
|
||||||
local weapon = Fk.getCardById(self:getEquipBySubtype(Card.SubtypeWeapon))
|
local weapon = Fk:getCardById(self:getEquipBySubtype(Card.SubtypeWeapon))
|
||||||
local baseAttackRange = math.max(weapon and weapon.attack_range or 1, 0)
|
local baseAttackRange = math.max(weapon and weapon.attack_range or 1, 0)
|
||||||
|
|
||||||
return math.max(baseAttackRange, 0)
|
return math.max(baseAttackRange, 0)
|
||||||
|
end
|
||||||
|
|
||||||
|
function Player:addCardUseHistory(cardName, num)
|
||||||
|
assert(type(num) == "number" and num ~= 0)
|
||||||
|
|
||||||
|
self.cardUsedHistory[cardName] = self.cardUsedHistory[cardName] or 0
|
||||||
|
self.cardUsedHistory[cardName] = self.cardUsedHistory[cardName] + num
|
||||||
|
end
|
||||||
|
|
||||||
|
function Player:resetCardUseHistory(cardName)
|
||||||
|
if self.cardUsedHistory[cardName] then
|
||||||
|
self.cardUsedHistory[cardName] = 0
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
return Player
|
return Player
|
||||||
|
|
|
@ -13,10 +13,10 @@ Skill.Limited = 4
|
||||||
Skill.Wake = 5
|
Skill.Wake = 5
|
||||||
|
|
||||||
function Skill:initialize(name, frequency)
|
function Skill:initialize(name, frequency)
|
||||||
-- TODO: visible, lord, etc
|
-- TODO: visible, lord, etc
|
||||||
self.name = name
|
self.name = name
|
||||||
self.frequency = frequency
|
self.frequency = frequency
|
||||||
self.visible = true
|
self.visible = true
|
||||||
end
|
end
|
||||||
|
|
||||||
return Skill
|
return Skill
|
||||||
|
|
57
lua/core/skill_type/active_skill.lua
Normal file
57
lua/core/skill_type/active_skill.lua
Normal file
|
@ -0,0 +1,57 @@
|
||||||
|
--- ActiveSkill is a skill type like SkillCard+ViewAsSkill in QSanguosha
|
||||||
|
---
|
||||||
|
---@class ActiveSkill : Skill
|
||||||
|
local ActiveSkill = Skill:subclass("ActiveSkill")
|
||||||
|
|
||||||
|
function ActiveSkill:initialize(name)
|
||||||
|
Skill.initialize(self, name, Skill.NotFrequent)
|
||||||
|
end
|
||||||
|
|
||||||
|
---------
|
||||||
|
-- Note: these functions are used both client and ai
|
||||||
|
------- {
|
||||||
|
|
||||||
|
--- Determine whether the skill can be used in playing phase
|
||||||
|
---@param player Player
|
||||||
|
function ActiveSkill:canUse(player)
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Determine whether a card can be selected by this skill
|
||||||
|
--- only used in skill of players
|
||||||
|
---@param to_select integer @ id of a card not selected
|
||||||
|
---@param selected integer[] @ ids of selected cards
|
||||||
|
---@param selected_targets integer[] @ ids of selected players
|
||||||
|
function ActiveSkill:cardFilter(to_select, selected, selected_targets)
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Determine whether a target can be selected by this skill
|
||||||
|
--- only used in skill of players
|
||||||
|
---@param to_select integer @ id of the target
|
||||||
|
---@param selected integer[] @ ids of selected targets
|
||||||
|
---@param selected_cards integer[] @ ids of selected cards
|
||||||
|
function ActiveSkill:targetFilter(to_select, selected, selected_cards)
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Determine if selected cards and targets are valid for this skill
|
||||||
|
--- If returns true, the OK button should be enabled
|
||||||
|
--- only used in skill of players
|
||||||
|
---@param selected integer[] @ ids of selected cards
|
||||||
|
---@param selected_targets integer[] @ ids of selected players
|
||||||
|
function ActiveSkill:feasible(selected, selected_targets)
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
|
||||||
|
------- }
|
||||||
|
|
||||||
|
---@param room Room
|
||||||
|
---@param cardUseEvent CardUseStruct
|
||||||
|
function ActiveSkill:onUse(room, cardUseEvent) end
|
||||||
|
|
||||||
|
---@param room Room
|
||||||
|
---@param cardEffectEvent CardEffectEvent
|
||||||
|
function ActiveSkill:onEffect(room, cardEffectEvent) end
|
||||||
|
|
||||||
|
return ActiveSkill
|
|
@ -6,12 +6,12 @@
|
||||||
local TriggerSkill = Skill:subclass("TriggerSkill")
|
local TriggerSkill = Skill:subclass("TriggerSkill")
|
||||||
|
|
||||||
function TriggerSkill:initialize(name, frequency)
|
function TriggerSkill:initialize(name, frequency)
|
||||||
Skill.initialize(self, name, frequency)
|
Skill.initialize(self, name, frequency)
|
||||||
|
|
||||||
self.global = false
|
self.global = false
|
||||||
self.events = {}
|
self.events = {}
|
||||||
self.refresh_events = {}
|
self.refresh_events = {}
|
||||||
self.priority_table = {} -- GameEvent --> priority
|
self.priority_table = {} -- GameEvent --> priority
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Default functions
|
-- Default functions
|
||||||
|
@ -37,8 +37,8 @@ function TriggerSkill:refresh(event, target, player, data) end
|
||||||
---@param data any @ useful data of the event
|
---@param data any @ useful data of the event
|
||||||
---@return boolean
|
---@return boolean
|
||||||
function TriggerSkill:triggerable(event, target, player, data)
|
function TriggerSkill:triggerable(event, target, player, data)
|
||||||
return target and (target == player)
|
return target and (target == player)
|
||||||
and (self.global or (target:isAlive() and target:hasSkill(self)))
|
and (self.global or (target:isAlive() and target:hasSkill(self)))
|
||||||
end
|
end
|
||||||
|
|
||||||
---Trigger this skill
|
---Trigger this skill
|
||||||
|
@ -48,10 +48,10 @@ end
|
||||||
---@param data any @ useful data of the event
|
---@param data any @ useful data of the event
|
||||||
---@return boolean @ returns true if trigger is broken
|
---@return boolean @ returns true if trigger is broken
|
||||||
function TriggerSkill:trigger(event, target, player, data)
|
function TriggerSkill:trigger(event, target, player, data)
|
||||||
if player.room:askForSkillInvoke(player, self.name) then
|
if player.room:askForSkillInvoke(player, self.name) then
|
||||||
return self:use(event, target, player, data)
|
return self:use(event, target, player, data)
|
||||||
end
|
end
|
||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
|
|
||||||
---Use this skill
|
---Use this skill
|
||||||
|
|
|
@ -1,52 +1,52 @@
|
||||||
-- the iterator of QList object
|
-- the iterator of QList object
|
||||||
local qlist_iterator = function(list, n)
|
local qlist_iterator = function(list, n)
|
||||||
if n < list:length() - 1 then
|
if n < list:length() - 1 then
|
||||||
return n + 1, list:at(n + 1) -- the next element of list
|
return n + 1, list:at(n + 1) -- the next element of list
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
function fk.qlist(list)
|
function fk.qlist(list)
|
||||||
return qlist_iterator, list, -1
|
return qlist_iterator, list, -1
|
||||||
end
|
end
|
||||||
|
|
||||||
function table:contains(element)
|
function table:contains(element)
|
||||||
if #self == 0 or type(self[1]) ~= type(element) then return false end
|
if #self == 0 or type(self[1]) ~= type(element) then return false end
|
||||||
for _, e in ipairs(self) do
|
for _, e in ipairs(self) do
|
||||||
if e == element then return true end
|
if e == element then return true end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
function table:shuffle()
|
function table:shuffle()
|
||||||
for i = #self, 2, -1 do
|
for i = #self, 2, -1 do
|
||||||
local j = math.random(i)
|
local j = math.random(i)
|
||||||
self[i], self[j] = self[j], self[i]
|
self[i], self[j] = self[j], self[i]
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
function table:insertTable(list)
|
function table:insertTable(list)
|
||||||
for _, e in ipairs(list) do
|
for _, e in ipairs(list) do
|
||||||
table.insert(self, e)
|
table.insert(self, e)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
function table:indexOf(value, from)
|
function table:indexOf(value, from)
|
||||||
from = from or 1
|
from = from or 1
|
||||||
for i = from, #self do
|
for i = from, #self do
|
||||||
if self[i] == value then return i end
|
if self[i] == value then return i end
|
||||||
end
|
end
|
||||||
return -1
|
return -1
|
||||||
end
|
end
|
||||||
|
|
||||||
function table:removeOne(element)
|
function table:removeOne(element)
|
||||||
if #self == 0 or type(self[1]) ~= type(element) then return false end
|
if #self == 0 or type(self[1]) ~= type(element) then return false end
|
||||||
|
|
||||||
for i = 1, #self do
|
for i = 1, #self do
|
||||||
if self[i] == element then
|
if self[i] == element then
|
||||||
table.remove(self, i)
|
table.remove(self, i)
|
||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Note: only clone key and value, no metatable
|
-- Note: only clone key and value, no metatable
|
||||||
|
@ -55,57 +55,57 @@ end
|
||||||
---@param self T
|
---@param self T
|
||||||
---@return T
|
---@return T
|
||||||
function table.clone(self)
|
function table.clone(self)
|
||||||
local ret = {}
|
local ret = {}
|
||||||
for k, v in pairs(self) do
|
for k, v in pairs(self) do
|
||||||
if type(v) == "table" then
|
if type(v) == "table" then
|
||||||
ret[k] = table.clone(v)
|
ret[k] = table.clone(v)
|
||||||
else
|
else
|
||||||
ret[k] = v
|
ret[k] = v
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
return ret
|
return ret
|
||||||
end
|
end
|
||||||
|
|
||||||
---@class Sql
|
---@class Sql
|
||||||
Sql = {
|
Sql = {
|
||||||
---@param filename string
|
---@param filename string
|
||||||
open = function(filename)
|
open = function(filename)
|
||||||
return fk.OpenDatabase(filename)
|
return fk.OpenDatabase(filename)
|
||||||
end,
|
end,
|
||||||
|
|
||||||
---@param db fk.SQLite3
|
---@param db fk.SQLite3
|
||||||
close = function(db)
|
close = function(db)
|
||||||
fk.CloseDatabase(db)
|
fk.CloseDatabase(db)
|
||||||
end,
|
end,
|
||||||
|
|
||||||
--- Execute an SQL statement.
|
--- Execute an SQL statement.
|
||||||
---@param db fk.SQLite3
|
---@param db fk.SQLite3
|
||||||
---@param sql string
|
---@param sql string
|
||||||
exec = function(db, sql)
|
exec = function(db, sql)
|
||||||
fk.ExecSQL(db, sql)
|
fk.ExecSQL(db, sql)
|
||||||
end,
|
end,
|
||||||
|
|
||||||
--- Execute a `SELECT` SQL statement.
|
--- Execute a `SELECT` SQL statement.
|
||||||
---@param db fk.SQLite3
|
---@param db fk.SQLite3
|
||||||
---@param sql string
|
---@param sql string
|
||||||
---@return table @ { [columnName] --> result : string[] }
|
---@return table @ { [columnName] --> result : string[] }
|
||||||
exec_select = function(db, sql)
|
exec_select = function(db, sql)
|
||||||
return json.decode(fk.SelectFromDb(db, sql))
|
return json.decode(fk.SelectFromDb(db, sql))
|
||||||
end,
|
end,
|
||||||
}
|
}
|
||||||
|
|
||||||
FileIO = {
|
FileIO = {
|
||||||
pwd = fk.QmlBackend_pwd,
|
pwd = fk.QmlBackend_pwd,
|
||||||
ls = function(filename)
|
ls = function(filename)
|
||||||
if filename == nil then
|
if filename == nil then
|
||||||
return fk.QmlBackend_ls(".")
|
return fk.QmlBackend_ls(".")
|
||||||
else
|
else
|
||||||
return fk.QmlBackend_ls(filename)
|
return fk.QmlBackend_ls(filename)
|
||||||
end
|
end
|
||||||
end,
|
end,
|
||||||
cd = fk.QmlBackend_cd,
|
cd = fk.QmlBackend_cd,
|
||||||
exists = fk.QmlBackend_exists,
|
exists = fk.QmlBackend_exists,
|
||||||
isDir = fk.QmlBackend_isDir
|
isDir = fk.QmlBackend_isDir
|
||||||
}
|
}
|
||||||
|
|
||||||
os.getms = fk.GetMicroSecond
|
os.getms = fk.GetMicroSecond
|
||||||
|
@ -113,23 +113,23 @@ os.getms = fk.GetMicroSecond
|
||||||
---@class Stack : Object
|
---@class Stack : Object
|
||||||
Stack = class("Stack")
|
Stack = class("Stack")
|
||||||
function Stack:initialize()
|
function Stack:initialize()
|
||||||
self.t = {}
|
self.t = {}
|
||||||
self.p = 0
|
self.p = 0
|
||||||
end
|
end
|
||||||
|
|
||||||
function Stack:push(e)
|
function Stack:push(e)
|
||||||
self.p = self.p + 1
|
self.p = self.p + 1
|
||||||
self.t[self.p] = e
|
self.t[self.p] = e
|
||||||
end
|
end
|
||||||
|
|
||||||
function Stack:isEmpty()
|
function Stack:isEmpty()
|
||||||
return self.p == 0
|
return self.p == 0
|
||||||
end
|
end
|
||||||
|
|
||||||
function Stack:pop()
|
function Stack:pop()
|
||||||
if self.p == 0 then return nil end
|
if self.p == 0 then return nil end
|
||||||
self.p = self.p - 1
|
self.p = self.p - 1
|
||||||
return self.t[self.p + 1]
|
return self.t[self.p + 1]
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
@ -139,15 +139,156 @@ end
|
||||||
---@param table string
|
---@param table string
|
||||||
---@param enum string[]
|
---@param enum string[]
|
||||||
function CreateEnum(table, enum)
|
function CreateEnum(table, enum)
|
||||||
local enum_format = "%s.%s = %d"
|
local enum_format = "%s.%s = %d"
|
||||||
for i, v in ipairs(enum) do
|
for i, v in ipairs(enum) do
|
||||||
print(string.format(enum_format, table, v, i))
|
print(string.format(enum_format, table, v, i))
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
function switch(param, case_table)
|
function switch(param, case_table)
|
||||||
local case = case_table[param]
|
local case = case_table[param]
|
||||||
if case then return case() end
|
if case then return case() end
|
||||||
local def = case_table["default"]
|
local def = case_table["default"]
|
||||||
return def and def() or nil
|
return def and def() or nil
|
||||||
end
|
end
|
||||||
|
|
||||||
|
---@class TargetGroup : Object
|
||||||
|
local TargetGroup = class("TargetGroup")
|
||||||
|
|
||||||
|
function TargetGroup.static:getRealTargets(targetGroup)
|
||||||
|
if not targetGroup then
|
||||||
|
return {}
|
||||||
|
end
|
||||||
|
|
||||||
|
local realTargets = {}
|
||||||
|
for _, targets in ipairs(targetGroup) do
|
||||||
|
table.insert(realTargets, targets[1])
|
||||||
|
end
|
||||||
|
|
||||||
|
return realTargets
|
||||||
|
end
|
||||||
|
|
||||||
|
function TargetGroup.static:includeRealTargets(targetGroup, playerId)
|
||||||
|
if not targetGroup then
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
for _, targets in ipairs(targetGroup) do
|
||||||
|
if targets[1] == playerId then
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
function TargetGroup.static:removeTarget(targetGroup, playerId)
|
||||||
|
if not targetGroup then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
for index, targets in ipairs(targetGroup) do
|
||||||
|
if (targets[1] == playerId) then
|
||||||
|
table.remove(targetGroup, index)
|
||||||
|
return
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function TargetGroup.static:pushTargets(targetGroup, playerIds)
|
||||||
|
if not targetGroup then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
if type(playerIds) == "table" then
|
||||||
|
table.insert(targetGroup, playerIds)
|
||||||
|
elseif type(playerIds) == "number" then
|
||||||
|
table.insert(targetGroup, { playerIds })
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
---@class AimGroup : Object
|
||||||
|
local AimGroup = class("AimGroup")
|
||||||
|
|
||||||
|
AimGroup.Undone = 1
|
||||||
|
AimGroup.Done = 2
|
||||||
|
AimGroup.Cancelled = 3
|
||||||
|
|
||||||
|
function AimGroup.static:initAimGroup(playerIds)
|
||||||
|
return { [AimGroup.Undone] = playerIds, [AimGroup.Done] = {}, [AimGroup.Cancelled] = {} }
|
||||||
|
end
|
||||||
|
|
||||||
|
function AimGroup.static:getAllTargets(aimGroup)
|
||||||
|
local targets = {}
|
||||||
|
table.insertTable(targets, aimGroup[AimGroup.Undone])
|
||||||
|
table.insertTable(targets, aimGroup[AimGroup.Done])
|
||||||
|
return targets
|
||||||
|
end
|
||||||
|
|
||||||
|
function AimGroup.static:getUndoneOrDoneTargets(aimGroup, done)
|
||||||
|
return done and aimGroup[AimGroup.Done] or aimGroup[AimGroup.Undone]
|
||||||
|
end
|
||||||
|
|
||||||
|
function AimGroup.static:setTargetDone(aimGroup, playerId)
|
||||||
|
local index = table.indexOf(aimGroup[AimGroup.Undone], playerId)
|
||||||
|
if index ~= -1 then
|
||||||
|
table.remove(aimGroup[AimGroup.Undone], index)
|
||||||
|
table.insert(aimGroup[AimGroup.Done], playerId)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function AimGroup.static:addTargets(room, aimEvent, playerIds)
|
||||||
|
local playerId = type(playerIds) == "table" and playerIds[1] or playerIds
|
||||||
|
table.insert(aimEvent.tos[AimGroup.Undone], playerId)
|
||||||
|
room:sortPlayersByAction(aimEvent.tos[AimGroup.Undone])
|
||||||
|
if aimEvent.targetGroup then
|
||||||
|
TargetGroup:pushTargets(aimEvent.targetGroup, playerIds)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function AimGroup.static:cancelTarget(aimEvent, playerId)
|
||||||
|
local cancelled = false
|
||||||
|
for status = AimGroup.Undone, AimGroup.Done do
|
||||||
|
local indexList = {}
|
||||||
|
for index, pId in ipairs(aimEvent.tos[status]) do
|
||||||
|
if pId == playerId then
|
||||||
|
table.insert(indexList, index)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if #indexList > 0 then
|
||||||
|
cancelled = true
|
||||||
|
for i = 1, #indexList do
|
||||||
|
table.remove(aimEvent.tos[status], indexList[i])
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if cancelled then
|
||||||
|
table.insert(aimEvent.tos[AimGroup.Cancelled], playerId)
|
||||||
|
if aimEvent.targetGroup then
|
||||||
|
TargetGroup:removeTarget(aimEvent.targetGroup, playerId)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function AimGroup.static:removeDeadTargets(room, aimEvent)
|
||||||
|
for index = AimGroup.Undone, AimGroup.Done do
|
||||||
|
aimEvent.tos[index] = room:deadPlayerFilter(aimEvent.tos[index])
|
||||||
|
end
|
||||||
|
|
||||||
|
if aimEvent.targetGroup then
|
||||||
|
local targets = TargetGroup:getRealTargets(aimEvent.targetGroup)
|
||||||
|
for _, target in ipairs(targets) do
|
||||||
|
if not room:getPlayerById(target):isAlive() then
|
||||||
|
TargetGroup:removeTarget(aimEvent.targetGroup, target)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function AimGroup.static:getCancelledTargets(aimGroup)
|
||||||
|
return aimGroup[AimGroup.Cancelled]
|
||||||
|
end
|
||||||
|
|
||||||
|
return { TargetGroup, AimGroup }
|
||||||
|
|
286
lua/fk_ex.lua
286
lua/fk_ex.lua
|
@ -1,18 +1,16 @@
|
||||||
-- load types for extension
|
-- load types for extension
|
||||||
|
|
||||||
SkillCard = require "core.card_type.skill"
|
dofile "lua/server/event.lua"
|
||||||
|
dofile "lua/server/system_enum.lua"
|
||||||
|
TriggerSkill = require "core.skill_type.trigger"
|
||||||
|
ActiveSkill = require "core.skill_type.active_skill"
|
||||||
|
|
||||||
BasicCard = require "core.card_type.basic"
|
BasicCard = require "core.card_type.basic"
|
||||||
local Trick = require "core.card_type.trick"
|
local Trick = require "core.card_type.trick"
|
||||||
TrickCard, DelayedTrickCard = table.unpack(Trick)
|
TrickCard, DelayedTrickCard = table.unpack(Trick)
|
||||||
local Equip = require "core.card_type.equip"
|
local Equip = require "core.card_type.equip"
|
||||||
_, Weapon, Armor, DefensiveRide, OffensiveRide, Treasure = table.unpack(Equip)
|
_, Weapon, Armor, DefensiveRide, OffensiveRide, Treasure = table.unpack(Equip)
|
||||||
|
|
||||||
dofile "lua/server/event.lua"
|
|
||||||
dofile "lua/server/system_enum.lua"
|
|
||||||
TriggerSkill = require "core.skill_type.trigger"
|
|
||||||
|
|
||||||
---@class CardSpec: Card
|
|
||||||
|
|
||||||
---@class SkillSpec: Skill
|
---@class SkillSpec: Skill
|
||||||
|
|
||||||
---@alias TrigFunc fun(self: TriggerSkill, event: Event, target: ServerPlayer, player: ServerPlayer):boolean
|
---@alias TrigFunc fun(self: TriggerSkill, event: Event, target: ServerPlayer, player: ServerPlayer):boolean
|
||||||
|
@ -26,166 +24,208 @@ TriggerSkill = require "core.skill_type.trigger"
|
||||||
---@field on_refresh TrigFunc
|
---@field on_refresh TrigFunc
|
||||||
---@field can_refresh TrigFunc
|
---@field can_refresh TrigFunc
|
||||||
|
|
||||||
|
---@param spec TriggerSkillSpec
|
||||||
|
---@return TriggerSkill
|
||||||
|
function fk.CreateTriggerSkill(spec)
|
||||||
|
assert(type(spec.name) == "string")
|
||||||
|
--assert(type(spec.on_trigger) == "function")
|
||||||
|
if spec.frequency then assert(type(spec.frequency) == "number") end
|
||||||
|
|
||||||
|
local frequency = spec.frequency or Skill.NotFrequent
|
||||||
|
local skill = TriggerSkill:new(spec.name, frequency)
|
||||||
|
|
||||||
|
if type(spec.events) == "number" then
|
||||||
|
table.insert(skill.events, spec.events)
|
||||||
|
elseif type(spec.events) == "table" then
|
||||||
|
table.insertTable(skill.events, spec.events)
|
||||||
|
end
|
||||||
|
|
||||||
|
if type(spec.refresh_events) == "number" then
|
||||||
|
table.insert(skill.refresh_events, spec.refresh_events)
|
||||||
|
elseif type(spec.refresh_events) == "table" then
|
||||||
|
table.insertTable(skill.refresh_events, spec.refresh_events)
|
||||||
|
end
|
||||||
|
|
||||||
|
if type(spec.global) == "boolean" then skill.global = spec.global end
|
||||||
|
|
||||||
|
if spec.on_trigger then skill.trigger = spec.on_trigger end
|
||||||
|
|
||||||
|
if spec.can_trigger then
|
||||||
|
skill.triggerable = spec.can_trigger
|
||||||
|
end
|
||||||
|
|
||||||
|
if spec.can_refresh then
|
||||||
|
skill.canRefresh = spec.can_refresh
|
||||||
|
end
|
||||||
|
|
||||||
|
if spec.on_refresh then
|
||||||
|
skill.refresh = spec.on_refresh
|
||||||
|
end
|
||||||
|
|
||||||
|
if not spec.priority then
|
||||||
|
if frequency == Skill.Wake then
|
||||||
|
spec.priority = 3
|
||||||
|
elseif frequency == Skill.Compulsory then
|
||||||
|
spec.priority = 2
|
||||||
|
else
|
||||||
|
spec.priority = 1
|
||||||
|
end
|
||||||
|
end
|
||||||
|
if type(spec.priority) == "number" then
|
||||||
|
for _, event in ipairs(skill.events) do
|
||||||
|
skill.priority_table[event] = spec.priority
|
||||||
|
end
|
||||||
|
elseif type(spec.priority) == "table" then
|
||||||
|
for event, priority in pairs(spec.priority) do
|
||||||
|
skill.priority_table[event] = priority
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return skill
|
||||||
|
end
|
||||||
|
|
||||||
|
---@class ActiveSkillSpec: SkillSpec
|
||||||
|
---@field can_use fun(self: ActiveSkill, player: Player): boolean
|
||||||
|
---@field card_filter fun(self: ActiveSkill, to_select: integer, selected: integer[], selected_targets: integer[]): boolean
|
||||||
|
---@field target_filter fun(self: ActiveSkill, to_select: integer, selected: integer[], selected_cards: integer[]): boolean
|
||||||
|
---@field feasible fun(self: ActiveSkill, selected: integer[], selected_targets: integer[]): boolean
|
||||||
|
---@field on_use fun(self: ActiveSkill, room: Room, cardUseEvent: CardUseStruct): boolean
|
||||||
|
---@field on_effect fun(self: ActiveSkill, room: Room, cardEffectEvent: CardEffectEvent): boolean
|
||||||
|
|
||||||
|
---@param spec ActiveSkillSpec
|
||||||
|
---@return ActiveSkill
|
||||||
|
function fk.CreateActiveSkill(spec)
|
||||||
|
assert(type(spec.name) == "string")
|
||||||
|
local skill = ActiveSkill:new(spec.name)
|
||||||
|
if spec.can_use then skill.canUse = spec.can_use 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.feasible then skill.feasible = spec.feasible end
|
||||||
|
if spec.on_use then skill.onUse = spec.on_use end
|
||||||
|
if spec.on_effect then skill.onEffect = spec.on_effect end
|
||||||
|
return skill
|
||||||
|
end
|
||||||
|
|
||||||
|
---@class CardSpec: Card
|
||||||
|
---@field skill Skill
|
||||||
|
|
||||||
|
local defaultCardSkill = fk.CreateActiveSkill{
|
||||||
|
name = "default_card_skill",
|
||||||
|
on_use = function(self, room, use)
|
||||||
|
if not use.tos or #TargetGroup:getRealTargets(use.tos) == 0 then
|
||||||
|
use.tos = { { use.from } }
|
||||||
|
end
|
||||||
|
end
|
||||||
|
}
|
||||||
|
|
||||||
---@param spec CardSpec
|
---@param spec CardSpec
|
||||||
---@return BasicCard
|
---@return BasicCard
|
||||||
function fk.CreateBasicCard(spec)
|
function fk.CreateBasicCard(spec)
|
||||||
assert(type(spec.name) == "string" or type(spec.class_name) == "string")
|
assert(type(spec.name) == "string" or type(spec.class_name) == "string")
|
||||||
if not spec.name then spec.name = spec.class_name
|
if not spec.name then spec.name = spec.class_name
|
||||||
elseif not spec.class_name then spec.class_name = spec.name end
|
elseif not spec.class_name then spec.class_name = spec.name end
|
||||||
if spec.suit then assert(type(spec.suit) == "number") end
|
if spec.suit then assert(type(spec.suit) == "number") end
|
||||||
if spec.number then assert(type(spec.number) == "number") end
|
if spec.number then assert(type(spec.number) == "number") end
|
||||||
|
|
||||||
local card = BasicCard:new(spec.name, spec.suit, spec.number)
|
local card = BasicCard:new(spec.name, spec.suit, spec.number)
|
||||||
return card
|
card.skill = spec.skill or defaultCardSkill
|
||||||
|
return card
|
||||||
end
|
end
|
||||||
|
|
||||||
---@param spec CardSpec
|
---@param spec CardSpec
|
||||||
---@return TrickCard
|
---@return TrickCard
|
||||||
function fk.CreateTrickCard(spec)
|
function fk.CreateTrickCard(spec)
|
||||||
assert(type(spec.name) == "string" or type(spec.class_name) == "string")
|
assert(type(spec.name) == "string" or type(spec.class_name) == "string")
|
||||||
if not spec.name then spec.name = spec.class_name
|
if not spec.name then spec.name = spec.class_name
|
||||||
elseif not spec.class_name then spec.class_name = spec.name end
|
elseif not spec.class_name then spec.class_name = spec.name end
|
||||||
if spec.suit then assert(type(spec.suit) == "number") end
|
if spec.suit then assert(type(spec.suit) == "number") end
|
||||||
if spec.number then assert(type(spec.number) == "number") end
|
if spec.number then assert(type(spec.number) == "number") end
|
||||||
|
|
||||||
local card = TrickCard:new(spec.name, spec.suit, spec.number)
|
local card = TrickCard:new(spec.name, spec.suit, spec.number)
|
||||||
return card
|
card.skill = spec.skill or defaultCardSkill
|
||||||
|
return card
|
||||||
end
|
end
|
||||||
|
|
||||||
---@param spec CardSpec
|
---@param spec CardSpec
|
||||||
---@return DelayedTrickCard
|
---@return DelayedTrickCard
|
||||||
function fk.CreateDelayedTrickCard(spec)
|
function fk.CreateDelayedTrickCard(spec)
|
||||||
assert(type(spec.name) == "string" or type(spec.class_name) == "string")
|
assert(type(spec.name) == "string" or type(spec.class_name) == "string")
|
||||||
if not spec.name then spec.name = spec.class_name
|
if not spec.name then spec.name = spec.class_name
|
||||||
elseif not spec.class_name then spec.class_name = spec.name end
|
elseif not spec.class_name then spec.class_name = spec.name end
|
||||||
if spec.suit then assert(type(spec.suit) == "number") end
|
if spec.suit then assert(type(spec.suit) == "number") end
|
||||||
if spec.number then assert(type(spec.number) == "number") end
|
if spec.number then assert(type(spec.number) == "number") end
|
||||||
|
|
||||||
local card = DelayedTrickCard:new(spec.name, spec.suit, spec.number)
|
local card = DelayedTrickCard:new(spec.name, spec.suit, spec.number)
|
||||||
return card
|
card.skill = spec.skill or defaultCardSkill
|
||||||
|
return card
|
||||||
end
|
end
|
||||||
|
|
||||||
---@param spec CardSpec
|
---@param spec CardSpec
|
||||||
---@return Weapon
|
---@return Weapon
|
||||||
function fk.CreateWeapon(spec)
|
function fk.CreateWeapon(spec)
|
||||||
assert(type(spec.name) == "string" or type(spec.class_name) == "string")
|
assert(type(spec.name) == "string" or type(spec.class_name) == "string")
|
||||||
if not spec.name then spec.name = spec.class_name
|
if not spec.name then spec.name = spec.class_name
|
||||||
elseif not spec.class_name then spec.class_name = spec.name end
|
elseif not spec.class_name then spec.class_name = spec.name end
|
||||||
if spec.suit then assert(type(spec.suit) == "number") end
|
if spec.suit then assert(type(spec.suit) == "number") end
|
||||||
if spec.number then assert(type(spec.number) == "number") end
|
if spec.number then assert(type(spec.number) == "number") end
|
||||||
if spec.attack_range then assert(type(spec.attack_range) == "number" and spec.attack_range >= 0) end
|
if spec.attack_range then assert(type(spec.attack_range) == "number" and spec.attack_range >= 0) end
|
||||||
|
|
||||||
local card = Weapon:new(spec.name, spec.suit, spec.number, spec.attack_range)
|
local card = Weapon:new(spec.name, spec.suit, spec.number, spec.attack_range)
|
||||||
return card
|
card.skill = spec.skill or defaultCardSkill
|
||||||
|
return card
|
||||||
end
|
end
|
||||||
|
|
||||||
---@param spec CardSpec
|
---@param spec CardSpec
|
||||||
---@return Armor
|
---@return Armor
|
||||||
function fk.CreateArmor(spec)
|
function fk.CreateArmor(spec)
|
||||||
assert(type(spec.name) == "string" or type(spec.class_name) == "string")
|
assert(type(spec.name) == "string" or type(spec.class_name) == "string")
|
||||||
if not spec.name then spec.name = spec.class_name
|
if not spec.name then spec.name = spec.class_name
|
||||||
elseif not spec.class_name then spec.class_name = spec.name end
|
elseif not spec.class_name then spec.class_name = spec.name end
|
||||||
if spec.suit then assert(type(spec.suit) == "number") end
|
if spec.suit then assert(type(spec.suit) == "number") end
|
||||||
if spec.number then assert(type(spec.number) == "number") end
|
if spec.number then assert(type(spec.number) == "number") end
|
||||||
|
|
||||||
local card = Armor:new(spec.name, spec.suit, spec.number)
|
local card = Armor:new(spec.name, spec.suit, spec.number)
|
||||||
return card
|
card.skill = spec.skill or defaultCardSkill
|
||||||
|
return card
|
||||||
end
|
end
|
||||||
|
|
||||||
---@param spec CardSpec
|
---@param spec CardSpec
|
||||||
---@return DefensiveRide
|
---@return DefensiveRide
|
||||||
function fk.CreateDefensiveRide(spec)
|
function fk.CreateDefensiveRide(spec)
|
||||||
assert(type(spec.name) == "string" or type(spec.class_name) == "string")
|
assert(type(spec.name) == "string" or type(spec.class_name) == "string")
|
||||||
if not spec.name then spec.name = spec.class_name
|
if not spec.name then spec.name = spec.class_name
|
||||||
elseif not spec.class_name then spec.class_name = spec.name end
|
elseif not spec.class_name then spec.class_name = spec.name end
|
||||||
if spec.suit then assert(type(spec.suit) == "number") end
|
if spec.suit then assert(type(spec.suit) == "number") end
|
||||||
if spec.number then assert(type(spec.number) == "number") end
|
if spec.number then assert(type(spec.number) == "number") end
|
||||||
|
|
||||||
local card = DefensiveRide:new(spec.name, spec.suit, spec.number)
|
local card = DefensiveRide:new(spec.name, spec.suit, spec.number)
|
||||||
return card
|
card.skill = spec.skill or defaultCardSkill
|
||||||
|
return card
|
||||||
end
|
end
|
||||||
|
|
||||||
---@param spec CardSpec
|
---@param spec CardSpec
|
||||||
---@return OffensiveRide
|
---@return OffensiveRide
|
||||||
function fk.CreateOffensiveRide(spec)
|
function fk.CreateOffensiveRide(spec)
|
||||||
assert(type(spec.name) == "string" or type(spec.class_name) == "string")
|
assert(type(spec.name) == "string" or type(spec.class_name) == "string")
|
||||||
if not spec.name then spec.name = spec.class_name
|
if not spec.name then spec.name = spec.class_name
|
||||||
elseif not spec.class_name then spec.class_name = spec.name end
|
elseif not spec.class_name then spec.class_name = spec.name end
|
||||||
if spec.suit then assert(type(spec.suit) == "number") end
|
if spec.suit then assert(type(spec.suit) == "number") end
|
||||||
if spec.number then assert(type(spec.number) == "number") end
|
if spec.number then assert(type(spec.number) == "number") end
|
||||||
|
|
||||||
local card = OffensiveRide:new(spec.name, spec.suit, spec.number)
|
local card = OffensiveRide:new(spec.name, spec.suit, spec.number)
|
||||||
return card
|
card.skill = spec.skill or defaultCardSkill
|
||||||
|
return card
|
||||||
end
|
end
|
||||||
|
|
||||||
---@param spec CardSpec
|
---@param spec CardSpec
|
||||||
---@return Treasure
|
---@return Treasure
|
||||||
function fk.CreateTreasure(spec)
|
function fk.CreateTreasure(spec)
|
||||||
assert(type(spec.name) == "string" or type(spec.class_name) == "string")
|
assert(type(spec.name) == "string" or type(spec.class_name) == "string")
|
||||||
if not spec.name then spec.name = spec.class_name
|
if not spec.name then spec.name = spec.class_name
|
||||||
elseif not spec.class_name then spec.class_name = spec.name end
|
elseif not spec.class_name then spec.class_name = spec.name end
|
||||||
if spec.suit then assert(type(spec.suit) == "number") end
|
if spec.suit then assert(type(spec.suit) == "number") end
|
||||||
if spec.number then assert(type(spec.number) == "number") end
|
if spec.number then assert(type(spec.number) == "number") end
|
||||||
|
|
||||||
local card = Treasure:new(spec.name, spec.suit, spec.number)
|
local card = Treasure:new(spec.name, spec.suit, spec.number)
|
||||||
return card
|
card.skill = spec.skill or defaultCardSkill
|
||||||
end
|
return card
|
||||||
|
|
||||||
---@param spec TriggerSkillSpec
|
|
||||||
---@return TriggerSkill
|
|
||||||
function fk.CreateTriggerSkill(spec)
|
|
||||||
assert(type(spec.name) == "string")
|
|
||||||
--assert(type(spec.on_trigger) == "function")
|
|
||||||
if spec.frequency then assert(type(spec.frequency) == "number") end
|
|
||||||
|
|
||||||
local frequency = spec.frequency or Skill.NotFrequent
|
|
||||||
local skill = TriggerSkill:new(spec.name, frequency)
|
|
||||||
|
|
||||||
if type(spec.events) == "number" then
|
|
||||||
table.insert(skill.events, spec.events)
|
|
||||||
elseif type(spec.events) == "table" then
|
|
||||||
table.insertTable(skill.events, spec.events)
|
|
||||||
end
|
|
||||||
|
|
||||||
if type(spec.refresh_events) == "number" then
|
|
||||||
table.insert(skill.refresh_events, spec.refresh_events)
|
|
||||||
elseif type(spec.refresh_events) == "table" then
|
|
||||||
table.insertTable(skill.refresh_events, spec.refresh_events)
|
|
||||||
end
|
|
||||||
|
|
||||||
if type(spec.global) == "boolean" then skill.global = spec.global end
|
|
||||||
|
|
||||||
if spec.on_trigger then skill.trigger = spec.on_trigger end
|
|
||||||
|
|
||||||
if spec.can_trigger then
|
|
||||||
skill.triggerable = spec.can_trigger
|
|
||||||
end
|
|
||||||
|
|
||||||
if spec.can_refresh then
|
|
||||||
skill.canRefresh = spec.can_refresh
|
|
||||||
end
|
|
||||||
|
|
||||||
if spec.on_refresh then
|
|
||||||
skill.refresh = spec.on_refresh
|
|
||||||
end
|
|
||||||
|
|
||||||
if not spec.priority then
|
|
||||||
if frequency == Skill.Wake then
|
|
||||||
spec.priority = 3
|
|
||||||
elseif frequency == Skill.Compulsory then
|
|
||||||
spec.priority = 2
|
|
||||||
else
|
|
||||||
spec.priority = 1
|
|
||||||
end
|
|
||||||
end
|
|
||||||
if type(spec.priority) == "number" then
|
|
||||||
for _, event in ipairs(skill.events) do
|
|
||||||
skill.priority_table[event] = spec.priority
|
|
||||||
end
|
|
||||||
elseif type(spec.priority) == "table" then
|
|
||||||
for event, priority in pairs(spec.priority) do
|
|
||||||
skill.priority_table[event] = priority
|
|
||||||
end
|
|
||||||
end
|
|
||||||
return skill
|
|
||||||
end
|
end
|
||||||
|
|
|
@ -10,7 +10,8 @@ class = require "middleclass"
|
||||||
json = require "json"
|
json = require "json"
|
||||||
|
|
||||||
dofile "lua/lib/sha256.lua"
|
dofile "lua/lib/sha256.lua"
|
||||||
dofile "lua/core/util.lua"
|
local GroupUtils = require "core.util"
|
||||||
|
TargetGroup, AimGroup = table.unpack(GroupUtils)
|
||||||
dofile "lua/core/debug.lua"
|
dofile "lua/core/debug.lua"
|
||||||
|
|
||||||
math.randomseed(os.time())
|
math.randomseed(os.time())
|
||||||
|
|
|
@ -50,4 +50,24 @@ fk.EnterDying = 38
|
||||||
fk.Dying = 39
|
fk.Dying = 39
|
||||||
fk.AfterDying = 40
|
fk.AfterDying = 40
|
||||||
|
|
||||||
fk.NumOfEvents = 41
|
fk.PreCardUse = 41
|
||||||
|
fk.AfterCardUseDeclared = 42
|
||||||
|
fk.AfterCardTargetDeclared = 43
|
||||||
|
fk.BeforeCardUseEffect = 44
|
||||||
|
fk.CardUsing = 45
|
||||||
|
fk.TargetSpecifying = 46
|
||||||
|
fk.TargetConfirming = 47
|
||||||
|
fk.TargetSpecified = 48
|
||||||
|
fk.TargetConfirmed = 49
|
||||||
|
fk.CardUseFinished = 50
|
||||||
|
|
||||||
|
fk.PreCardRespond = 51
|
||||||
|
fk.CardResponding = 52
|
||||||
|
fk.CardRespondFinished = 53
|
||||||
|
|
||||||
|
fk.PreCardEffect = 54
|
||||||
|
fk.BeforeCardEffect = 55
|
||||||
|
fk.CardEffecting = 56
|
||||||
|
fk.CardEffectFinished = 57
|
||||||
|
|
||||||
|
fk.NumOfEvents = 58
|
||||||
|
|
|
@ -8,274 +8,274 @@
|
||||||
local GameLogic = class("GameLogic")
|
local GameLogic = class("GameLogic")
|
||||||
|
|
||||||
function GameLogic:initialize(room)
|
function GameLogic:initialize(room)
|
||||||
self.room = room
|
self.room = room
|
||||||
self.skill_table = {} -- TriggerEvent --> TriggerSkill[]
|
self.skill_table = {} -- TriggerEvent --> TriggerSkill[]
|
||||||
self.refresh_skill_table = {}
|
self.refresh_skill_table = {}
|
||||||
self.skills = {} -- skillName[]
|
self.skills = {} -- skillName[]
|
||||||
self.event_stack = Stack:new()
|
self.event_stack = Stack:new()
|
||||||
|
|
||||||
self.role_table = {
|
self.role_table = {
|
||||||
{ "lord" },
|
{ "lord" },
|
||||||
{ "lord", "rebel" },
|
{ "lord", "rebel" },
|
||||||
{ "lord", "rebel", "renegade" },
|
{ "lord", "rebel", "renegade" },
|
||||||
{ "lord", "loyalist", "rebel", "renegade" },
|
{ "lord", "loyalist", "rebel", "renegade" },
|
||||||
{ "lord", "loyalist", "rebel", "rebel", "renegade" },
|
{ "lord", "loyalist", "rebel", "rebel", "renegade" },
|
||||||
{ "lord", "loyalist", "rebel", "rebel", "rebel", "renegade" },
|
{ "lord", "loyalist", "rebel", "rebel", "rebel", "renegade" },
|
||||||
{ "lord", "loyalist", "loyalist", "rebel", "rebel", "rebel", "renegade" },
|
{ "lord", "loyalist", "loyalist", "rebel", "rebel", "rebel", "renegade" },
|
||||||
{ "lord", "loyalist", "loyalist", "rebel", "rebel", "rebel", "rebel", "renegade" },
|
{ "lord", "loyalist", "loyalist", "rebel", "rebel", "rebel", "rebel", "renegade" },
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
function GameLogic:run()
|
function GameLogic:run()
|
||||||
-- default logic
|
-- default logic
|
||||||
table.shuffle(self.room.players)
|
table.shuffle(self.room.players)
|
||||||
self:assignRoles()
|
self:assignRoles()
|
||||||
self.room:adjustSeats()
|
self.room:adjustSeats()
|
||||||
|
|
||||||
self:chooseGenerals()
|
self:chooseGenerals()
|
||||||
self:prepareForStart()
|
self:prepareForStart()
|
||||||
self:action()
|
self:action()
|
||||||
end
|
end
|
||||||
|
|
||||||
function GameLogic:assignRoles()
|
function GameLogic:assignRoles()
|
||||||
local room = self.room
|
local room = self.room
|
||||||
local n = #room.players
|
local n = #room.players
|
||||||
local roles = self.role_table[n]
|
local roles = self.role_table[n]
|
||||||
table.shuffle(roles)
|
table.shuffle(roles)
|
||||||
|
|
||||||
for i = 1, n do
|
for i = 1, n do
|
||||||
local p = room.players[i]
|
local p = room.players[i]
|
||||||
p.role = roles[i]
|
p.role = roles[i]
|
||||||
if p.role == "lord" then
|
if p.role == "lord" then
|
||||||
room:broadcastProperty(p, "role")
|
room:broadcastProperty(p, "role")
|
||||||
else
|
else
|
||||||
room:notifyProperty(p, p, "role")
|
room:notifyProperty(p, p, "role")
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
function GameLogic:chooseGenerals()
|
function GameLogic:chooseGenerals()
|
||||||
local room = self.room
|
local room = self.room
|
||||||
local function setPlayerGeneral(player, general)
|
local function setPlayerGeneral(player, general)
|
||||||
if Fk.generals[general] == nil then return end
|
if Fk.generals[general] == nil then return end
|
||||||
player.general = general
|
player.general = general
|
||||||
self.room:notifyProperty(player, player, "general")
|
self.room:notifyProperty(player, player, "general")
|
||||||
end
|
end
|
||||||
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(3)
|
local generals = Fk:getGeneralsRandomly(3)
|
||||||
for i = 1, #generals do
|
for i = 1, #generals do
|
||||||
generals[i] = generals[i].name
|
generals[i] = generals[i].name
|
||||||
end
|
|
||||||
lord_general = room:askForGeneral(lord, generals)
|
|
||||||
setPlayerGeneral(lord, lord_general)
|
|
||||||
room:broadcastProperty(lord, "general")
|
|
||||||
end
|
end
|
||||||
|
lord_general = room:askForGeneral(lord, generals)
|
||||||
|
setPlayerGeneral(lord, lord_general)
|
||||||
|
room:broadcastProperty(lord, "general")
|
||||||
|
end
|
||||||
|
|
||||||
local nonlord = room:getOtherPlayers(lord)
|
local nonlord = room:getOtherPlayers(lord)
|
||||||
local generals = Fk:getGeneralsRandomly(#nonlord * 3, Fk.generals, {lord_general})
|
local generals = Fk:getGeneralsRandomly(#nonlord * 3, Fk.generals, {lord_general})
|
||||||
table.shuffle(generals)
|
table.shuffle(generals)
|
||||||
for _, p in ipairs(nonlord) do
|
for _, p in ipairs(nonlord) do
|
||||||
local arg = {
|
local arg = {
|
||||||
(table.remove(generals, 1)).name,
|
(table.remove(generals, 1)).name,
|
||||||
(table.remove(generals, 1)).name,
|
(table.remove(generals, 1)).name,
|
||||||
(table.remove(generals, 1)).name,
|
(table.remove(generals, 1)).name,
|
||||||
}
|
}
|
||||||
p.request_data = json.encode(arg)
|
p.request_data = json.encode(arg)
|
||||||
p.default_reply = arg[1]
|
p.default_reply = arg[1]
|
||||||
end
|
end
|
||||||
|
|
||||||
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 general = json.decode(p.client_reply)[1]
|
local general = json.decode(p.client_reply)[1]
|
||||||
setPlayerGeneral(p, general)
|
setPlayerGeneral(p, general)
|
||||||
else
|
else
|
||||||
setPlayerGeneral(p, p.default_reply)
|
setPlayerGeneral(p, p.default_reply)
|
||||||
end
|
|
||||||
p.default_reply = ""
|
|
||||||
end
|
end
|
||||||
|
p.default_reply = ""
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
function GameLogic:prepareForStart()
|
function GameLogic:prepareForStart()
|
||||||
local room = self.room
|
local room = self.room
|
||||||
local players = room.players
|
local players = room.players
|
||||||
room.alive_players = {table.unpack(players)}
|
room.alive_players = {table.unpack(players)}
|
||||||
for i = 1, #players - 1 do
|
for i = 1, #players - 1 do
|
||||||
players[i].next = players[i + 1]
|
players[i].next = players[i + 1]
|
||||||
|
end
|
||||||
|
players[#players].next = players[1]
|
||||||
|
|
||||||
|
for _, p in ipairs(players) do
|
||||||
|
assert(p.general ~= "")
|
||||||
|
local general = Fk.generals[p.general]
|
||||||
|
p.maxHp = general.maxHp
|
||||||
|
p.hp = general.hp
|
||||||
|
-- TODO: setup AI here
|
||||||
|
|
||||||
|
if p.role ~= "lord" then
|
||||||
|
room:broadcastProperty(p, "general")
|
||||||
|
elseif #players >= 5 then
|
||||||
|
p.maxHp = p.maxHp + 1
|
||||||
|
p.hp = p.hp + 1
|
||||||
end
|
end
|
||||||
players[#players].next = players[1]
|
room:broadcastProperty(p, "maxHp")
|
||||||
|
room:broadcastProperty(p, "hp")
|
||||||
|
|
||||||
for _, p in ipairs(players) do
|
-- TODO: add skills to player
|
||||||
assert(p.general ~= "")
|
end
|
||||||
local general = Fk.generals[p.general]
|
|
||||||
p.maxHp = general.maxHp
|
|
||||||
p.hp = general.hp
|
|
||||||
-- TODO: setup AI here
|
|
||||||
|
|
||||||
if p.role ~= "lord" then
|
-- TODO: prepare drawPile
|
||||||
room:broadcastProperty(p, "general")
|
-- TODO: init cards in drawPile
|
||||||
elseif #players >= 5 then
|
local allCardIds = Fk:getAllCardIds()
|
||||||
p.maxHp = p.maxHp + 1
|
table.shuffle(allCardIds)
|
||||||
p.hp = p.hp + 1
|
room.draw_pile = allCardIds
|
||||||
end
|
for _, id in ipairs(room.draw_pile) do
|
||||||
room:broadcastProperty(p, "maxHp")
|
self.room:setCardArea(id, Card.DrawPile)
|
||||||
room:broadcastProperty(p, "hp")
|
end
|
||||||
|
|
||||||
-- TODO: add skills to player
|
self:addTriggerSkill(GameRule)
|
||||||
end
|
for _, trig in ipairs(Fk.global_trigger) do
|
||||||
|
self:addTriggerSkill(trig)
|
||||||
-- TODO: prepare drawPile
|
end
|
||||||
-- TODO: init cards in drawPile
|
|
||||||
local allCardIds = Fk:getAllCardIds()
|
|
||||||
table.shuffle(allCardIds)
|
|
||||||
room.draw_pile = allCardIds
|
|
||||||
for _, id in ipairs(room.draw_pile) do
|
|
||||||
self.room:setCardArea(id, Card.DrawPile)
|
|
||||||
end
|
|
||||||
|
|
||||||
self:addTriggerSkill(GameRule)
|
|
||||||
for _, trig in ipairs(Fk.global_trigger) do
|
|
||||||
self:addTriggerSkill(trig)
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
function GameLogic:action()
|
function GameLogic:action()
|
||||||
self:trigger(fk.GameStart)
|
self:trigger(fk.GameStart)
|
||||||
local room = self.room
|
local room = self.room
|
||||||
|
|
||||||
for _, p in ipairs(room.players) do
|
for _, p in ipairs(room.players) do
|
||||||
self:trigger(fk.DrawInitialCards, p, { num = 4 })
|
self:trigger(fk.DrawInitialCards, p, { num = 4 })
|
||||||
end
|
end
|
||||||
|
|
||||||
while true do
|
while true do
|
||||||
self:trigger(fk.TurnStart, room.current)
|
self:trigger(fk.TurnStart, room.current)
|
||||||
if room.game_finished then break end
|
if room.game_finished then break end
|
||||||
room.current = room.current:getNextAlive()
|
room.current = room.current:getNextAlive()
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
---@param skill TriggerSkill
|
---@param skill TriggerSkill
|
||||||
function GameLogic:addTriggerSkill(skill)
|
function GameLogic:addTriggerSkill(skill)
|
||||||
if skill == nil or table.contains(self.skills, skill.name) then
|
if skill == nil or table.contains(self.skills, skill.name) then
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
table.insert(self.skills, skill.name)
|
table.insert(self.skills, skill.name)
|
||||||
|
|
||||||
for _, event in ipairs(skill.refresh_events) do
|
for _, event in ipairs(skill.refresh_events) do
|
||||||
if self.refresh_skill_table[event] == nil then
|
if self.refresh_skill_table[event] == nil then
|
||||||
self.refresh_skill_table[event] = {}
|
self.refresh_skill_table[event] = {}
|
||||||
end
|
|
||||||
table.insert(self.refresh_skill_table[event], skill)
|
|
||||||
end
|
end
|
||||||
|
table.insert(self.refresh_skill_table[event], skill)
|
||||||
|
end
|
||||||
|
|
||||||
for _, event in ipairs(skill.events) do
|
for _, event in ipairs(skill.events) do
|
||||||
if self.skill_table[event] == nil then
|
if self.skill_table[event] == nil then
|
||||||
self.skill_table[event] = {}
|
self.skill_table[event] = {}
|
||||||
end
|
|
||||||
table.insert(self.skill_table[event], skill)
|
|
||||||
end
|
end
|
||||||
|
table.insert(self.skill_table[event], skill)
|
||||||
|
end
|
||||||
|
|
||||||
if skill.visible then
|
if skill.visible then
|
||||||
if (Fk.related_skills[skill.name] == nil) then return end
|
if (Fk.related_skills[skill.name] == nil) then return end
|
||||||
for _, s in ipairs(Fk.related_skills[skill.name]) do
|
for _, s in ipairs(Fk.related_skills[skill.name]) do
|
||||||
if (s.class == TriggerSkill) then
|
if (s.class == TriggerSkill) then
|
||||||
self:addTriggerSkill(s)
|
self:addTriggerSkill(s)
|
||||||
end
|
end
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
---@param event Event
|
---@param event Event
|
||||||
---@param target ServerPlayer
|
---@param target ServerPlayer
|
||||||
---@param data any
|
---@param data any
|
||||||
function GameLogic:trigger(event, target, data)
|
function GameLogic:trigger(event, target, data)
|
||||||
local room = self.room
|
local room = self.room
|
||||||
local broken = false
|
local broken = false
|
||||||
local skills = self.skill_table[event] or {}
|
local skills = self.skill_table[event] or {}
|
||||||
local skills_to_refresh = self.refresh_skill_table[event] or {}
|
local skills_to_refresh = self.refresh_skill_table[event] or {}
|
||||||
local player = target
|
local player = target
|
||||||
|
|
||||||
self.event_stack:push({event, target, data})
|
self.event_stack:push({event, target, data})
|
||||||
|
|
||||||
if target == nil then
|
if target == nil then
|
||||||
for _, skill in ipairs(skills_to_refresh) do
|
for _, skill in ipairs(skills_to_refresh) do
|
||||||
if skill:canRefresh(event, target, player, data) then
|
if skill:canRefresh(event, target, player, data) then
|
||||||
skill:refresh(event, target, player, data)
|
skill:refresh(event, target, player, data)
|
||||||
end
|
end
|
||||||
end
|
|
||||||
|
|
||||||
for _, skill in ipairs(skills) do
|
|
||||||
if skill:triggerable(event, target, player, data) then
|
|
||||||
broken = skill:trigger(event, target, player, data)
|
|
||||||
if broken then break end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
self.event_stack:pop()
|
|
||||||
return broken
|
|
||||||
end
|
end
|
||||||
|
|
||||||
repeat do
|
for _, skill in ipairs(skills) do
|
||||||
-- refresh skills. This should not be broken
|
if skill:triggerable(event, target, player, data) then
|
||||||
for _, skill in ipairs(skills_to_refresh) do
|
broken = skill:trigger(event, target, player, data)
|
||||||
if skill:canRefresh(event, target, player, data) then
|
|
||||||
skill:refresh(event, target, player, data)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
player = player.next
|
|
||||||
end until player == target
|
|
||||||
|
|
||||||
---@param a TriggerSkill
|
|
||||||
---@param b TriggerSkill
|
|
||||||
local compare_func = function (a, b)
|
|
||||||
return a.priority_table[event] > b.priority_table[event]
|
|
||||||
end
|
|
||||||
table.sort(skills, compare_func)
|
|
||||||
|
|
||||||
repeat do
|
|
||||||
local triggerable_skills = {} ---@type table<number, TriggerSkill[]>
|
|
||||||
local priority_table = {} ---@type number[]
|
|
||||||
for _, skill in ipairs(skills) do
|
|
||||||
if skill:triggerable(event, target, player, data) then
|
|
||||||
local priority = skill.priority_table[event]
|
|
||||||
if triggerable_skills[priority] == nil then
|
|
||||||
triggerable_skills[priority] = {}
|
|
||||||
end
|
|
||||||
table.insert(triggerable_skills[priority], skill)
|
|
||||||
if not table.contains(priority_table, priority) then
|
|
||||||
table.insert(priority_table, priority)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
for _, priority in ipairs(priority_table) do
|
|
||||||
local triggerables = triggerable_skills[priority]
|
|
||||||
local skill_names = {} ---@type string[]
|
|
||||||
for _, skill in ipairs(triggerables) do
|
|
||||||
table.insert(skill_names, skill.name)
|
|
||||||
end
|
|
||||||
|
|
||||||
while #skill_names > 0 do
|
|
||||||
local skill_name = room:askForChoice(player, skill_names, "trigger")
|
|
||||||
local skill = triggerables[table.indexOf(skill_names, skill_name)]
|
|
||||||
broken = skill:trigger(event, target, player, data)
|
|
||||||
if broken then break end
|
|
||||||
table.removeOne(skill_names, skill_name)
|
|
||||||
table.removeOne(triggerables, skill)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
if broken then break end
|
if broken then break end
|
||||||
|
end
|
||||||
player = player.next
|
end
|
||||||
end until player == target
|
|
||||||
|
|
||||||
self.event_stack:pop()
|
self.event_stack:pop()
|
||||||
return broken
|
return broken
|
||||||
|
end
|
||||||
|
|
||||||
|
repeat do
|
||||||
|
-- refresh skills. This should not be broken
|
||||||
|
for _, skill in ipairs(skills_to_refresh) do
|
||||||
|
if skill:canRefresh(event, target, player, data) then
|
||||||
|
skill:refresh(event, target, player, data)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
player = player.next
|
||||||
|
end until player == target
|
||||||
|
|
||||||
|
---@param a TriggerSkill
|
||||||
|
---@param b TriggerSkill
|
||||||
|
local compare_func = function (a, b)
|
||||||
|
return a.priority_table[event] > b.priority_table[event]
|
||||||
|
end
|
||||||
|
table.sort(skills, compare_func)
|
||||||
|
|
||||||
|
repeat do
|
||||||
|
local triggerable_skills = {} ---@type table<number, TriggerSkill[]>
|
||||||
|
local priority_table = {} ---@type number[]
|
||||||
|
for _, skill in ipairs(skills) do
|
||||||
|
if skill:triggerable(event, target, player, data) then
|
||||||
|
local priority = skill.priority_table[event]
|
||||||
|
if triggerable_skills[priority] == nil then
|
||||||
|
triggerable_skills[priority] = {}
|
||||||
|
end
|
||||||
|
table.insert(triggerable_skills[priority], skill)
|
||||||
|
if not table.contains(priority_table, priority) then
|
||||||
|
table.insert(priority_table, priority)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
for _, priority in ipairs(priority_table) do
|
||||||
|
local triggerables = triggerable_skills[priority]
|
||||||
|
local skill_names = {} ---@type string[]
|
||||||
|
for _, skill in ipairs(triggerables) do
|
||||||
|
table.insert(skill_names, skill.name)
|
||||||
|
end
|
||||||
|
|
||||||
|
while #skill_names > 0 do
|
||||||
|
local skill_name = room:askForChoice(player, skill_names, "trigger")
|
||||||
|
local skill = triggerables[table.indexOf(skill_names, skill_name)]
|
||||||
|
broken = skill:trigger(event, target, player, data)
|
||||||
|
if broken then break end
|
||||||
|
table.removeOne(skill_names, skill_name)
|
||||||
|
table.removeOne(triggerables, skill)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if broken then break end
|
||||||
|
|
||||||
|
player = player.next
|
||||||
|
end until player == target
|
||||||
|
|
||||||
|
self.event_stack:pop()
|
||||||
|
return broken
|
||||||
end
|
end
|
||||||
|
|
||||||
return GameLogic
|
return GameLogic
|
||||||
|
|
|
@ -6,63 +6,63 @@ fk.lobby_callback = {}
|
||||||
local db = fk.ServerInstance:getDatabase()
|
local db = fk.ServerInstance:getDatabase()
|
||||||
|
|
||||||
function Lobby:initialize(_lobby)
|
function Lobby:initialize(_lobby)
|
||||||
self.lobby = _lobby
|
self.lobby = _lobby
|
||||||
self.lobby.callback = function(_self, command, jsonData)
|
self.lobby.callback = function(_self, command, jsonData)
|
||||||
local cb = fk.lobby_callback[command]
|
local cb = fk.lobby_callback[command]
|
||||||
if (type(cb) == "function") then
|
if (type(cb) == "function") then
|
||||||
cb(jsonData)
|
cb(jsonData)
|
||||||
else
|
else
|
||||||
print("Lobby error: Unknown command " .. command);
|
print("Lobby error: Unknown command " .. command);
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
fk.lobby_callback["UpdateAvatar"] = function(jsonData)
|
fk.lobby_callback["UpdateAvatar"] = function(jsonData)
|
||||||
-- jsonData: [ int uid, string newavatar ]
|
-- jsonData: [ int uid, string newavatar ]
|
||||||
local data = json.decode(jsonData)
|
local data = json.decode(jsonData)
|
||||||
local id, avatar = data[1], data[2]
|
local id, avatar = data[1], data[2]
|
||||||
local sql = "UPDATE userinfo SET avatar='%s' WHERE id=%d;"
|
local sql = "UPDATE userinfo SET avatar='%s' WHERE id=%d;"
|
||||||
Sql.exec(db, string.format(sql, avatar, id))
|
Sql.exec(db, string.format(sql, avatar, id))
|
||||||
local player = fk.ServerInstance:findPlayer(id)
|
local player = fk.ServerInstance:findPlayer(id)
|
||||||
player:setAvatar(avatar)
|
player:setAvatar(avatar)
|
||||||
player:doNotify("UpdateAvatar", avatar)
|
player:doNotify("UpdateAvatar", avatar)
|
||||||
end
|
end
|
||||||
|
|
||||||
fk.lobby_callback["UpdatePassword"] = function(jsonData)
|
fk.lobby_callback["UpdatePassword"] = function(jsonData)
|
||||||
-- jsonData: [ int uid, string oldpassword, int newpassword ]
|
-- jsonData: [ int uid, string oldpassword, int newpassword ]
|
||||||
local data = json.decode(jsonData)
|
local data = json.decode(jsonData)
|
||||||
local id, old, new = data[1], data[2], data[3]
|
local id, old, new = data[1], data[2], data[3]
|
||||||
local sql_find = "SELECT password FROM userinfo WHERE id=%d;"
|
local sql_find = "SELECT password FROM userinfo WHERE id=%d;"
|
||||||
local sql_update = "UPDATE userinfo SET password='%s' WHERE id=%d;"
|
local sql_update = "UPDATE userinfo SET password='%s' WHERE id=%d;"
|
||||||
|
|
||||||
local passed = false
|
local passed = false
|
||||||
local result = Sql.exec_select(db, string.format(sql_find, id))
|
local result = Sql.exec_select(db, string.format(sql_find, id))
|
||||||
passed = (result["password"][1] == sha256(old))
|
passed = (result["password"][1] == sha256(old))
|
||||||
if passed then
|
if passed then
|
||||||
Sql.exec(db, string.format(sql_update, sha256(new), id))
|
Sql.exec(db, string.format(sql_update, sha256(new), id))
|
||||||
end
|
end
|
||||||
|
|
||||||
local player = fk.ServerInstance:findPlayer(tonumber(id))
|
local player = fk.ServerInstance:findPlayer(tonumber(id))
|
||||||
player:doNotify("UpdatePassword", passed and "1" or "0")
|
player:doNotify("UpdatePassword", passed and "1" or "0")
|
||||||
end
|
end
|
||||||
|
|
||||||
fk.lobby_callback["CreateRoom"] = function(jsonData)
|
fk.lobby_callback["CreateRoom"] = function(jsonData)
|
||||||
-- jsonData: [ int uid, string name, int capacity ]
|
-- jsonData: [ int uid, string name, int capacity ]
|
||||||
local data = json.decode(jsonData)
|
local data = json.decode(jsonData)
|
||||||
local owner = fk.ServerInstance:findPlayer(tonumber(data[1]))
|
local owner = fk.ServerInstance:findPlayer(tonumber(data[1]))
|
||||||
local roomName = data[2]
|
local roomName = data[2]
|
||||||
local capacity = data[3]
|
local capacity = data[3]
|
||||||
fk.ServerInstance:createRoom(owner, roomName, capacity)
|
fk.ServerInstance:createRoom(owner, roomName, capacity)
|
||||||
end
|
end
|
||||||
|
|
||||||
fk.lobby_callback["EnterRoom"] = function(jsonData)
|
fk.lobby_callback["EnterRoom"] = function(jsonData)
|
||||||
-- jsonData: [ int uid, int roomId ]
|
-- jsonData: [ int uid, int roomId ]
|
||||||
local data = json.decode(jsonData)
|
local data = json.decode(jsonData)
|
||||||
local player = fk.ServerInstance:findPlayer(tonumber(data[1]))
|
local player = fk.ServerInstance:findPlayer(tonumber(data[1]))
|
||||||
local room = fk.ServerInstance:findRoom(tonumber(data[2]))
|
local room = fk.ServerInstance:findRoom(tonumber(data[2]))
|
||||||
room:addPlayer(player)
|
room:addPlayer(player)
|
||||||
end
|
end
|
||||||
|
|
||||||
function CreateRoom(_room)
|
function CreateRoom(_room)
|
||||||
LobbyInstance = Lobby:new(_room)
|
LobbyInstance = Lobby:new(_room)
|
||||||
end
|
end
|
||||||
|
|
1225
lua/server/room.lua
1225
lua/server/room.lua
File diff suppressed because it is too large
Load Diff
|
@ -12,30 +12,30 @@
|
||||||
local ServerPlayer = Player:subclass("ServerPlayer")
|
local ServerPlayer = Player:subclass("ServerPlayer")
|
||||||
|
|
||||||
function ServerPlayer:initialize(_self)
|
function ServerPlayer:initialize(_self)
|
||||||
Player.initialize(self)
|
Player.initialize(self)
|
||||||
self.serverplayer = _self
|
self.serverplayer = _self
|
||||||
self.id = _self:getId()
|
self.id = _self:getId()
|
||||||
self.room = nil
|
self.room = nil
|
||||||
|
|
||||||
self.next = nil
|
self.next = nil
|
||||||
|
|
||||||
-- Below are for doBroadcastRequest
|
-- Below are for doBroadcastRequest
|
||||||
self.request_data = ""
|
self.request_data = ""
|
||||||
self.client_reply = ""
|
self.client_reply = ""
|
||||||
self.default_reply = ""
|
self.default_reply = ""
|
||||||
self.reply_ready = false
|
self.reply_ready = false
|
||||||
self.phases = {}
|
self.phases = {}
|
||||||
end
|
end
|
||||||
|
|
||||||
---@return integer
|
---@return integer
|
||||||
function ServerPlayer:getId()
|
function ServerPlayer:getId()
|
||||||
return self.id
|
return self.id
|
||||||
end
|
end
|
||||||
|
|
||||||
---@param command string
|
---@param command string
|
||||||
---@param jsonData string
|
---@param jsonData string
|
||||||
function ServerPlayer:doNotify(command, jsonData)
|
function ServerPlayer:doNotify(command, jsonData)
|
||||||
self.serverplayer:doNotify(command, jsonData)
|
self.serverplayer:doNotify(command, jsonData)
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Send a request to client, and allow client to reply within *timeout* seconds.
|
--- Send a request to client, and allow client to reply within *timeout* seconds.
|
||||||
|
@ -45,10 +45,10 @@ end
|
||||||
---@param jsonData string
|
---@param jsonData string
|
||||||
---@param timeout integer
|
---@param timeout integer
|
||||||
function ServerPlayer:doRequest(command, jsonData, timeout)
|
function ServerPlayer:doRequest(command, jsonData, timeout)
|
||||||
timeout = timeout or self.room.timeout
|
timeout = timeout or self.room.timeout
|
||||||
self.client_reply = ""
|
self.client_reply = ""
|
||||||
self.reply_ready = false
|
self.reply_ready = false
|
||||||
self.serverplayer:doRequest(command, jsonData, timeout)
|
self.serverplayer:doRequest(command, jsonData, timeout)
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Wait for at most *timeout* seconds for reply from client.
|
--- Wait for at most *timeout* seconds for reply from client.
|
||||||
|
@ -57,153 +57,153 @@ end
|
||||||
---@param timeout integer @ seconds to wait
|
---@param timeout integer @ seconds to wait
|
||||||
---@return string @ JSON data
|
---@return string @ JSON data
|
||||||
function ServerPlayer:waitForReply(timeout)
|
function ServerPlayer:waitForReply(timeout)
|
||||||
local result = ""
|
local result = ""
|
||||||
if timeout == nil then
|
if timeout == nil then
|
||||||
result = self.serverplayer:waitForReply()
|
result = self.serverplayer:waitForReply()
|
||||||
else
|
else
|
||||||
result = self.serverplayer:waitForReply(timeout)
|
result = self.serverplayer:waitForReply(timeout)
|
||||||
end
|
end
|
||||||
self.request_data = ""
|
self.request_data = ""
|
||||||
self.client_reply = result
|
self.client_reply = result
|
||||||
if result ~= "" then self.reply_ready = true end
|
if result ~= "" then self.reply_ready = true end
|
||||||
return result
|
return result
|
||||||
end
|
end
|
||||||
|
|
||||||
---@param skill Skill
|
---@param skill Skill
|
||||||
function ServerPlayer:hasSkill(skill)
|
function ServerPlayer:hasSkill(skill)
|
||||||
return table.contains(self.player_skills, skill)
|
return table.contains(self.player_skills, skill)
|
||||||
end
|
end
|
||||||
|
|
||||||
function ServerPlayer:isAlive()
|
function ServerPlayer:isAlive()
|
||||||
return self.dead == false
|
return self.dead == false
|
||||||
end
|
end
|
||||||
|
|
||||||
function ServerPlayer:getNextAlive()
|
function ServerPlayer:getNextAlive()
|
||||||
if #self.room.alive_players == 0 then
|
if #self.room.alive_players == 0 then
|
||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
local ret = self.next
|
local ret = self.next
|
||||||
while ret.dead do
|
while ret.dead do
|
||||||
ret = ret.next
|
ret = ret.next
|
||||||
end
|
end
|
||||||
return ret
|
return ret
|
||||||
end
|
end
|
||||||
|
|
||||||
function ServerPlayer:turnOver()
|
function ServerPlayer:turnOver()
|
||||||
self.faceup = not self.faceup
|
self.faceup = not self.faceup
|
||||||
self.room:broadcastProperty(self, "faceup")
|
self.room:broadcastProperty(self, "faceup")
|
||||||
|
|
||||||
-- TODO: log
|
-- TODO: log
|
||||||
self.room.logic:trigger(fk.TurnedOver, self)
|
self.room.logic:trigger(fk.TurnedOver, self)
|
||||||
end
|
end
|
||||||
|
|
||||||
---@param from_phase Phase
|
---@param from_phase Phase
|
||||||
---@param to_phase Phase
|
---@param to_phase Phase
|
||||||
function ServerPlayer:changePhase(from_phase, to_phase)
|
function ServerPlayer:changePhase(from_phase, to_phase)
|
||||||
local room = self.room
|
local room = self.room
|
||||||
local logic = room.logic
|
local logic = room.logic
|
||||||
self.phase = Player.PhaseNone
|
self.phase = Player.PhaseNone
|
||||||
|
|
||||||
local phase_change = {
|
local phase_change = {
|
||||||
from = from_phase,
|
from = from_phase,
|
||||||
to = to_phase
|
to = to_phase
|
||||||
}
|
}
|
||||||
|
|
||||||
local skip = logic:trigger(fk.EventPhaseChanging, self, phase_change)
|
local skip = logic:trigger(fk.EventPhaseChanging, self, phase_change)
|
||||||
if skip and to_phase ~= Player.NotActive then
|
if skip and to_phase ~= Player.NotActive then
|
||||||
self.phase = from_phase
|
self.phase = from_phase
|
||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
|
|
||||||
self.phase = to_phase
|
self.phase = to_phase
|
||||||
room:notifyProperty(self, self, "phase")
|
room:notifyProperty(self, self, "phase")
|
||||||
|
|
||||||
if #self.phases > 0 then
|
if #self.phases > 0 then
|
||||||
table.remove(self.phases, 1)
|
table.remove(self.phases, 1)
|
||||||
end
|
end
|
||||||
|
|
||||||
if not logic:trigger(fk.EventPhaseStart, self) then
|
|
||||||
if self.phase ~= Player.NotActive then
|
|
||||||
logic:trigger(fk.EventPhaseProceeding, self)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
|
if not logic:trigger(fk.EventPhaseStart, self) then
|
||||||
if self.phase ~= Player.NotActive then
|
if self.phase ~= Player.NotActive then
|
||||||
logic:trigger(fk.EventPhaseEnd, self)
|
logic:trigger(fk.EventPhaseProceeding, self)
|
||||||
end
|
end
|
||||||
|
end
|
||||||
|
|
||||||
return false
|
if self.phase ~= Player.NotActive then
|
||||||
|
logic:trigger(fk.EventPhaseEnd, self)
|
||||||
|
end
|
||||||
|
|
||||||
|
return false
|
||||||
end
|
end
|
||||||
|
|
||||||
---@param phase_table Phase[]
|
---@param phase_table Phase[]
|
||||||
function ServerPlayer:play(phase_table)
|
function ServerPlayer:play(phase_table)
|
||||||
phase_table = phase_table or {}
|
phase_table = phase_table or {}
|
||||||
if #phase_table > 0 then
|
if #phase_table > 0 then
|
||||||
if not table.contains(phase_table, Player.NotActive) then
|
if not table.contains(phase_table, Player.NotActive) then
|
||||||
table.insert(phase_table, Player.NotActive)
|
table.insert(phase_table, Player.NotActive)
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
phase_table = {
|
phase_table = {
|
||||||
Player.RoundStart, Player.Start,
|
Player.RoundStart, Player.Start,
|
||||||
Player.Judge, Player.Draw, Player.Play, Player.Discard,
|
Player.Judge, Player.Draw, Player.Play, Player.Discard,
|
||||||
Player.Finish, Player.NotActive,
|
Player.Finish, Player.NotActive,
|
||||||
}
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
self.phases = phase_table
|
||||||
|
self.phase_state = {}
|
||||||
|
|
||||||
|
local phases = self.phases
|
||||||
|
local phase_state = self.phase_state
|
||||||
|
local room = self.room
|
||||||
|
|
||||||
|
for i = 1, #phases do
|
||||||
|
phase_state[i] = {
|
||||||
|
phase = phases[i],
|
||||||
|
skipped = false
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
for i = 1, #phases do
|
||||||
|
if self.dead then
|
||||||
|
self:changePhase(self.phase, Player.NotActive)
|
||||||
|
break
|
||||||
end
|
end
|
||||||
|
|
||||||
self.phases = phase_table
|
self.phase_index = i
|
||||||
self.phase_state = {}
|
local phase_change = {
|
||||||
|
from = self.phase,
|
||||||
|
to = phases[i]
|
||||||
|
}
|
||||||
|
|
||||||
local phases = self.phases
|
local logic = self.room.logic
|
||||||
local phase_state = self.phase_state
|
self.phase = Player.PhaseNone
|
||||||
local room = self.room
|
|
||||||
|
|
||||||
for i = 1, #phases do
|
local skip = logic:trigger(fk.EventPhaseChanging, self, phase_change)
|
||||||
phase_state[i] = {
|
phases[i] = phase_change.to
|
||||||
phase = phases[i],
|
phase_state[i].phase = phases[i]
|
||||||
skipped = false
|
|
||||||
}
|
self.phase = phases[i]
|
||||||
|
room:notifyProperty(self, self, "phase")
|
||||||
|
|
||||||
|
local cancel_skip = true
|
||||||
|
if phases[i] ~= Player.NotActive and (phase_state[i].skipped or skip) then
|
||||||
|
cancel_skip = logic:trigger(fk.EventPhaseSkipping, self)
|
||||||
end
|
end
|
||||||
|
|
||||||
for i = 1, #phases do
|
if (not skip) or (cancel_skip) then
|
||||||
if self.dead then
|
if not logic:trigger(fk.EventPhaseStart, self) then
|
||||||
self:changePhase(self.phase, Player.NotActive)
|
if self.phase ~= Player.NotActive then
|
||||||
break
|
logic:trigger(fk.EventPhaseProceeding, self)
|
||||||
end
|
end
|
||||||
|
end
|
||||||
|
|
||||||
self.phase_index = i
|
if self.phase ~= Player.NotActive then
|
||||||
local phase_change = {
|
logic:trigger(fk.EventPhaseEnd, self)
|
||||||
from = self.phase,
|
else break end
|
||||||
to = phases[i]
|
|
||||||
}
|
|
||||||
|
|
||||||
local logic = self.room.logic
|
|
||||||
self.phase = Player.PhaseNone
|
|
||||||
|
|
||||||
local skip = logic:trigger(fk.EventPhaseChanging, self, phase_change)
|
|
||||||
phases[i] = phase_change.to
|
|
||||||
phase_state[i].phase = phases[i]
|
|
||||||
|
|
||||||
self.phase = phases[i]
|
|
||||||
room:notifyProperty(self, self, "phase")
|
|
||||||
|
|
||||||
local cancel_skip = true
|
|
||||||
if phases[i] ~= Player.NotActive and (phase_state[i].skipped or skip) then
|
|
||||||
cancel_skip = logic:trigger(fk.EventPhaseSkipping, self)
|
|
||||||
end
|
|
||||||
|
|
||||||
if (not skip) or (cancel_skip) then
|
|
||||||
if not logic:trigger(fk.EventPhaseStart, self) then
|
|
||||||
if self.phase ~= Player.NotActive then
|
|
||||||
logic:trigger(fk.EventPhaseProceeding, self)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
if self.phase ~= Player.NotActive then
|
|
||||||
logic:trigger(fk.EventPhaseEnd, self)
|
|
||||||
else break end
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
return ServerPlayer
|
return ServerPlayer
|
||||||
|
|
|
@ -10,6 +10,9 @@
|
||||||
---@alias DyingStruct { who: integer, damage: DamageStruct }
|
---@alias DyingStruct { who: integer, damage: DamageStruct }
|
||||||
---@alias DeathStruct { who: integer, damage: DamageStruct }
|
---@alias DeathStruct { who: integer, damage: DamageStruct }
|
||||||
|
|
||||||
|
---@alias CardUseStruct { from: integer, tos: TargetGroup, cardId: integer, toCardId: integer|null, responseToEvent: CardUseStruct|null, nullifiedTargets: interger[]|null, extraUse: boolean|null, disresponsiveList: integer[]|null, unoffsetableList: integer[]|null, addtionalDamage: integer|null, customFrom: integer|null, cardIdsResponded: integer[]|null }
|
||||||
|
---@alias AimStruct { from: integer, cardId: integer, tos: AimGroup, to: integer, targetGroup: TargetGroup|null, nullifiedTargets: integer[]|null, firstTarget: boolean, additionalDamage: integer|null, disresponsive: boolean|null, unoffsetableList: boolean|null }
|
||||||
|
---@alias CardEffectEvent { from: integer, tos: TargetGroup, cardId: integer, toCardId: integer|null, responseToEvent: CardUseStruct|null, nullifiedTargets: interger[]|null, extraUse: boolean|null, disresponsiveList: integer[]|null, unoffsetableList: integer[]|null, addtionalDamage: integer|null, customFrom: integer|null, cardIdsResponded: integer[]|null }
|
||||||
|
|
||||||
---@alias MoveReason integer
|
---@alias MoveReason integer
|
||||||
|
|
||||||
|
@ -21,6 +24,8 @@ fk.ReasonPut = 5
|
||||||
fk.ReasonPutIntoDiscardPile = 6
|
fk.ReasonPutIntoDiscardPile = 6
|
||||||
fk.ReasonPrey = 7
|
fk.ReasonPrey = 7
|
||||||
fk.ReasonExchange = 8
|
fk.ReasonExchange = 8
|
||||||
|
fk.ReasonUse = 9
|
||||||
|
fk.ReasonResonpse = 10
|
||||||
|
|
||||||
---@alias DamageType integer
|
---@alias DamageType integer
|
||||||
|
|
||||||
|
|
|
@ -1,118 +1,133 @@
|
||||||
GameRule = fk.CreateTriggerSkill{
|
GameRule = fk.CreateTriggerSkill{
|
||||||
name = "game_rule",
|
name = "game_rule",
|
||||||
events = {
|
events = {
|
||||||
fk.GameStart, fk.DrawInitialCards, fk.TurnStart,
|
fk.GameStart, fk.DrawInitialCards, fk.TurnStart,
|
||||||
fk.EventPhaseProceeding, fk.EventPhaseEnd, fk.EventPhaseChanging,
|
fk.EventPhaseProceeding, fk.EventPhaseEnd, fk.EventPhaseChanging,
|
||||||
},
|
},
|
||||||
priority = 0,
|
priority = 0,
|
||||||
|
|
||||||
can_trigger = function(self, event, target, player, data)
|
can_trigger = function(self, event, target, player, data)
|
||||||
return (target == player) or (target == nil)
|
return (target == player) or (target == nil)
|
||||||
end,
|
end,
|
||||||
|
|
||||||
on_trigger = function(self, event, target, player, data)
|
on_trigger = function(self, event, target, player, data)
|
||||||
if RoomInstance.tag["SkipGameRule"] then
|
if RoomInstance.tag["SkipGameRule"] then
|
||||||
RoomInstance.tag["SkipGameRule"] = false
|
RoomInstance.tag["SkipGameRule"] = false
|
||||||
return false
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
if target == nil then
|
||||||
|
if event == fk.GameStart then
|
||||||
|
print("Game started")
|
||||||
|
RoomInstance.tag["FirstRound"] = true
|
||||||
|
end
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
local room = player.room
|
||||||
|
switch(event, {
|
||||||
|
[fk.DrawInitialCards] = function()
|
||||||
|
if data.num > 0 then
|
||||||
|
-- TODO: need a new function to call the UI
|
||||||
|
local cardIds = room:getNCards(data.num)
|
||||||
|
player:addCards(Player.Hand, cardIds)
|
||||||
|
local move_to_notify = {} ---@type CardsMoveStruct
|
||||||
|
move_to_notify.toArea = Card.PlayerHand
|
||||||
|
move_to_notify.to = player:getId()
|
||||||
|
move_to_notify.moveInfo = {}
|
||||||
|
for _, id in ipairs(cardIds) do
|
||||||
|
table.insert(move_to_notify.moveInfo,
|
||||||
|
{ cardId = id, fromArea = Card.DrawPile })
|
||||||
|
end
|
||||||
|
room:notifyMoveCards(room.players, {move_to_notify})
|
||||||
|
|
||||||
|
for _, id in ipairs(cardIds) do
|
||||||
|
room:setCardArea(id, Card.PlayerHand)
|
||||||
end
|
end
|
||||||
|
|
||||||
if target == nil then
|
room.logic:trigger(fk.AfterDrawInitialCards, player, data)
|
||||||
if event == fk.GameStart then
|
end
|
||||||
print("Game started")
|
|
||||||
RoomInstance.tag["FirstRound"] = true
|
|
||||||
end
|
|
||||||
return false
|
|
||||||
end
|
|
||||||
|
|
||||||
local room = player.room
|
|
||||||
switch(event, {
|
|
||||||
[fk.DrawInitialCards] = function()
|
|
||||||
if data.num > 0 then
|
|
||||||
-- TODO: need a new function to call the UI
|
|
||||||
local cardIds = room:getNCards(data.num)
|
|
||||||
player:addCards(Player.Hand, cardIds)
|
|
||||||
local move_to_notify = {} ---@type CardsMoveStruct
|
|
||||||
move_to_notify.toArea = Card.PlayerHand
|
|
||||||
move_to_notify.to = player:getId()
|
|
||||||
move_to_notify.moveInfo = {}
|
|
||||||
for _, id in ipairs(cardIds) do
|
|
||||||
table.insert(move_to_notify.moveInfo,
|
|
||||||
{ cardId = id, fromArea = Card.DrawPile })
|
|
||||||
end
|
|
||||||
room:notifyMoveCards(room.players, {move_to_notify})
|
|
||||||
|
|
||||||
for _, id in ipairs(cardIds) do
|
|
||||||
room:setCardArea(id, Card.PlayerHand)
|
|
||||||
end
|
|
||||||
|
|
||||||
room.logic:trigger(fk.AfterDrawInitialCards, player, data)
|
|
||||||
end
|
|
||||||
end,
|
|
||||||
[fk.TurnStart] = function()
|
|
||||||
player = room.current
|
|
||||||
if room.tag["FirstRound"] == true then
|
|
||||||
room.tag["FirstRound"] = false
|
|
||||||
player:setFlag("Global_FirstRound")
|
|
||||||
end
|
|
||||||
|
|
||||||
-- TODO: send log
|
|
||||||
|
|
||||||
player:addMark("Global_TurnCount")
|
|
||||||
if not player.faceup then
|
|
||||||
player:setFlag("-Global_FirstRound")
|
|
||||||
player:turnOver()
|
|
||||||
elseif not player.dead then
|
|
||||||
player:play()
|
|
||||||
end
|
|
||||||
end,
|
|
||||||
[fk.EventPhaseProceeding] = function()
|
|
||||||
switch(player.phase, {
|
|
||||||
[Player.PhaseNone] = function()
|
|
||||||
error("You should never proceed PhaseNone")
|
|
||||||
end,
|
|
||||||
[Player.RoundStart] = function()
|
|
||||||
|
|
||||||
end,
|
|
||||||
[Player.Start] = function()
|
|
||||||
|
|
||||||
end,
|
|
||||||
[Player.Judge] = function()
|
|
||||||
|
|
||||||
end,
|
|
||||||
[Player.Draw] = function()
|
|
||||||
room:drawCards(player, 2, self.name)
|
|
||||||
end,
|
|
||||||
[Player.Play] = function()
|
|
||||||
room:askForSkillInvoke(player, "rule")
|
|
||||||
end,
|
|
||||||
[Player.Discard] = function()
|
|
||||||
local discardNum = #player:getCardIds(Player.Hand) - player:getMaxCards()
|
|
||||||
if discardNum > 0 then
|
|
||||||
room:askForDiscard(player, discardNum, discardNum, false, self.name)
|
|
||||||
end
|
|
||||||
end,
|
|
||||||
[Player.Finish] = function()
|
|
||||||
|
|
||||||
end,
|
|
||||||
[Player.NotActive] = function()
|
|
||||||
|
|
||||||
end,
|
|
||||||
})
|
|
||||||
end,
|
|
||||||
[fk.EventPhaseEnd] = function()
|
|
||||||
if player.phase == Player.Play then
|
|
||||||
-- TODO: clear history
|
|
||||||
end
|
|
||||||
end,
|
|
||||||
[fk.EventPhaseChanging] = function()
|
|
||||||
-- TODO: copy but dont copy all
|
|
||||||
end,
|
|
||||||
default = function()
|
|
||||||
print("game_rule: Event=" .. event)
|
|
||||||
room:askForSkillInvoke(player, "rule")
|
|
||||||
end,
|
|
||||||
})
|
|
||||||
return false
|
|
||||||
end,
|
end,
|
||||||
|
[fk.TurnStart] = function()
|
||||||
|
player = room.current
|
||||||
|
if room.tag["FirstRound"] == true then
|
||||||
|
room.tag["FirstRound"] = false
|
||||||
|
player:setFlag("Global_FirstRound")
|
||||||
|
end
|
||||||
|
|
||||||
|
-- TODO: send log
|
||||||
|
|
||||||
|
player:addMark("Global_TurnCount")
|
||||||
|
if not player.faceup then
|
||||||
|
player:setFlag("-Global_FirstRound")
|
||||||
|
player:turnOver()
|
||||||
|
elseif not player.dead then
|
||||||
|
player:play()
|
||||||
|
end
|
||||||
|
end,
|
||||||
|
[fk.EventPhaseProceeding] = function()
|
||||||
|
switch(player.phase, {
|
||||||
|
[Player.PhaseNone] = function()
|
||||||
|
error("You should never proceed PhaseNone")
|
||||||
|
end,
|
||||||
|
[Player.RoundStart] = function()
|
||||||
|
|
||||||
|
end,
|
||||||
|
[Player.Start] = function()
|
||||||
|
|
||||||
|
end,
|
||||||
|
[Player.Judge] = function()
|
||||||
|
|
||||||
|
end,
|
||||||
|
[Player.Draw] = function()
|
||||||
|
room:drawCards(player, 2, self.name)
|
||||||
|
end,
|
||||||
|
[Player.Play] = function()
|
||||||
|
while not player.dead do
|
||||||
|
local result = room:doRequest(player, "PlayCard", player:getId())
|
||||||
|
if result == "" then break end
|
||||||
|
|
||||||
|
local data = json.decode(result)
|
||||||
|
local card = data.card
|
||||||
|
local targets = data.targets
|
||||||
|
local use = {} ---@type CardUseStruct
|
||||||
|
use.from = player:getId()
|
||||||
|
use.tos = {}
|
||||||
|
for _, target in ipairs(targets) do
|
||||||
|
table.insert(use.tos, { target })
|
||||||
|
end
|
||||||
|
use.cardId = card
|
||||||
|
room:useCard(use)
|
||||||
|
end
|
||||||
|
end,
|
||||||
|
[Player.Discard] = function()
|
||||||
|
local discardNum = #player:getCardIds(Player.Hand) - player:getMaxCards()
|
||||||
|
if discardNum > 0 then
|
||||||
|
room:askForDiscard(player, discardNum, discardNum, false, self.name)
|
||||||
|
end
|
||||||
|
end,
|
||||||
|
[Player.Finish] = function()
|
||||||
|
|
||||||
|
end,
|
||||||
|
[Player.NotActive] = function()
|
||||||
|
|
||||||
|
end,
|
||||||
|
})
|
||||||
|
end,
|
||||||
|
[fk.EventPhaseEnd] = function()
|
||||||
|
if player.phase == Player.Play then
|
||||||
|
-- TODO: clear history
|
||||||
|
end
|
||||||
|
end,
|
||||||
|
[fk.EventPhaseChanging] = function()
|
||||||
|
-- TODO: copy but dont copy all
|
||||||
|
end,
|
||||||
|
default = function()
|
||||||
|
print("game_rule: Event=" .. event)
|
||||||
|
room:askForSkillInvoke(player, "rule")
|
||||||
|
end,
|
||||||
|
})
|
||||||
|
return false
|
||||||
|
end,
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,161 +3,161 @@ extension.metadata = require "packages.standard.metadata"
|
||||||
dofile "packages/standard/game_rule.lua"
|
dofile "packages/standard/game_rule.lua"
|
||||||
|
|
||||||
Fk:loadTranslationTable{
|
Fk:loadTranslationTable{
|
||||||
["standard"] = "标准包",
|
["standard"] = "标准包",
|
||||||
["wei"] = "魏",
|
["wei"] = "魏",
|
||||||
["shu"] = "蜀",
|
["shu"] = "蜀",
|
||||||
["wu"] = "吴",
|
["wu"] = "吴",
|
||||||
["qun"] = "群",
|
["qun"] = "群",
|
||||||
}
|
}
|
||||||
|
|
||||||
local caocao = General:new(extension, "caocao", "wei", 4)
|
local caocao = General:new(extension, "caocao", "wei", 4)
|
||||||
extension:addGeneral(caocao)
|
extension:addGeneral(caocao)
|
||||||
Fk:loadTranslationTable{
|
Fk:loadTranslationTable{
|
||||||
["caocao"] = "曹操",
|
["caocao"] = "曹操",
|
||||||
}
|
}
|
||||||
|
|
||||||
local simayi = General:new(extension, "simayi", "wei", 3)
|
local simayi = General:new(extension, "simayi", "wei", 3)
|
||||||
extension:addGeneral(simayi)
|
extension:addGeneral(simayi)
|
||||||
Fk:loadTranslationTable{
|
Fk:loadTranslationTable{
|
||||||
["simayi"] = "司马懿",
|
["simayi"] = "司马懿",
|
||||||
}
|
}
|
||||||
|
|
||||||
local xiahoudun = General:new(extension, "xiahoudun", "wei", 4)
|
local xiahoudun = General:new(extension, "xiahoudun", "wei", 4)
|
||||||
extension:addGeneral(xiahoudun)
|
extension:addGeneral(xiahoudun)
|
||||||
Fk:loadTranslationTable{
|
Fk:loadTranslationTable{
|
||||||
["xiahoudun"] = "夏侯惇",
|
["xiahoudun"] = "夏侯惇",
|
||||||
}
|
}
|
||||||
|
|
||||||
local zhangliao = General:new(extension, "zhangliao", "wei", 4)
|
local zhangliao = General:new(extension, "zhangliao", "wei", 4)
|
||||||
extension:addGeneral(zhangliao)
|
extension:addGeneral(zhangliao)
|
||||||
Fk:loadTranslationTable{
|
Fk:loadTranslationTable{
|
||||||
["zhangliao"] = "张辽",
|
["zhangliao"] = "张辽",
|
||||||
}
|
}
|
||||||
|
|
||||||
local xuchu = General:new(extension, "xuchu", "wei", 4)
|
local xuchu = General:new(extension, "xuchu", "wei", 4)
|
||||||
extension:addGeneral(xuchu)
|
extension:addGeneral(xuchu)
|
||||||
Fk:loadTranslationTable{
|
Fk:loadTranslationTable{
|
||||||
["xuchu"] = "许褚",
|
["xuchu"] = "许褚",
|
||||||
}
|
}
|
||||||
|
|
||||||
local guojia = General:new(extension, "guojia", "wei", 4)
|
local guojia = General:new(extension, "guojia", "wei", 4)
|
||||||
extension:addGeneral(guojia)
|
extension:addGeneral(guojia)
|
||||||
Fk:loadTranslationTable{
|
Fk:loadTranslationTable{
|
||||||
["guojia"] = "郭嘉",
|
["guojia"] = "郭嘉",
|
||||||
}
|
}
|
||||||
|
|
||||||
local zhenji = General:new(extension, "zhenji", "wei", 3)
|
local zhenji = General:new(extension, "zhenji", "wei", 3)
|
||||||
extension:addGeneral(zhenji)
|
extension:addGeneral(zhenji)
|
||||||
Fk:loadTranslationTable{
|
Fk:loadTranslationTable{
|
||||||
["zhenji"] = "甄姬",
|
["zhenji"] = "甄姬",
|
||||||
}
|
}
|
||||||
|
|
||||||
local liubei = General:new(extension, "liubei", "shu", 4)
|
local liubei = General:new(extension, "liubei", "shu", 4)
|
||||||
extension:addGeneral(liubei)
|
extension:addGeneral(liubei)
|
||||||
Fk:loadTranslationTable{
|
Fk:loadTranslationTable{
|
||||||
["liubei"] = "刘备",
|
["liubei"] = "刘备",
|
||||||
}
|
}
|
||||||
|
|
||||||
local guanyu = General:new(extension, "guanyu", "shu", 4)
|
local guanyu = General:new(extension, "guanyu", "shu", 4)
|
||||||
extension:addGeneral(guanyu)
|
extension:addGeneral(guanyu)
|
||||||
Fk:loadTranslationTable{
|
Fk:loadTranslationTable{
|
||||||
["guanyu"] = "关羽",
|
["guanyu"] = "关羽",
|
||||||
}
|
}
|
||||||
|
|
||||||
local zhangfei = General:new(extension, "zhangfei", "shu", 4)
|
local zhangfei = General:new(extension, "zhangfei", "shu", 4)
|
||||||
extension:addGeneral(zhangfei)
|
extension:addGeneral(zhangfei)
|
||||||
Fk:loadTranslationTable{
|
Fk:loadTranslationTable{
|
||||||
["zhangfei"] = "张飞",
|
["zhangfei"] = "张飞",
|
||||||
}
|
}
|
||||||
|
|
||||||
local zhugeliang = General:new(extension, "zhugeliang", "shu", 3)
|
local zhugeliang = General:new(extension, "zhugeliang", "shu", 3)
|
||||||
extension:addGeneral(zhugeliang)
|
extension:addGeneral(zhugeliang)
|
||||||
Fk:loadTranslationTable{
|
Fk:loadTranslationTable{
|
||||||
["zhugeliang"] = "诸葛亮",
|
["zhugeliang"] = "诸葛亮",
|
||||||
}
|
}
|
||||||
|
|
||||||
local zhaoyun = General:new(extension, "zhaoyun", "shu", 4)
|
local zhaoyun = General:new(extension, "zhaoyun", "shu", 4)
|
||||||
extension:addGeneral(zhaoyun)
|
extension:addGeneral(zhaoyun)
|
||||||
Fk:loadTranslationTable{
|
Fk:loadTranslationTable{
|
||||||
["zhaoyun"] = "赵云",
|
["zhaoyun"] = "赵云",
|
||||||
}
|
}
|
||||||
|
|
||||||
local machao = General:new(extension, "machao", "shu", 4)
|
local machao = General:new(extension, "machao", "shu", 4)
|
||||||
extension:addGeneral(machao)
|
extension:addGeneral(machao)
|
||||||
Fk:loadTranslationTable{
|
Fk:loadTranslationTable{
|
||||||
["machao"] = "马超",
|
["machao"] = "马超",
|
||||||
}
|
}
|
||||||
|
|
||||||
local huangyueying = General:new(extension, "huangyueying", "shu", 3)
|
local huangyueying = General:new(extension, "huangyueying", "shu", 3)
|
||||||
extension:addGeneral(huangyueying)
|
extension:addGeneral(huangyueying)
|
||||||
Fk:loadTranslationTable{
|
Fk:loadTranslationTable{
|
||||||
["huangyueying"] = "黄月英",
|
["huangyueying"] = "黄月英",
|
||||||
}
|
}
|
||||||
|
|
||||||
local sunquan = General:new(extension, "sunquan", "wu", 4)
|
local sunquan = General:new(extension, "sunquan", "wu", 4)
|
||||||
extension:addGeneral(sunquan)
|
extension:addGeneral(sunquan)
|
||||||
Fk:loadTranslationTable{
|
Fk:loadTranslationTable{
|
||||||
["sunquan"] = "孙权",
|
["sunquan"] = "孙权",
|
||||||
}
|
}
|
||||||
|
|
||||||
local ganning = General:new(extension, "ganning", "wu", 4)
|
local ganning = General:new(extension, "ganning", "wu", 4)
|
||||||
extension:addGeneral(ganning)
|
extension:addGeneral(ganning)
|
||||||
Fk:loadTranslationTable{
|
Fk:loadTranslationTable{
|
||||||
["ganning"] = "甘宁",
|
["ganning"] = "甘宁",
|
||||||
}
|
}
|
||||||
|
|
||||||
local lvmeng = General:new(extension, "lvmeng", "wu", 4)
|
local lvmeng = General:new(extension, "lvmeng", "wu", 4)
|
||||||
extension:addGeneral(lvmeng)
|
extension:addGeneral(lvmeng)
|
||||||
Fk:loadTranslationTable{
|
Fk:loadTranslationTable{
|
||||||
["lvmeng"] = "吕蒙",
|
["lvmeng"] = "吕蒙",
|
||||||
}
|
}
|
||||||
|
|
||||||
local huanggai = General:new(extension, "huanggai", "wu", 4)
|
local huanggai = General:new(extension, "huanggai", "wu", 4)
|
||||||
extension:addGeneral(huanggai)
|
extension:addGeneral(huanggai)
|
||||||
Fk:loadTranslationTable{
|
Fk:loadTranslationTable{
|
||||||
["huanggai"] = "黄盖",
|
["huanggai"] = "黄盖",
|
||||||
}
|
}
|
||||||
|
|
||||||
local zhouyu = General:new(extension, "zhouyu", "wu", 3)
|
local zhouyu = General:new(extension, "zhouyu", "wu", 3)
|
||||||
extension:addGeneral(zhouyu)
|
extension:addGeneral(zhouyu)
|
||||||
Fk:loadTranslationTable{
|
Fk:loadTranslationTable{
|
||||||
["zhouyu"] = "周瑜",
|
["zhouyu"] = "周瑜",
|
||||||
}
|
}
|
||||||
|
|
||||||
local daqiao = General:new(extension, "daqiao", "wu", 3)
|
local daqiao = General:new(extension, "daqiao", "wu", 3)
|
||||||
extension:addGeneral(daqiao)
|
extension:addGeneral(daqiao)
|
||||||
Fk:loadTranslationTable{
|
Fk:loadTranslationTable{
|
||||||
["daqiao"] = "大乔",
|
["daqiao"] = "大乔",
|
||||||
}
|
}
|
||||||
|
|
||||||
local luxun = General:new(extension, "luxun", "wu", 3)
|
local luxun = General:new(extension, "luxun", "wu", 3)
|
||||||
extension:addGeneral(luxun)
|
extension:addGeneral(luxun)
|
||||||
Fk:loadTranslationTable{
|
Fk:loadTranslationTable{
|
||||||
["luxun"] = "陆逊",
|
["luxun"] = "陆逊",
|
||||||
}
|
}
|
||||||
|
|
||||||
local sunshangxiang = General:new(extension, "sunshangxiang", "wu", 3)
|
local sunshangxiang = General:new(extension, "sunshangxiang", "wu", 3)
|
||||||
extension:addGeneral(sunshangxiang)
|
extension:addGeneral(sunshangxiang)
|
||||||
Fk:loadTranslationTable{
|
Fk:loadTranslationTable{
|
||||||
["sunshangxiang"] = "孙尚香",
|
["sunshangxiang"] = "孙尚香",
|
||||||
}
|
}
|
||||||
|
|
||||||
local huatuo = General:new(extension, "huatuo", "qun", 3)
|
local huatuo = General:new(extension, "huatuo", "qun", 3)
|
||||||
extension:addGeneral(huatuo)
|
extension:addGeneral(huatuo)
|
||||||
Fk:loadTranslationTable{
|
Fk:loadTranslationTable{
|
||||||
["huatuo"] = "华佗",
|
["huatuo"] = "华佗",
|
||||||
}
|
}
|
||||||
|
|
||||||
local lvbu = General:new(extension, "lvbu", "qun", 4)
|
local lvbu = General:new(extension, "lvbu", "qun", 4)
|
||||||
extension:addGeneral(lvbu)
|
extension:addGeneral(lvbu)
|
||||||
Fk:loadTranslationTable{
|
Fk:loadTranslationTable{
|
||||||
["lvbu"] = "吕布",
|
["lvbu"] = "吕布",
|
||||||
}
|
}
|
||||||
|
|
||||||
local diaochan = General:new(extension, "diaochan", "qun", 3)
|
local diaochan = General:new(extension, "diaochan", "qun", 3)
|
||||||
extension:addGeneral(diaochan)
|
extension:addGeneral(diaochan)
|
||||||
Fk:loadTranslationTable{
|
Fk:loadTranslationTable{
|
||||||
["diaochan"] = "貂蝉",
|
["diaochan"] = "貂蝉",
|
||||||
}
|
}
|
||||||
|
|
||||||
return extension
|
return extension
|
||||||
|
|
|
@ -1,14 +1,14 @@
|
||||||
return {
|
return {
|
||||||
name = "standard",
|
name = "standard",
|
||||||
author = "official",
|
author = "official",
|
||||||
description = "",
|
description = "",
|
||||||
collaborators = {
|
collaborators = {
|
||||||
program = {},
|
program = {},
|
||||||
designer = {},
|
designer = {},
|
||||||
cv = {},
|
cv = {},
|
||||||
illustrator = {},
|
illustrator = {},
|
||||||
},
|
},
|
||||||
|
|
||||||
dependencies = {},
|
dependencies = {},
|
||||||
extra_files = {},
|
extra_files = {},
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,510 +2,523 @@ local extension = Package:new("standard_cards", Package.CardPack)
|
||||||
extension.metadata = require "packages.standard_cards.metadata"
|
extension.metadata = require "packages.standard_cards.metadata"
|
||||||
|
|
||||||
Fk:loadTranslationTable{
|
Fk:loadTranslationTable{
|
||||||
["standard_cards"] = "标+EX"
|
["standard_cards"] = "标+EX"
|
||||||
}
|
}
|
||||||
|
|
||||||
local slash = fk.CreateBasicCard{
|
local slash = fk.CreateBasicCard{
|
||||||
name = "slash",
|
name = "slash",
|
||||||
number = 7,
|
number = 7,
|
||||||
suit = Card.Spade,
|
suit = Card.Spade,
|
||||||
}
|
}
|
||||||
Fk:loadTranslationTable{
|
Fk:loadTranslationTable{
|
||||||
["slash"] = "杀",
|
["slash"] = "杀",
|
||||||
}
|
}
|
||||||
|
|
||||||
extension:addCards({
|
extension:addCards({
|
||||||
slash,
|
slash,
|
||||||
slash:clone(Card.Spade, 8),
|
slash:clone(Card.Spade, 8),
|
||||||
slash:clone(Card.Spade, 8),
|
slash:clone(Card.Spade, 8),
|
||||||
slash:clone(Card.Spade, 9),
|
slash:clone(Card.Spade, 9),
|
||||||
slash:clone(Card.Spade, 9),
|
slash:clone(Card.Spade, 9),
|
||||||
slash:clone(Card.Spade, 10),
|
slash:clone(Card.Spade, 10),
|
||||||
slash:clone(Card.Spade, 10),
|
slash:clone(Card.Spade, 10),
|
||||||
|
|
||||||
slash:clone(Card.Club, 2),
|
slash:clone(Card.Club, 2),
|
||||||
slash:clone(Card.Club, 3),
|
slash:clone(Card.Club, 3),
|
||||||
slash:clone(Card.Club, 4),
|
slash:clone(Card.Club, 4),
|
||||||
slash:clone(Card.Club, 5),
|
slash:clone(Card.Club, 5),
|
||||||
slash:clone(Card.Club, 6),
|
slash:clone(Card.Club, 6),
|
||||||
slash:clone(Card.Club, 7),
|
slash:clone(Card.Club, 7),
|
||||||
slash:clone(Card.Club, 8),
|
slash:clone(Card.Club, 8),
|
||||||
slash:clone(Card.Club, 8),
|
slash:clone(Card.Club, 8),
|
||||||
slash:clone(Card.Club, 9),
|
slash:clone(Card.Club, 9),
|
||||||
slash:clone(Card.Club, 9),
|
slash:clone(Card.Club, 9),
|
||||||
slash:clone(Card.Club, 10),
|
slash:clone(Card.Club, 10),
|
||||||
slash:clone(Card.Club, 10),
|
slash:clone(Card.Club, 10),
|
||||||
slash:clone(Card.Club, 11),
|
slash:clone(Card.Club, 11),
|
||||||
slash:clone(Card.Club, 11),
|
slash:clone(Card.Club, 11),
|
||||||
|
|
||||||
slash:clone(Card.Heart, 10),
|
slash:clone(Card.Heart, 10),
|
||||||
slash:clone(Card.Heart, 10),
|
slash:clone(Card.Heart, 10),
|
||||||
slash:clone(Card.Heart, 11),
|
slash:clone(Card.Heart, 11),
|
||||||
|
|
||||||
slash:clone(Card.Diamond, 6),
|
slash:clone(Card.Diamond, 6),
|
||||||
slash:clone(Card.Diamond, 7),
|
slash:clone(Card.Diamond, 7),
|
||||||
slash:clone(Card.Diamond, 8),
|
slash:clone(Card.Diamond, 8),
|
||||||
slash:clone(Card.Diamond, 9),
|
slash:clone(Card.Diamond, 9),
|
||||||
slash:clone(Card.Diamond, 10),
|
slash:clone(Card.Diamond, 10),
|
||||||
slash:clone(Card.Diamond, 13),
|
slash:clone(Card.Diamond, 13),
|
||||||
})
|
})
|
||||||
|
|
||||||
local jink = fk.CreateBasicCard{
|
local jink = fk.CreateBasicCard{
|
||||||
name = "jink",
|
name = "jink",
|
||||||
suit = Card.Heart,
|
suit = Card.Heart,
|
||||||
number = 2,
|
number = 2,
|
||||||
}
|
}
|
||||||
Fk:loadTranslationTable{
|
Fk:loadTranslationTable{
|
||||||
["jink"] = "闪",
|
["jink"] = "闪",
|
||||||
}
|
}
|
||||||
|
|
||||||
extension:addCards({
|
extension:addCards({
|
||||||
jink,
|
jink,
|
||||||
jink:clone(Card.Heart, 2),
|
jink:clone(Card.Heart, 2),
|
||||||
jink:clone(Card.Heart, 13),
|
jink:clone(Card.Heart, 13),
|
||||||
|
|
||||||
jink:clone(Card.Diamond, 2),
|
jink:clone(Card.Diamond, 2),
|
||||||
jink:clone(Card.Diamond, 2),
|
jink:clone(Card.Diamond, 2),
|
||||||
jink:clone(Card.Diamond, 3),
|
jink:clone(Card.Diamond, 3),
|
||||||
jink:clone(Card.Diamond, 4),
|
jink:clone(Card.Diamond, 4),
|
||||||
jink:clone(Card.Diamond, 5),
|
jink:clone(Card.Diamond, 5),
|
||||||
jink:clone(Card.Diamond, 6),
|
jink:clone(Card.Diamond, 6),
|
||||||
jink:clone(Card.Diamond, 7),
|
jink:clone(Card.Diamond, 7),
|
||||||
jink:clone(Card.Diamond, 8),
|
jink:clone(Card.Diamond, 8),
|
||||||
jink:clone(Card.Diamond, 9),
|
jink:clone(Card.Diamond, 9),
|
||||||
jink:clone(Card.Diamond, 10),
|
jink:clone(Card.Diamond, 10),
|
||||||
jink:clone(Card.Diamond, 11),
|
jink:clone(Card.Diamond, 11),
|
||||||
jink:clone(Card.Diamond, 11),
|
jink:clone(Card.Diamond, 11),
|
||||||
})
|
})
|
||||||
|
|
||||||
local peach = fk.CreateBasicCard{
|
local peach = fk.CreateBasicCard{
|
||||||
name = "peach",
|
name = "peach",
|
||||||
suit = Card.Heart,
|
suit = Card.Heart,
|
||||||
number = 3,
|
number = 3,
|
||||||
}
|
}
|
||||||
Fk:loadTranslationTable{
|
Fk:loadTranslationTable{
|
||||||
["peach"] = "桃",
|
["peach"] = "桃",
|
||||||
}
|
}
|
||||||
|
|
||||||
extension:addCards({
|
extension:addCards({
|
||||||
peach,
|
peach,
|
||||||
peach:clone(Card.Heart, 4),
|
peach:clone(Card.Heart, 4),
|
||||||
peach:clone(Card.Heart, 6),
|
peach:clone(Card.Heart, 6),
|
||||||
peach:clone(Card.Heart, 7),
|
peach:clone(Card.Heart, 7),
|
||||||
peach:clone(Card.Heart, 8),
|
peach:clone(Card.Heart, 8),
|
||||||
peach:clone(Card.Heart, 9),
|
peach:clone(Card.Heart, 9),
|
||||||
peach:clone(Card.Heart, 12),
|
peach:clone(Card.Heart, 12),
|
||||||
peach:clone(Card.Heart, 12),
|
peach:clone(Card.Heart, 12),
|
||||||
})
|
})
|
||||||
|
|
||||||
local dismantlement = fk.CreateTrickCard{
|
local dismantlement = fk.CreateTrickCard{
|
||||||
name = "dismantlement",
|
name = "dismantlement",
|
||||||
suit = Card.Spade,
|
suit = Card.Spade,
|
||||||
number = 3,
|
number = 3,
|
||||||
}
|
}
|
||||||
Fk:loadTranslationTable{
|
Fk:loadTranslationTable{
|
||||||
["dismantlement"] = "过河拆桥",
|
["dismantlement"] = "过河拆桥",
|
||||||
}
|
}
|
||||||
|
|
||||||
extension:addCards({
|
extension:addCards({
|
||||||
dismantlement,
|
dismantlement,
|
||||||
dismantlement:clone(Card.Spade, 4),
|
dismantlement:clone(Card.Spade, 4),
|
||||||
dismantlement:clone(Card.Spade, 12),
|
dismantlement:clone(Card.Spade, 12),
|
||||||
|
|
||||||
dismantlement:clone(Card.Club, 3),
|
dismantlement:clone(Card.Club, 3),
|
||||||
dismantlement:clone(Card.Club, 4),
|
dismantlement:clone(Card.Club, 4),
|
||||||
|
|
||||||
dismantlement:clone(Card.Heart, 12),
|
dismantlement:clone(Card.Heart, 12),
|
||||||
})
|
})
|
||||||
|
|
||||||
local snatch = fk.CreateTrickCard{
|
local snatch = fk.CreateTrickCard{
|
||||||
name = "snatch",
|
name = "snatch",
|
||||||
suit = Card.Spade,
|
suit = Card.Spade,
|
||||||
number = 3,
|
number = 3,
|
||||||
}
|
}
|
||||||
Fk:loadTranslationTable{
|
Fk:loadTranslationTable{
|
||||||
["snatch"] = "顺手牵羊",
|
["snatch"] = "顺手牵羊",
|
||||||
}
|
}
|
||||||
|
|
||||||
extension:addCards({
|
extension:addCards({
|
||||||
snatch,
|
snatch,
|
||||||
snatch:clone(Card.Spade, 4),
|
snatch:clone(Card.Spade, 4),
|
||||||
snatch:clone(Card.Spade, 11),
|
snatch:clone(Card.Spade, 11),
|
||||||
|
|
||||||
snatch:clone(Card.Diamond, 3),
|
snatch:clone(Card.Diamond, 3),
|
||||||
snatch:clone(Card.Diamond, 4),
|
snatch:clone(Card.Diamond, 4),
|
||||||
})
|
})
|
||||||
|
|
||||||
local duel = fk.CreateTrickCard{
|
local duel = fk.CreateTrickCard{
|
||||||
name = "duel",
|
name = "duel",
|
||||||
suit = Card.Spade,
|
suit = Card.Spade,
|
||||||
number = 1,
|
number = 1,
|
||||||
}
|
}
|
||||||
Fk:loadTranslationTable{
|
Fk:loadTranslationTable{
|
||||||
["duel"] = "决斗",
|
["duel"] = "决斗",
|
||||||
}
|
}
|
||||||
|
|
||||||
extension:addCards({
|
extension:addCards({
|
||||||
duel,
|
duel,
|
||||||
|
|
||||||
duel:clone(Card.Club, 1),
|
duel:clone(Card.Club, 1),
|
||||||
|
|
||||||
duel:clone(Card.Diamond, 1),
|
duel:clone(Card.Diamond, 1),
|
||||||
})
|
})
|
||||||
|
|
||||||
local collateral = fk.CreateTrickCard{
|
local collateral = fk.CreateTrickCard{
|
||||||
name = "collateral",
|
name = "collateral",
|
||||||
suit = Card.Club,
|
suit = Card.Club,
|
||||||
number = 12,
|
number = 12,
|
||||||
}
|
}
|
||||||
Fk:loadTranslationTable{
|
Fk:loadTranslationTable{
|
||||||
["collateral"] = "借刀杀人",
|
["collateral"] = "借刀杀人",
|
||||||
}
|
}
|
||||||
|
|
||||||
extension:addCards({
|
extension:addCards({
|
||||||
collateral,
|
collateral,
|
||||||
collateral:clone(Card.Club, 13),
|
collateral:clone(Card.Club, 13),
|
||||||
})
|
})
|
||||||
|
|
||||||
|
local exNihiloSkill = fk.CreateActiveSkill{
|
||||||
|
name = "ex_nihilo_skill",
|
||||||
|
on_use = function(self, room, cardUseEvent)
|
||||||
|
if not cardUseEvent.tos or #TargetGroup:getRealTargets(cardUseEvent.tos) == 0 then
|
||||||
|
cardUseEvent.tos = { { cardUseEvent.from } }
|
||||||
|
end
|
||||||
|
end,
|
||||||
|
on_effect = function(self, room, cardEffectEvent)
|
||||||
|
room:drawCards(room:getPlayerById(TargetGroup:getRealTargets(cardEffectEvent.tos)[1]), 2, "ex_nihilo")
|
||||||
|
end
|
||||||
|
}
|
||||||
|
|
||||||
local exNihilo = fk.CreateTrickCard{
|
local exNihilo = fk.CreateTrickCard{
|
||||||
name = "ex_nihilo",
|
name = "ex_nihilo",
|
||||||
suit = Card.Heart,
|
suit = Card.Heart,
|
||||||
number = 7,
|
number = 7,
|
||||||
|
skill = exNihiloSkill,
|
||||||
}
|
}
|
||||||
Fk:loadTranslationTable{
|
Fk:loadTranslationTable{
|
||||||
["ex_nihilo"] = "无中生有",
|
["ex_nihilo"] = "无中生有",
|
||||||
}
|
}
|
||||||
|
|
||||||
extension:addCards({
|
extension:addCards({
|
||||||
exNihilo,
|
exNihilo,
|
||||||
exNihilo:clone(Card.Heart, 8),
|
exNihilo:clone(Card.Heart, 8),
|
||||||
exNihilo:clone(Card.Heart, 9),
|
exNihilo:clone(Card.Heart, 9),
|
||||||
exNihilo:clone(Card.Heart, 11),
|
exNihilo:clone(Card.Heart, 11),
|
||||||
})
|
})
|
||||||
|
|
||||||
local nullification = fk.CreateTrickCard{
|
local nullification = fk.CreateTrickCard{
|
||||||
name = "nullification",
|
name = "nullification",
|
||||||
suit = Card.Spade,
|
suit = Card.Spade,
|
||||||
number = 11,
|
number = 11,
|
||||||
}
|
}
|
||||||
Fk:loadTranslationTable{
|
Fk:loadTranslationTable{
|
||||||
["nullification"] = "无懈可击",
|
["nullification"] = "无懈可击",
|
||||||
}
|
}
|
||||||
|
|
||||||
extension:addCards({
|
extension:addCards({
|
||||||
nullification,
|
nullification,
|
||||||
|
|
||||||
nullification:clone(Card.Club, 12),
|
nullification:clone(Card.Club, 12),
|
||||||
nullification:clone(Card.Club, 13),
|
nullification:clone(Card.Club, 13),
|
||||||
|
|
||||||
nullification:clone(Card.Diamond, 12),
|
nullification:clone(Card.Diamond, 12),
|
||||||
})
|
})
|
||||||
|
|
||||||
local savageAssault = fk.CreateTrickCard{
|
local savageAssault = fk.CreateTrickCard{
|
||||||
name = "savage_assault",
|
name = "savage_assault",
|
||||||
suit = Card.Spade,
|
suit = Card.Spade,
|
||||||
number = 7,
|
number = 7,
|
||||||
}
|
}
|
||||||
Fk:loadTranslationTable{
|
Fk:loadTranslationTable{
|
||||||
["savage_assault"] = "南蛮入侵",
|
["savage_assault"] = "南蛮入侵",
|
||||||
}
|
}
|
||||||
|
|
||||||
extension:addCards({
|
extension:addCards({
|
||||||
savageAssault,
|
savageAssault,
|
||||||
savageAssault:clone(Card.Spade, 13),
|
savageAssault:clone(Card.Spade, 13),
|
||||||
savageAssault:clone(Card.Club, 7),
|
savageAssault:clone(Card.Club, 7),
|
||||||
})
|
})
|
||||||
|
|
||||||
local archeryAttack = fk.CreateTrickCard{
|
local archeryAttack = fk.CreateTrickCard{
|
||||||
name = "archery_attack",
|
name = "archery_attack",
|
||||||
suit = Card.Heart,
|
suit = Card.Heart,
|
||||||
number = 1,
|
number = 1,
|
||||||
}
|
}
|
||||||
Fk:loadTranslationTable{
|
Fk:loadTranslationTable{
|
||||||
["archery_attack"] = "万箭齐发",
|
["archery_attack"] = "万箭齐发",
|
||||||
}
|
}
|
||||||
|
|
||||||
extension:addCards({
|
extension:addCards({
|
||||||
archeryAttack,
|
archeryAttack,
|
||||||
})
|
})
|
||||||
|
|
||||||
local godSalvation = fk.CreateTrickCard{
|
local godSalvation = fk.CreateTrickCard{
|
||||||
name = "god_salvation",
|
name = "god_salvation",
|
||||||
suit = Card.Heart,
|
suit = Card.Heart,
|
||||||
number = 1,
|
number = 1,
|
||||||
}
|
}
|
||||||
Fk:loadTranslationTable{
|
Fk:loadTranslationTable{
|
||||||
["god_salvation"] = "桃园结义",
|
["god_salvation"] = "桃园结义",
|
||||||
}
|
}
|
||||||
|
|
||||||
extension:addCards({
|
extension:addCards({
|
||||||
godSalvation,
|
godSalvation,
|
||||||
})
|
})
|
||||||
|
|
||||||
local amazingGrace = fk.CreateTrickCard{
|
local amazingGrace = fk.CreateTrickCard{
|
||||||
name = "amazing_grace",
|
name = "amazing_grace",
|
||||||
suit = Card.Heart,
|
suit = Card.Heart,
|
||||||
number = 3,
|
number = 3,
|
||||||
}
|
}
|
||||||
Fk:loadTranslationTable{
|
Fk:loadTranslationTable{
|
||||||
["amazing_grace"] = "五谷丰登",
|
["amazing_grace"] = "五谷丰登",
|
||||||
}
|
}
|
||||||
|
|
||||||
extension:addCards({
|
extension:addCards({
|
||||||
amazingGrace,
|
amazingGrace,
|
||||||
amazingGrace:clone(Card.Heart, 4),
|
amazingGrace:clone(Card.Heart, 4),
|
||||||
})
|
})
|
||||||
|
|
||||||
local lightning = fk.CreateDelayedTrickCard{
|
local lightning = fk.CreateDelayedTrickCard{
|
||||||
name = "lightning",
|
name = "lightning",
|
||||||
suit = Card.Spade,
|
suit = Card.Spade,
|
||||||
number = 1,
|
number = 1,
|
||||||
}
|
}
|
||||||
Fk:loadTranslationTable{
|
Fk:loadTranslationTable{
|
||||||
["lightning"] = "闪电",
|
["lightning"] = "闪电",
|
||||||
}
|
}
|
||||||
|
|
||||||
extension:addCards({
|
extension:addCards({
|
||||||
lightning,
|
lightning,
|
||||||
lightning:clone(Card.Heart, 12),
|
lightning:clone(Card.Heart, 12),
|
||||||
})
|
})
|
||||||
|
|
||||||
local indulgence = fk.CreateDelayedTrickCard{
|
local indulgence = fk.CreateDelayedTrickCard{
|
||||||
name = "indulgence",
|
name = "indulgence",
|
||||||
suit = Card.Spade,
|
suit = Card.Spade,
|
||||||
number = 6,
|
number = 6,
|
||||||
}
|
}
|
||||||
Fk:loadTranslationTable{
|
Fk:loadTranslationTable{
|
||||||
["indulgence"] = "乐不思蜀",
|
["indulgence"] = "乐不思蜀",
|
||||||
}
|
}
|
||||||
|
|
||||||
extension:addCards({
|
extension:addCards({
|
||||||
indulgence,
|
indulgence,
|
||||||
indulgence:clone(Card.Club, 6),
|
indulgence:clone(Card.Club, 6),
|
||||||
indulgence:clone(Card.Heart, 6),
|
indulgence:clone(Card.Heart, 6),
|
||||||
})
|
})
|
||||||
|
|
||||||
local crossbow = fk.CreateWeapon{
|
local crossbow = fk.CreateWeapon{
|
||||||
name = "crossbow",
|
name = "crossbow",
|
||||||
suit = Card.Club,
|
suit = Card.Club,
|
||||||
number = 1,
|
number = 1,
|
||||||
}
|
}
|
||||||
Fk:loadTranslationTable{
|
Fk:loadTranslationTable{
|
||||||
["crossbow"] = "诸葛连弩",
|
["crossbow"] = "诸葛连弩",
|
||||||
}
|
}
|
||||||
|
|
||||||
extension:addCards({
|
extension:addCards({
|
||||||
crossbow,
|
crossbow,
|
||||||
crossbow:clone(Card.Diamond, 1),
|
crossbow:clone(Card.Diamond, 1),
|
||||||
})
|
})
|
||||||
|
|
||||||
local qingGang = fk.CreateWeapon{
|
local qingGang = fk.CreateWeapon{
|
||||||
name = "qinggang_sword",
|
name = "qinggang_sword",
|
||||||
suit = Card.Spade,
|
suit = Card.Spade,
|
||||||
number = 6,
|
number = 6,
|
||||||
}
|
}
|
||||||
Fk:loadTranslationTable{
|
Fk:loadTranslationTable{
|
||||||
["qinggang_sword"] = "青釭剑",
|
["qinggang_sword"] = "青釭剑",
|
||||||
}
|
}
|
||||||
|
|
||||||
extension:addCards({
|
extension:addCards({
|
||||||
qingGang,
|
qingGang,
|
||||||
})
|
})
|
||||||
|
|
||||||
local iceSword = fk.CreateWeapon{
|
local iceSword = fk.CreateWeapon{
|
||||||
name = "ice_sword",
|
name = "ice_sword",
|
||||||
suit = Card.Spade,
|
suit = Card.Spade,
|
||||||
number = 2,
|
number = 2,
|
||||||
}
|
}
|
||||||
Fk:loadTranslationTable{
|
Fk:loadTranslationTable{
|
||||||
["ice_sword"] = "寒冰剑",
|
["ice_sword"] = "寒冰剑",
|
||||||
}
|
}
|
||||||
|
|
||||||
extension:addCards({
|
extension:addCards({
|
||||||
iceSword,
|
iceSword,
|
||||||
})
|
})
|
||||||
|
|
||||||
local doubleSwords = fk.CreateWeapon{
|
local doubleSwords = fk.CreateWeapon{
|
||||||
name = "double_swords",
|
name = "double_swords",
|
||||||
suit = Card.Spade,
|
suit = Card.Spade,
|
||||||
number = 2,
|
number = 2,
|
||||||
}
|
}
|
||||||
Fk:loadTranslationTable{
|
Fk:loadTranslationTable{
|
||||||
["double_swords"] = "雌雄双股剑",
|
["double_swords"] = "雌雄双股剑",
|
||||||
}
|
}
|
||||||
|
|
||||||
extension:addCards({
|
extension:addCards({
|
||||||
doubleSwords,
|
doubleSwords,
|
||||||
})
|
})
|
||||||
|
|
||||||
local blade = fk.CreateWeapon{
|
local blade = fk.CreateWeapon{
|
||||||
name = "blade",
|
name = "blade",
|
||||||
suit = Card.Spade,
|
suit = Card.Spade,
|
||||||
number = 5,
|
number = 5,
|
||||||
}
|
}
|
||||||
Fk:loadTranslationTable{
|
Fk:loadTranslationTable{
|
||||||
["blade"] = "青龙偃月刀",
|
["blade"] = "青龙偃月刀",
|
||||||
}
|
}
|
||||||
|
|
||||||
extension:addCards({
|
extension:addCards({
|
||||||
blade,
|
blade,
|
||||||
})
|
})
|
||||||
|
|
||||||
local spear = fk.CreateWeapon{
|
local spear = fk.CreateWeapon{
|
||||||
name = "spear",
|
name = "spear",
|
||||||
suit = Card.Spade,
|
suit = Card.Spade,
|
||||||
number = 12,
|
number = 12,
|
||||||
}
|
}
|
||||||
Fk:loadTranslationTable{
|
Fk:loadTranslationTable{
|
||||||
["spear"] = "丈八蛇矛",
|
["spear"] = "丈八蛇矛",
|
||||||
}
|
}
|
||||||
|
|
||||||
extension:addCards({
|
extension:addCards({
|
||||||
spear,
|
spear,
|
||||||
})
|
})
|
||||||
|
|
||||||
local axe = fk.CreateWeapon{
|
local axe = fk.CreateWeapon{
|
||||||
name = "axe",
|
name = "axe",
|
||||||
suit = Card.Diamond,
|
suit = Card.Diamond,
|
||||||
number = 5,
|
number = 5,
|
||||||
}
|
}
|
||||||
Fk:loadTranslationTable{
|
Fk:loadTranslationTable{
|
||||||
["axe"] = "贯石斧",
|
["axe"] = "贯石斧",
|
||||||
}
|
}
|
||||||
|
|
||||||
extension:addCards({
|
extension:addCards({
|
||||||
axe,
|
axe,
|
||||||
})
|
})
|
||||||
|
|
||||||
local halberd = fk.CreateWeapon{
|
local halberd = fk.CreateWeapon{
|
||||||
name = "halberd",
|
name = "halberd",
|
||||||
suit = Card.Diamond,
|
suit = Card.Diamond,
|
||||||
number = 12,
|
number = 12,
|
||||||
}
|
}
|
||||||
Fk:loadTranslationTable{
|
Fk:loadTranslationTable{
|
||||||
["halberd"] = "方天画戟",
|
["halberd"] = "方天画戟",
|
||||||
}
|
}
|
||||||
|
|
||||||
extension:addCards({
|
extension:addCards({
|
||||||
halberd,
|
halberd,
|
||||||
})
|
})
|
||||||
|
|
||||||
local kylinBow = fk.CreateWeapon{
|
local kylinBow = fk.CreateWeapon{
|
||||||
name = "kylin_bow",
|
name = "kylin_bow",
|
||||||
suit = Card.Heart,
|
suit = Card.Heart,
|
||||||
number = 5,
|
number = 5,
|
||||||
}
|
}
|
||||||
Fk:loadTranslationTable{
|
Fk:loadTranslationTable{
|
||||||
["kylin_bow"] = "麒麟弓",
|
["kylin_bow"] = "麒麟弓",
|
||||||
}
|
}
|
||||||
|
|
||||||
extension:addCards({
|
extension:addCards({
|
||||||
kylinBow,
|
kylinBow,
|
||||||
})
|
})
|
||||||
|
|
||||||
local eightDiagram = fk.CreateArmor{
|
local eightDiagram = fk.CreateArmor{
|
||||||
name = "eight_diagram",
|
name = "eight_diagram",
|
||||||
suit = Card.Spade,
|
suit = Card.Spade,
|
||||||
number = 2,
|
number = 2,
|
||||||
}
|
}
|
||||||
Fk:loadTranslationTable{
|
Fk:loadTranslationTable{
|
||||||
["eight_diagram"] = "八卦阵",
|
["eight_diagram"] = "八卦阵",
|
||||||
}
|
}
|
||||||
|
|
||||||
extension:addCards({
|
extension:addCards({
|
||||||
eightDiagram,
|
eightDiagram,
|
||||||
eightDiagram:clone(Card.Club, 2),
|
eightDiagram:clone(Card.Club, 2),
|
||||||
})
|
})
|
||||||
|
|
||||||
local niohShield = fk.CreateArmor{
|
local niohShield = fk.CreateArmor{
|
||||||
name = "nioh_shield",
|
name = "nioh_shield",
|
||||||
suit = Card.Club,
|
suit = Card.Club,
|
||||||
number = 2,
|
number = 2,
|
||||||
}
|
}
|
||||||
Fk:loadTranslationTable{
|
Fk:loadTranslationTable{
|
||||||
["nioh_shield"] = "仁王盾",
|
["nioh_shield"] = "仁王盾",
|
||||||
}
|
}
|
||||||
|
|
||||||
extension:addCards({
|
extension:addCards({
|
||||||
niohShield,
|
niohShield,
|
||||||
})
|
})
|
||||||
|
|
||||||
local diLu = fk.CreateDefensiveRide{
|
local diLu = fk.CreateDefensiveRide{
|
||||||
name = "dilu",
|
name = "dilu",
|
||||||
suit = Card.Club,
|
suit = Card.Club,
|
||||||
number = 5,
|
number = 5,
|
||||||
}
|
}
|
||||||
Fk:loadTranslationTable{
|
Fk:loadTranslationTable{
|
||||||
["dilu"] = "的卢",
|
["dilu"] = "的卢",
|
||||||
}
|
}
|
||||||
|
|
||||||
extension:addCards({
|
extension:addCards({
|
||||||
diLu,
|
diLu,
|
||||||
})
|
})
|
||||||
|
|
||||||
local jueYing = fk.CreateDefensiveRide{
|
local jueYing = fk.CreateDefensiveRide{
|
||||||
name = "jueying",
|
name = "jueying",
|
||||||
suit = Card.Spade,
|
suit = Card.Spade,
|
||||||
number = 5,
|
number = 5,
|
||||||
}
|
}
|
||||||
Fk:loadTranslationTable{
|
Fk:loadTranslationTable{
|
||||||
["jueying"] = "绝影",
|
["jueying"] = "绝影",
|
||||||
}
|
}
|
||||||
|
|
||||||
extension:addCards({
|
extension:addCards({
|
||||||
jueYing,
|
jueYing,
|
||||||
})
|
})
|
||||||
|
|
||||||
local zhuaHuangFeiDian = fk.CreateDefensiveRide{
|
local zhuaHuangFeiDian = fk.CreateDefensiveRide{
|
||||||
name = "zhuahuangfeidian",
|
name = "zhuahuangfeidian",
|
||||||
suit = Card.Heart,
|
suit = Card.Heart,
|
||||||
number = 13,
|
number = 13,
|
||||||
}
|
}
|
||||||
Fk:loadTranslationTable{
|
Fk:loadTranslationTable{
|
||||||
["zhuahuangfeidian"] = "爪黄飞电",
|
["zhuahuangfeidian"] = "爪黄飞电",
|
||||||
}
|
}
|
||||||
|
|
||||||
extension:addCards({
|
extension:addCards({
|
||||||
zhuaHuangFeiDian,
|
zhuaHuangFeiDian,
|
||||||
})
|
})
|
||||||
|
|
||||||
local chiTu = fk.CreateOffensiveRide{
|
local chiTu = fk.CreateOffensiveRide{
|
||||||
name = "chitu",
|
name = "chitu",
|
||||||
suit = Card.Heart,
|
suit = Card.Heart,
|
||||||
number = 5,
|
number = 5,
|
||||||
}
|
}
|
||||||
Fk:loadTranslationTable{
|
Fk:loadTranslationTable{
|
||||||
["chitu"] = "赤兔",
|
["chitu"] = "赤兔",
|
||||||
}
|
}
|
||||||
|
|
||||||
extension:addCards({
|
extension:addCards({
|
||||||
chiTu,
|
chiTu,
|
||||||
})
|
})
|
||||||
|
|
||||||
local daYuan = fk.CreateOffensiveRide{
|
local daYuan = fk.CreateOffensiveRide{
|
||||||
name = "dayuan",
|
name = "dayuan",
|
||||||
suit = Card.Spade,
|
suit = Card.Spade,
|
||||||
number = 13,
|
number = 13,
|
||||||
}
|
}
|
||||||
Fk:loadTranslationTable{
|
Fk:loadTranslationTable{
|
||||||
["dayuan"] = "大宛",
|
["dayuan"] = "大宛",
|
||||||
}
|
}
|
||||||
|
|
||||||
extension:addCards({
|
extension:addCards({
|
||||||
daYuan,
|
daYuan,
|
||||||
})
|
})
|
||||||
|
|
||||||
local ziXing = fk.CreateOffensiveRide{
|
local ziXing = fk.CreateOffensiveRide{
|
||||||
name = "zixing",
|
name = "zixing",
|
||||||
suit = Card.Heart,
|
suit = Card.Heart,
|
||||||
number = 5,
|
number = 5,
|
||||||
}
|
}
|
||||||
Fk:loadTranslationTable{
|
Fk:loadTranslationTable{
|
||||||
["zixing"] = "紫骍",
|
["zixing"] = "紫骍",
|
||||||
}
|
}
|
||||||
|
|
||||||
extension:addCards({
|
extension:addCards({
|
||||||
ziXing,
|
ziXing,
|
||||||
})
|
})
|
||||||
|
|
||||||
return extension
|
return extension
|
||||||
|
|
|
@ -1,14 +1,14 @@
|
||||||
return {
|
return {
|
||||||
name = "standard_cards",
|
name = "standard_cards",
|
||||||
author = "official",
|
author = "official",
|
||||||
description = "",
|
description = "",
|
||||||
collaborators = {
|
collaborators = {
|
||||||
program = {},
|
program = {},
|
||||||
designer = {},
|
designer = {},
|
||||||
cv = {},
|
cv = {},
|
||||||
illustrator = {},
|
illustrator = {},
|
||||||
},
|
},
|
||||||
|
|
||||||
dependencies = {},
|
dependencies = {},
|
||||||
extra_files = {},
|
extra_files = {},
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,13 +1,13 @@
|
||||||
import QtQuick 2.15
|
import QtQuick 2.15
|
||||||
|
|
||||||
QtObject {
|
QtObject {
|
||||||
// Client configuration
|
// Client configuration
|
||||||
|
|
||||||
// Player property of client
|
// Player property of client
|
||||||
property string screenName: ""
|
property string screenName: ""
|
||||||
property string password: ""
|
property string password: ""
|
||||||
|
|
||||||
// Client data
|
// Client data
|
||||||
property int roomCapacity: 0
|
property int roomCapacity: 0
|
||||||
property int roomTimeout: 0
|
property int roomTimeout: 0
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,62 +3,62 @@ import QtQuick.Controls 2.0
|
||||||
import QtQuick.Layouts 1.15
|
import QtQuick.Layouts 1.15
|
||||||
|
|
||||||
Item {
|
Item {
|
||||||
id: root
|
id: root
|
||||||
|
|
||||||
width: childrenRect.width
|
width: childrenRect.width
|
||||||
height: childrenRect.height
|
height: childrenRect.height
|
||||||
|
|
||||||
signal finished()
|
signal finished()
|
||||||
|
|
||||||
ColumnLayout {
|
ColumnLayout {
|
||||||
spacing: 20
|
spacing: 20
|
||||||
|
|
||||||
RowLayout {
|
RowLayout {
|
||||||
anchors.rightMargin: 8
|
anchors.rightMargin: 8
|
||||||
spacing: 16
|
spacing: 16
|
||||||
Text {
|
Text {
|
||||||
text: "Room Name"
|
text: "Room Name"
|
||||||
}
|
}
|
||||||
TextField {
|
TextField {
|
||||||
id: roomName
|
id: roomName
|
||||||
font.pixelSize: 18
|
font.pixelSize: 18
|
||||||
text: Self.screenName + "'s Room"
|
text: Self.screenName + "'s Room"
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
RowLayout {
|
|
||||||
anchors.rightMargin: 8
|
|
||||||
spacing: 16
|
|
||||||
Text {
|
|
||||||
text: "Player num"
|
|
||||||
}
|
|
||||||
SpinBox {
|
|
||||||
id: playerNum
|
|
||||||
from: 2
|
|
||||||
to: 8
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
RowLayout {
|
|
||||||
anchors.rightMargin: 8
|
|
||||||
spacing: 16
|
|
||||||
Button {
|
|
||||||
text: "OK"
|
|
||||||
onClicked: {
|
|
||||||
root.finished();
|
|
||||||
mainWindow.busy = true;
|
|
||||||
ClientInstance.notifyServer(
|
|
||||||
"CreateRoom",
|
|
||||||
JSON.stringify([roomName.text, playerNum.value])
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Button {
|
|
||||||
text: "Cancel"
|
|
||||||
onClicked: {
|
|
||||||
root.finished();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
RowLayout {
|
||||||
|
anchors.rightMargin: 8
|
||||||
|
spacing: 16
|
||||||
|
Text {
|
||||||
|
text: "Player num"
|
||||||
|
}
|
||||||
|
SpinBox {
|
||||||
|
id: playerNum
|
||||||
|
from: 2
|
||||||
|
to: 8
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
RowLayout {
|
||||||
|
anchors.rightMargin: 8
|
||||||
|
spacing: 16
|
||||||
|
Button {
|
||||||
|
text: "OK"
|
||||||
|
onClicked: {
|
||||||
|
root.finished();
|
||||||
|
mainWindow.busy = true;
|
||||||
|
ClientInstance.notifyServer(
|
||||||
|
"CreateRoom",
|
||||||
|
JSON.stringify([roomName.text, playerNum.value])
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Button {
|
||||||
|
text: "Cancel"
|
||||||
|
onClicked: {
|
||||||
|
root.finished();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,98 +3,98 @@ import QtQuick.Controls 2.0
|
||||||
import QtQuick.Layouts 1.15
|
import QtQuick.Layouts 1.15
|
||||||
|
|
||||||
Item {
|
Item {
|
||||||
id: root
|
id: root
|
||||||
|
|
||||||
width: childrenRect.width
|
width: childrenRect.width
|
||||||
height: childrenRect.height
|
height: childrenRect.height
|
||||||
|
|
||||||
signal finished()
|
signal finished()
|
||||||
|
|
||||||
ColumnLayout {
|
ColumnLayout {
|
||||||
spacing: 20
|
spacing: 20
|
||||||
|
|
||||||
RowLayout {
|
RowLayout {
|
||||||
anchors.rightMargin: 8
|
anchors.rightMargin: 8
|
||||||
spacing: 16
|
spacing: 16
|
||||||
Text {
|
Text {
|
||||||
text: "Username"
|
text: "Username"
|
||||||
}
|
}
|
||||||
Text {
|
Text {
|
||||||
text: Self.screenName
|
text: Self.screenName
|
||||||
font.pixelSize: 18
|
font.pixelSize: 18
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
RowLayout {
|
|
||||||
anchors.rightMargin: 8
|
|
||||||
spacing: 16
|
|
||||||
Text {
|
|
||||||
text: "Avatar"
|
|
||||||
}
|
|
||||||
TextField {
|
|
||||||
id: avatarName
|
|
||||||
font.pixelSize: 18
|
|
||||||
text: Self.avatar
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
RowLayout {
|
|
||||||
anchors.rightMargin: 8
|
|
||||||
spacing: 16
|
|
||||||
Text {
|
|
||||||
text: "Old Password"
|
|
||||||
}
|
|
||||||
TextField {
|
|
||||||
id: oldPassword
|
|
||||||
echoMode: TextInput.Password
|
|
||||||
passwordCharacter: "*"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
RowLayout {
|
|
||||||
anchors.rightMargin: 8
|
|
||||||
spacing: 16
|
|
||||||
Text {
|
|
||||||
text: "New Password"
|
|
||||||
}
|
|
||||||
TextField {
|
|
||||||
id: newPassword
|
|
||||||
echoMode: TextInput.Password
|
|
||||||
passwordCharacter: "*"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
RowLayout {
|
|
||||||
anchors.rightMargin: 8
|
|
||||||
spacing: 16
|
|
||||||
Button {
|
|
||||||
text: "Update Avatar"
|
|
||||||
enabled: avatarName.text !== ""
|
|
||||||
onClicked: {
|
|
||||||
mainWindow.busy = true;
|
|
||||||
ClientInstance.notifyServer(
|
|
||||||
"UpdateAvatar",
|
|
||||||
JSON.stringify([avatarName.text])
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Button {
|
|
||||||
text: "Update Password"
|
|
||||||
enabled: oldPassword.text !== "" && newPassword.text !== ""
|
|
||||||
onClicked: {
|
|
||||||
mainWindow.busy = true;
|
|
||||||
ClientInstance.notifyServer(
|
|
||||||
"UpdatePassword",
|
|
||||||
JSON.stringify([oldPassword.text, newPassword.text])
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Button {
|
|
||||||
text: "Exit"
|
|
||||||
onClicked: {
|
|
||||||
root.finished();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
RowLayout {
|
||||||
|
anchors.rightMargin: 8
|
||||||
|
spacing: 16
|
||||||
|
Text {
|
||||||
|
text: "Avatar"
|
||||||
|
}
|
||||||
|
TextField {
|
||||||
|
id: avatarName
|
||||||
|
font.pixelSize: 18
|
||||||
|
text: Self.avatar
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
RowLayout {
|
||||||
|
anchors.rightMargin: 8
|
||||||
|
spacing: 16
|
||||||
|
Text {
|
||||||
|
text: "Old Password"
|
||||||
|
}
|
||||||
|
TextField {
|
||||||
|
id: oldPassword
|
||||||
|
echoMode: TextInput.Password
|
||||||
|
passwordCharacter: "*"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
RowLayout {
|
||||||
|
anchors.rightMargin: 8
|
||||||
|
spacing: 16
|
||||||
|
Text {
|
||||||
|
text: "New Password"
|
||||||
|
}
|
||||||
|
TextField {
|
||||||
|
id: newPassword
|
||||||
|
echoMode: TextInput.Password
|
||||||
|
passwordCharacter: "*"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
RowLayout {
|
||||||
|
anchors.rightMargin: 8
|
||||||
|
spacing: 16
|
||||||
|
Button {
|
||||||
|
text: "Update Avatar"
|
||||||
|
enabled: avatarName.text !== ""
|
||||||
|
onClicked: {
|
||||||
|
mainWindow.busy = true;
|
||||||
|
ClientInstance.notifyServer(
|
||||||
|
"UpdateAvatar",
|
||||||
|
JSON.stringify([avatarName.text])
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Button {
|
||||||
|
text: "Update Password"
|
||||||
|
enabled: oldPassword.text !== "" && newPassword.text !== ""
|
||||||
|
onClicked: {
|
||||||
|
mainWindow.busy = true;
|
||||||
|
ClientInstance.notifyServer(
|
||||||
|
"UpdatePassword",
|
||||||
|
JSON.stringify([oldPassword.text, newPassword.text])
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Button {
|
||||||
|
text: "Exit"
|
||||||
|
onClicked: {
|
||||||
|
root.finished();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import QtQuick 2.0
|
import QtQuick 2.0
|
||||||
|
|
||||||
Text {
|
Text {
|
||||||
text: "dsdsd"
|
text: "dsdsd"
|
||||||
}
|
}
|
66
qml/Logic.js
66
qml/Logic.js
|
@ -1,53 +1,53 @@
|
||||||
var callbacks = {};
|
var callbacks = {};
|
||||||
|
|
||||||
callbacks["NetworkDelayTest"] = function(jsonData) {
|
callbacks["NetworkDelayTest"] = function(jsonData) {
|
||||||
ClientInstance.notifyServer("Setup", JSON.stringify([
|
ClientInstance.notifyServer("Setup", JSON.stringify([
|
||||||
config.screenName,
|
config.screenName,
|
||||||
config.password
|
config.password
|
||||||
]));
|
]));
|
||||||
}
|
}
|
||||||
|
|
||||||
callbacks["ErrorMsg"] = function(jsonData) {
|
callbacks["ErrorMsg"] = function(jsonData) {
|
||||||
console.log("ERROR: " + jsonData);
|
console.log("ERROR: " + jsonData);
|
||||||
toast.show(jsonData, 5000);
|
toast.show(jsonData, 5000);
|
||||||
mainWindow.busy = false;
|
mainWindow.busy = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
callbacks["BackToStart"] = function(jsonData) {
|
callbacks["BackToStart"] = function(jsonData) {
|
||||||
while (mainStack.depth > 1) {
|
while (mainStack.depth > 1) {
|
||||||
mainStack.pop();
|
mainStack.pop();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
callbacks["EnterLobby"] = function(jsonData) {
|
callbacks["EnterLobby"] = function(jsonData) {
|
||||||
// depth == 1 means the lobby page is not present in mainStack
|
// depth == 1 means the lobby page is not present in mainStack
|
||||||
if (mainStack.depth === 1) {
|
if (mainStack.depth === 1) {
|
||||||
mainStack.push(lobby);
|
mainStack.push(lobby);
|
||||||
} else {
|
} else {
|
||||||
mainStack.pop();
|
mainStack.pop();
|
||||||
}
|
}
|
||||||
mainWindow.busy = false;
|
mainWindow.busy = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
callbacks["EnterRoom"] = function(jsonData) {
|
callbacks["EnterRoom"] = function(jsonData) {
|
||||||
// jsonData: int capacity, int timeout
|
// jsonData: int capacity, int timeout
|
||||||
let data = JSON.parse(jsonData);
|
let data = JSON.parse(jsonData);
|
||||||
config.roomCapacity = data[0];
|
config.roomCapacity = data[0];
|
||||||
config.roomTimeout = data[1];
|
config.roomTimeout = data[1];
|
||||||
mainStack.push(room);
|
mainStack.push(room);
|
||||||
mainWindow.busy = false;
|
mainWindow.busy = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
callbacks["UpdateRoomList"] = function(jsonData) {
|
callbacks["UpdateRoomList"] = function(jsonData) {
|
||||||
let current = mainStack.currentItem; // should be lobby
|
let current = mainStack.currentItem; // should be lobby
|
||||||
current.roomModel.clear();
|
current.roomModel.clear();
|
||||||
JSON.parse(jsonData).forEach(function(room) {
|
JSON.parse(jsonData).forEach(function(room) {
|
||||||
current.roomModel.append({
|
current.roomModel.append({
|
||||||
roomId: room[0],
|
roomId: room[0],
|
||||||
roomName: room[1],
|
roomName: room[1],
|
||||||
gameMode: room[2],
|
gameMode: room[2],
|
||||||
playerNum: room[3],
|
playerNum: room[3],
|
||||||
capacity: room[4],
|
capacity: room[4],
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,49 +4,49 @@ import QtQuick.Controls 2.0
|
||||||
import "RoomElement"
|
import "RoomElement"
|
||||||
|
|
||||||
Item {
|
Item {
|
||||||
id: root
|
id: root
|
||||||
|
|
||||||
property bool loaded: false
|
property bool loaded: false
|
||||||
|
|
||||||
ListView {
|
ListView {
|
||||||
width: Math.floor(root.width / 98) * 98
|
width: Math.floor(root.width / 98) * 98
|
||||||
height: parent.height
|
height: parent.height
|
||||||
anchors.centerIn: parent
|
anchors.centerIn: parent
|
||||||
ScrollBar.vertical: ScrollBar {}
|
ScrollBar.vertical: ScrollBar {}
|
||||||
model: ListModel {
|
model: ListModel {
|
||||||
id: packages
|
id: packages
|
||||||
}
|
}
|
||||||
|
|
||||||
delegate: ColumnLayout {
|
delegate: ColumnLayout {
|
||||||
Text { text: Backend.translate(name) }
|
Text { text: Backend.translate(name) }
|
||||||
GridLayout {
|
GridLayout {
|
||||||
columns: root.width / 98
|
columns: root.width / 98
|
||||||
Repeater {
|
Repeater {
|
||||||
model: JSON.parse(Backend.getCards(name))
|
model: JSON.parse(Backend.callLuaFunction("GetCards", [name]))
|
||||||
CardItem {
|
CardItem {
|
||||||
autoBack: false
|
autoBack: false
|
||||||
Component.onCompleted: {
|
Component.onCompleted: {
|
||||||
let data = JSON.parse(Backend.getCardData(modelData));
|
let data = JSON.parse(Backend.callLuaFunction("GetCardData", [modelData]));
|
||||||
setData(data);
|
setData(data);
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Button {
|
Button {
|
||||||
text: "Quit"
|
text: "Quit"
|
||||||
anchors.right: parent.right
|
anchors.right: parent.right
|
||||||
onClicked: {
|
onClicked: {
|
||||||
mainStack.pop();
|
mainStack.pop();
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function loadPackages() {
|
function loadPackages() {
|
||||||
if (loaded) return;
|
if (loaded) return;
|
||||||
let packs = JSON.parse(Backend.getAllCardPack());
|
let packs = JSON.parse(Backend.callLuaFunction("GetAllCardPack", []));
|
||||||
packs.forEach((name) => packages.append({ name: name }));
|
packs.forEach((name) => packages.append({ name: name }));
|
||||||
loaded = true;
|
loaded = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,50 +4,50 @@ import QtQuick.Controls 2.0
|
||||||
import "RoomElement"
|
import "RoomElement"
|
||||||
|
|
||||||
Item {
|
Item {
|
||||||
id: root
|
id: root
|
||||||
|
|
||||||
property bool loaded: false
|
property bool loaded: false
|
||||||
|
|
||||||
ListView {
|
ListView {
|
||||||
width: Math.floor(root.width / 98) * 98
|
width: Math.floor(root.width / 98) * 98
|
||||||
height: parent.height
|
height: parent.height
|
||||||
anchors.centerIn: parent
|
anchors.centerIn: parent
|
||||||
ScrollBar.vertical: ScrollBar {}
|
ScrollBar.vertical: ScrollBar {}
|
||||||
model: ListModel {
|
model: ListModel {
|
||||||
id: packages
|
id: packages
|
||||||
}
|
}
|
||||||
|
|
||||||
delegate: ColumnLayout {
|
delegate: ColumnLayout {
|
||||||
Text { text: Backend.translate(name) }
|
Text { text: Backend.translate(name) }
|
||||||
GridLayout {
|
GridLayout {
|
||||||
columns: root.width / 98
|
columns: root.width / 98
|
||||||
Repeater {
|
Repeater {
|
||||||
model: JSON.parse(Backend.getGenerals(name))
|
model: JSON.parse(Backend.callLuaFunction("GetGenerals", [name]))
|
||||||
GeneralCardItem {
|
GeneralCardItem {
|
||||||
autoBack: false
|
autoBack: false
|
||||||
Component.onCompleted: {
|
Component.onCompleted: {
|
||||||
let data = JSON.parse(Backend.getGeneralData(modelData));
|
let data = JSON.parse(Backend.callLuaFunction("GetGeneralData", [modelData]));
|
||||||
name = modelData;
|
name = modelData;
|
||||||
kingdom = data.kingdom;
|
kingdom = data.kingdom;
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Button {
|
Button {
|
||||||
text: "Quit"
|
text: "Quit"
|
||||||
anchors.right: parent.right
|
anchors.right: parent.right
|
||||||
onClicked: {
|
onClicked: {
|
||||||
mainStack.pop();
|
mainStack.pop();
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function loadPackages() {
|
function loadPackages() {
|
||||||
if (loaded) return;
|
if (loaded) return;
|
||||||
let packs = JSON.parse(Backend.getAllGeneralPack());
|
let packs = JSON.parse(Backend.callLuaFunction("GetAllGeneralPack", []));
|
||||||
packs.forEach((name) => packages.append({ name: name }));
|
packs.forEach((name) => packages.append({ name: name }));
|
||||||
loaded = true;
|
loaded = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,50 +2,50 @@ import QtQuick 2.15
|
||||||
import QtQuick.Controls 2.0
|
import QtQuick.Controls 2.0
|
||||||
|
|
||||||
Item {
|
Item {
|
||||||
id: root
|
id: root
|
||||||
|
|
||||||
Frame {
|
Frame {
|
||||||
id: join_server
|
id: join_server
|
||||||
anchors.centerIn: parent
|
anchors.centerIn: parent
|
||||||
Column {
|
Column {
|
||||||
spacing: 8
|
spacing: 8
|
||||||
TextField {
|
TextField {
|
||||||
id: server_addr
|
id: server_addr
|
||||||
text: "127.0.0.1"
|
text: "127.0.0.1"
|
||||||
}
|
}
|
||||||
TextField {
|
TextField {
|
||||||
id: screenNameEdit
|
id: screenNameEdit
|
||||||
text: "player"
|
text: "player"
|
||||||
}
|
}
|
||||||
/*TextField {
|
/*TextField {
|
||||||
id: avatarEdit
|
id: avatarEdit
|
||||||
text: "liubei"
|
text: "liubei"
|
||||||
}*/
|
}*/
|
||||||
TextField {
|
TextField {
|
||||||
id: passwordEdit
|
id: passwordEdit
|
||||||
text: ""
|
text: ""
|
||||||
echoMode: TextInput.Password
|
echoMode: TextInput.Password
|
||||||
passwordCharacter: "*"
|
passwordCharacter: "*"
|
||||||
}
|
}
|
||||||
Button {
|
Button {
|
||||||
text: "Join Server"
|
text: "Join Server"
|
||||||
onClicked: {
|
onClicked: {
|
||||||
config.screenName = screenNameEdit.text;
|
config.screenName = screenNameEdit.text;
|
||||||
config.password = passwordEdit.text;
|
config.password = passwordEdit.text;
|
||||||
mainWindow.busy = true;
|
mainWindow.busy = true;
|
||||||
Backend.joinServer(server_addr.text);
|
Backend.joinServer(server_addr.text);
|
||||||
}
|
|
||||||
}
|
|
||||||
Button {
|
|
||||||
text: "Console start"
|
|
||||||
onClicked: {
|
|
||||||
config.screenName = screenNameEdit.text;
|
|
||||||
config.password = passwordEdit.text;
|
|
||||||
mainWindow.busy = true;
|
|
||||||
Backend.startServer(9527);
|
|
||||||
Backend.joinServer("127.0.0.1");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
Button {
|
||||||
|
text: "Console start"
|
||||||
|
onClicked: {
|
||||||
|
config.screenName = screenNameEdit.text;
|
||||||
|
config.password = passwordEdit.text;
|
||||||
|
mainWindow.busy = true;
|
||||||
|
Backend.startServer(9527);
|
||||||
|
Backend.joinServer("127.0.0.1");
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,154 +5,154 @@ import QtQuick.Layouts 1.15
|
||||||
import "Logic.js" as Logic
|
import "Logic.js" as Logic
|
||||||
|
|
||||||
Item {
|
Item {
|
||||||
id: root
|
id: root
|
||||||
property alias roomModel: roomModel
|
property alias roomModel: roomModel
|
||||||
Component {
|
Component {
|
||||||
id: roomDelegate
|
id: roomDelegate
|
||||||
|
|
||||||
RowLayout {
|
|
||||||
width: roomList.width * 0.9
|
|
||||||
spacing: 16
|
|
||||||
Text {
|
|
||||||
text: roomId
|
|
||||||
}
|
|
||||||
|
|
||||||
Text {
|
|
||||||
horizontalAlignment: Text.AlignHCenter
|
|
||||||
Layout.fillWidth: true
|
|
||||||
text: roomName
|
|
||||||
}
|
|
||||||
|
|
||||||
Text {
|
|
||||||
text: gameMode
|
|
||||||
}
|
|
||||||
|
|
||||||
Text {
|
|
||||||
color: (playerNum == capacity) ? "red" : "black"
|
|
||||||
text: playerNum + "/" + capacity
|
|
||||||
}
|
|
||||||
|
|
||||||
Text {
|
|
||||||
text: "Enter"
|
|
||||||
font.underline: true
|
|
||||||
MouseArea {
|
|
||||||
anchors.fill: parent
|
|
||||||
hoverEnabled: true
|
|
||||||
onEntered: { parent.color = "blue" }
|
|
||||||
onExited: { parent.color = "black" }
|
|
||||||
onClicked: {
|
|
||||||
mainWindow.busy = true;
|
|
||||||
ClientInstance.notifyServer(
|
|
||||||
"EnterRoom",
|
|
||||||
JSON.stringify([roomId])
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ListModel {
|
|
||||||
id: roomModel
|
|
||||||
}
|
|
||||||
|
|
||||||
RowLayout {
|
RowLayout {
|
||||||
anchors.fill: parent
|
width: roomList.width * 0.9
|
||||||
Rectangle {
|
spacing: 16
|
||||||
Layout.preferredWidth: root.width * 0.7
|
Text {
|
||||||
Layout.fillHeight: true
|
text: roomId
|
||||||
color: "#e2e2e1"
|
}
|
||||||
radius: 4
|
|
||||||
Text {
|
|
||||||
width: parent.width
|
|
||||||
horizontalAlignment: Text.AlignHCenter
|
|
||||||
text: "Room List"
|
|
||||||
}
|
|
||||||
ListView {
|
|
||||||
height: parent.height * 0.9
|
|
||||||
width: parent.width * 0.95
|
|
||||||
contentHeight: roomDelegate.height * count
|
|
||||||
ScrollBar.vertical: ScrollBar {}
|
|
||||||
anchors.centerIn: parent
|
|
||||||
id: roomList
|
|
||||||
delegate: roomDelegate
|
|
||||||
model: roomModel
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ColumnLayout {
|
Text {
|
||||||
Button {
|
horizontalAlignment: Text.AlignHCenter
|
||||||
text: "Edit Profile"
|
Layout.fillWidth: true
|
||||||
onClicked: {
|
text: roomName
|
||||||
globalPopup.source = "EditProfile.qml";
|
}
|
||||||
globalPopup.open();
|
|
||||||
}
|
Text {
|
||||||
}
|
text: gameMode
|
||||||
Button {
|
}
|
||||||
text: "Create Room"
|
|
||||||
onClicked: {
|
Text {
|
||||||
globalPopup.source = "CreateRoom.qml";
|
color: (playerNum == capacity) ? "red" : "black"
|
||||||
globalPopup.open();
|
text: playerNum + "/" + capacity
|
||||||
}
|
}
|
||||||
}
|
|
||||||
Button {
|
Text {
|
||||||
text: "Generals Overview"
|
text: "Enter"
|
||||||
onClicked: {
|
font.underline: true
|
||||||
mainStack.push(generalsOverview);
|
MouseArea {
|
||||||
mainStack.currentItem.loadPackages();
|
anchors.fill: parent
|
||||||
}
|
hoverEnabled: true
|
||||||
}
|
onEntered: { parent.color = "blue" }
|
||||||
Button {
|
onExited: { parent.color = "black" }
|
||||||
text: "Cards Overview"
|
onClicked: {
|
||||||
onClicked: {
|
mainWindow.busy = true;
|
||||||
mainStack.push(cardsOverview);
|
ClientInstance.notifyServer(
|
||||||
mainStack.currentItem.loadPackages();
|
"EnterRoom",
|
||||||
}
|
JSON.stringify([roomId])
|
||||||
}
|
);
|
||||||
Button {
|
}
|
||||||
text: "Scenarios Overview"
|
|
||||||
}
|
|
||||||
Button {
|
|
||||||
text: "About"
|
|
||||||
}
|
|
||||||
Button {
|
|
||||||
text: "Exit Lobby"
|
|
||||||
onClicked: {
|
|
||||||
toast.show("Goodbye.");
|
|
||||||
Backend.quitLobby();
|
|
||||||
mainStack.pop();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ListModel {
|
||||||
|
id: roomModel
|
||||||
|
}
|
||||||
|
|
||||||
|
RowLayout {
|
||||||
|
anchors.fill: parent
|
||||||
|
Rectangle {
|
||||||
|
Layout.preferredWidth: root.width * 0.7
|
||||||
|
Layout.fillHeight: true
|
||||||
|
color: "#e2e2e1"
|
||||||
|
radius: 4
|
||||||
|
Text {
|
||||||
|
width: parent.width
|
||||||
|
horizontalAlignment: Text.AlignHCenter
|
||||||
|
text: "Room List"
|
||||||
|
}
|
||||||
|
ListView {
|
||||||
|
height: parent.height * 0.9
|
||||||
|
width: parent.width * 0.95
|
||||||
|
contentHeight: roomDelegate.height * count
|
||||||
|
ScrollBar.vertical: ScrollBar {}
|
||||||
|
anchors.centerIn: parent
|
||||||
|
id: roomList
|
||||||
|
delegate: roomDelegate
|
||||||
|
model: roomModel
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Loader {
|
ColumnLayout {
|
||||||
id: lobby_dialog
|
Button {
|
||||||
z: 1000
|
text: "Edit Profile"
|
||||||
onSourceChanged: {
|
onClicked: {
|
||||||
if (item === null)
|
globalPopup.source = "EditProfile.qml";
|
||||||
return;
|
globalPopup.open();
|
||||||
item.finished.connect(function(){
|
|
||||||
source = "";
|
|
||||||
});
|
|
||||||
item.widthChanged.connect(function(){
|
|
||||||
lobby_dialog.moveToCenter();
|
|
||||||
});
|
|
||||||
item.heightChanged.connect(function(){
|
|
||||||
lobby_dialog.moveToCenter();
|
|
||||||
});
|
|
||||||
moveToCenter();
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
Button {
|
||||||
|
text: "Create Room"
|
||||||
|
onClicked: {
|
||||||
|
globalPopup.source = "CreateRoom.qml";
|
||||||
|
globalPopup.open();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Button {
|
||||||
|
text: "Generals Overview"
|
||||||
|
onClicked: {
|
||||||
|
mainStack.push(generalsOverview);
|
||||||
|
mainStack.currentItem.loadPackages();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Button {
|
||||||
|
text: "Cards Overview"
|
||||||
|
onClicked: {
|
||||||
|
mainStack.push(cardsOverview);
|
||||||
|
mainStack.currentItem.loadPackages();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Button {
|
||||||
|
text: "Scenarios Overview"
|
||||||
|
}
|
||||||
|
Button {
|
||||||
|
text: "About"
|
||||||
|
}
|
||||||
|
Button {
|
||||||
|
text: "Exit Lobby"
|
||||||
|
onClicked: {
|
||||||
|
toast.show("Goodbye.");
|
||||||
|
Backend.quitLobby();
|
||||||
|
mainStack.pop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function moveToCenter()
|
Loader {
|
||||||
{
|
id: lobby_dialog
|
||||||
item.x = Math.round((root.width - item.width) / 2);
|
z: 1000
|
||||||
item.y = Math.round(root.height * 0.67 - item.height / 2);
|
onSourceChanged: {
|
||||||
}
|
if (item === null)
|
||||||
|
return;
|
||||||
|
item.finished.connect(function(){
|
||||||
|
source = "";
|
||||||
|
});
|
||||||
|
item.widthChanged.connect(function(){
|
||||||
|
lobby_dialog.moveToCenter();
|
||||||
|
});
|
||||||
|
item.heightChanged.connect(function(){
|
||||||
|
lobby_dialog.moveToCenter();
|
||||||
|
});
|
||||||
|
moveToCenter();
|
||||||
}
|
}
|
||||||
|
|
||||||
Component.onCompleted: {
|
function moveToCenter()
|
||||||
toast.show("Welcome to FreeKill lobby!");
|
{
|
||||||
|
item.x = Math.round((root.width - item.width) / 2);
|
||||||
|
item.y = Math.round(root.height * 0.67 - item.height / 2);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Component.onCompleted: {
|
||||||
|
toast.show("Welcome to FreeKill lobby!");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,13 +1,13 @@
|
||||||
callbacks["UpdateAvatar"] = function(jsonData) {
|
callbacks["UpdateAvatar"] = function(jsonData) {
|
||||||
mainWindow.busy = false;
|
mainWindow.busy = false;
|
||||||
Self.avatar = jsonData;
|
Self.avatar = jsonData;
|
||||||
toast.show("Update avatar done.");
|
toast.show("Update avatar done.");
|
||||||
}
|
}
|
||||||
|
|
||||||
callbacks["UpdatePassword"] = function(jsonData) {
|
callbacks["UpdatePassword"] = function(jsonData) {
|
||||||
mainWindow.busy = false;
|
mainWindow.busy = false;
|
||||||
if (jsonData === "1")
|
if (jsonData === "1")
|
||||||
toast.show("Update password done.");
|
toast.show("Update password done.");
|
||||||
else
|
else
|
||||||
toast.show("Old password wrong!", 5000);
|
toast.show("Old password wrong!", 5000);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,69 +1,69 @@
|
||||||
import QtQuick 2.15
|
import QtQuick 2.15
|
||||||
|
|
||||||
Item {
|
Item {
|
||||||
property bool enabled: true
|
property bool enabled: true
|
||||||
property alias text: title.text
|
property alias text: title.text
|
||||||
property alias textColor: title.color
|
property alias textColor: title.color
|
||||||
property alias textFont: title.font
|
property alias textFont: title.font
|
||||||
property alias backgroundColor: bg.color
|
property alias backgroundColor: bg.color
|
||||||
property alias border: bg.border
|
property alias border: bg.border
|
||||||
property alias iconSource: icon.source
|
property alias iconSource: icon.source
|
||||||
property int padding: 5
|
property int padding: 5
|
||||||
|
|
||||||
signal clicked
|
signal clicked
|
||||||
|
|
||||||
id: button
|
id: button
|
||||||
width: icon.width + title.implicitWidth + padding * 2
|
width: icon.width + title.implicitWidth + padding * 2
|
||||||
height: Math.max(icon.height, title.implicitHeight) + padding * 2
|
height: Math.max(icon.height, title.implicitHeight) + padding * 2
|
||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
id: bg
|
id: bg
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
color: "black"
|
color: "black"
|
||||||
border.width: 2
|
border.width: 2
|
||||||
border.color: "white"
|
border.color: "white"
|
||||||
opacity: 0.8
|
opacity: 0.8
|
||||||
|
}
|
||||||
|
|
||||||
|
states: [
|
||||||
|
State {
|
||||||
|
name: "hovered"; when: mouse.containsMouse
|
||||||
|
PropertyChanges { target: bg; color: "white" }
|
||||||
|
PropertyChanges { target: title; color: "black" }
|
||||||
|
},
|
||||||
|
State {
|
||||||
|
name: "disabled"; when: !enabled
|
||||||
|
PropertyChanges { target: button; opacity: 0.2 }
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
MouseArea {
|
||||||
|
id: mouse
|
||||||
|
anchors.fill: parent
|
||||||
|
hoverEnabled: parent.enabled
|
||||||
|
onReleased: if (parent.enabled) parent.clicked()
|
||||||
|
}
|
||||||
|
|
||||||
|
Row {
|
||||||
|
x: padding
|
||||||
|
y: padding
|
||||||
|
anchors.centerIn: parent
|
||||||
|
spacing: 5
|
||||||
|
|
||||||
|
Image {
|
||||||
|
id: icon
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
fillMode: Image.PreserveAspectFit
|
||||||
}
|
}
|
||||||
|
|
||||||
states: [
|
Text {
|
||||||
State {
|
id: title
|
||||||
name: "hovered"; when: mouse.containsMouse
|
font.pixelSize: 18
|
||||||
PropertyChanges { target: bg; color: "white" }
|
// font.family: "WenQuanYi Micro Hei"
|
||||||
PropertyChanges { target: title; color: "black" }
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
},
|
text: ""
|
||||||
State {
|
color: "white"
|
||||||
name: "disabled"; when: !enabled
|
|
||||||
PropertyChanges { target: button; opacity: 0.2 }
|
|
||||||
}
|
|
||||||
]
|
|
||||||
|
|
||||||
MouseArea {
|
|
||||||
id: mouse
|
|
||||||
anchors.fill: parent
|
|
||||||
hoverEnabled: parent.enabled
|
|
||||||
onReleased: if (parent.enabled) parent.clicked()
|
|
||||||
}
|
|
||||||
|
|
||||||
Row {
|
|
||||||
x: padding
|
|
||||||
y: padding
|
|
||||||
anchors.centerIn: parent
|
|
||||||
spacing: 5
|
|
||||||
|
|
||||||
Image {
|
|
||||||
id: icon
|
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
|
||||||
fillMode: Image.PreserveAspectFit
|
|
||||||
}
|
|
||||||
|
|
||||||
Text {
|
|
||||||
id: title
|
|
||||||
font.pixelSize: 18
|
|
||||||
// font.family: "WenQuanYi Micro Hei"
|
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
|
||||||
text: ""
|
|
||||||
color: "white"
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -5,325 +5,347 @@ import "RoomElement"
|
||||||
import "RoomLogic.js" as Logic
|
import "RoomLogic.js" as Logic
|
||||||
|
|
||||||
Item {
|
Item {
|
||||||
id: roomScene
|
id: roomScene
|
||||||
|
|
||||||
property int playerNum: 0
|
property int playerNum: 0
|
||||||
property var dashboardModel
|
property var dashboardModel
|
||||||
|
|
||||||
property bool isOwner: false
|
property bool isOwner: false
|
||||||
property bool isStarted: false
|
property bool isStarted: false
|
||||||
|
|
||||||
property alias popupBox: popupBox
|
property alias popupBox: popupBox
|
||||||
property alias promptText: prompt.text
|
property alias promptText: prompt.text
|
||||||
|
|
||||||
|
property var selected_targets: []
|
||||||
|
|
||||||
|
// tmp
|
||||||
|
Row {
|
||||||
|
Button{text:"摸1牌"
|
||||||
|
onClicked:{
|
||||||
|
Logic.moveCards([{
|
||||||
|
from:Logic.Player.DrawPile,
|
||||||
|
to:Logic.Player.PlaceHand,
|
||||||
|
cards:[1],
|
||||||
|
}])
|
||||||
|
}}
|
||||||
|
Button{text:"弃1牌"
|
||||||
|
onClicked:{Logic.moveCards([{
|
||||||
|
to:Logic.Player.DrawPile,
|
||||||
|
from:Logic.Player.PlaceHand,
|
||||||
|
cards:[1],
|
||||||
|
}])}}
|
||||||
|
}
|
||||||
|
Button {
|
||||||
|
text: "quit"
|
||||||
|
anchors.top: parent.top
|
||||||
|
anchors.right: parent.right
|
||||||
|
onClicked: {
|
||||||
|
ClientInstance.clearPlayers();
|
||||||
|
ClientInstance.notifyServer("QuitRoom", "[]");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Button {
|
||||||
|
text: "add robot"
|
||||||
|
visible: dashboardModel.isOwner && !isStarted
|
||||||
|
anchors.centerIn: parent
|
||||||
|
onClicked: {
|
||||||
|
ClientInstance.notifyServer("AddRobot", "[]");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
states: [
|
||||||
|
State { name: "notactive" }, // Normal status
|
||||||
|
State { name: "playing" }, // Playing cards in playing phase
|
||||||
|
State { name: "responding" }, // all requests need to operate dashboard
|
||||||
|
State { name: "replying" } // requests only operate a popup window
|
||||||
|
]
|
||||||
|
state: "notactive"
|
||||||
|
transitions: [
|
||||||
|
Transition {
|
||||||
|
from: "*"; to: "notactive"
|
||||||
|
ScriptAction {
|
||||||
|
script: {
|
||||||
|
promptText = "";
|
||||||
|
progress.visible = false;
|
||||||
|
okCancel.visible = false;
|
||||||
|
endPhaseButton.visible = false;
|
||||||
|
|
||||||
|
dashboard.disableAllCards();
|
||||||
|
if (dashboard.pending_skill !== "")
|
||||||
|
dashboard.stopPending();
|
||||||
|
selected_targets = [];
|
||||||
|
|
||||||
|
if (popupBox.item != null) {
|
||||||
|
popupBox.item.finished();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
Transition {
|
||||||
|
from: "*"; to: "playing"
|
||||||
|
ScriptAction {
|
||||||
|
script: {
|
||||||
|
dashboard.enableCards();
|
||||||
|
progress.visible = true;
|
||||||
|
okCancel.visible = true;
|
||||||
|
endPhaseButton.visible = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
Transition {
|
||||||
|
from: "*"; to: "responding"
|
||||||
|
ScriptAction {
|
||||||
|
script: {
|
||||||
|
progress.visible = true;
|
||||||
|
okCancel.visible = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
Transition {
|
||||||
|
from: "*"; to: "replying"
|
||||||
|
ScriptAction {
|
||||||
|
script: {
|
||||||
|
progress.visible = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
/* Layout:
|
||||||
|
* +---------------------+
|
||||||
|
* | Photos, get more |
|
||||||
|
* | in arrangePhotos() |
|
||||||
|
* | tablePile |
|
||||||
|
* | progress,prompt,btn |
|
||||||
|
* +---------------------+
|
||||||
|
* | dashboard |
|
||||||
|
* +---------------------+
|
||||||
|
*/
|
||||||
|
|
||||||
|
ListModel {
|
||||||
|
id: photoModel
|
||||||
|
}
|
||||||
|
|
||||||
|
Item {
|
||||||
|
id: roomArea
|
||||||
|
width: roomScene.width
|
||||||
|
height: roomScene.height - dashboard.height
|
||||||
|
|
||||||
|
Repeater {
|
||||||
|
id: photos
|
||||||
|
model: photoModel
|
||||||
|
Photo {
|
||||||
|
playerid: model.id
|
||||||
|
general: model.general
|
||||||
|
screenName: model.screenName
|
||||||
|
role: model.role
|
||||||
|
kingdom: model.kingdom
|
||||||
|
netstate: model.netstate
|
||||||
|
maxHp: model.maxHp
|
||||||
|
hp: model.hp
|
||||||
|
seatNumber: model.seatNumber
|
||||||
|
isDead: model.isDead
|
||||||
|
dying: model.dying
|
||||||
|
faceup: model.faceup
|
||||||
|
chained: model.chained
|
||||||
|
drank: model.drank
|
||||||
|
isOwner: model.isOwner
|
||||||
|
|
||||||
|
onSelectedChanged: {
|
||||||
|
Logic.updateSelectedTargets(playerid, selected);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onWidthChanged: Logic.arrangePhotos();
|
||||||
|
onHeightChanged: Logic.arrangePhotos();
|
||||||
|
|
||||||
|
InvisibleCardArea {
|
||||||
|
id: drawPile
|
||||||
|
x: parent.width / 2
|
||||||
|
y: roomScene.height / 2
|
||||||
|
}
|
||||||
|
|
||||||
|
TablePile {
|
||||||
|
id: tablePile
|
||||||
|
width: parent.width * 0.6
|
||||||
|
height: 150
|
||||||
|
x: parent.width * 0.2
|
||||||
|
y: parent.height * 0.6
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Dashboard {
|
||||||
|
id: dashboard
|
||||||
|
width: roomScene.width
|
||||||
|
anchors.top: roomArea.bottom
|
||||||
|
|
||||||
|
self.playerid: dashboardModel.id
|
||||||
|
self.general: dashboardModel.general
|
||||||
|
self.screenName: dashboardModel.screenName
|
||||||
|
self.role: dashboardModel.role
|
||||||
|
self.kingdom: dashboardModel.kingdom
|
||||||
|
self.netstate: dashboardModel.netstate
|
||||||
|
self.maxHp: dashboardModel.maxHp
|
||||||
|
self.hp: dashboardModel.hp
|
||||||
|
self.seatNumber: dashboardModel.seatNumber
|
||||||
|
self.isDead: dashboardModel.isDead
|
||||||
|
self.dying: dashboardModel.dying
|
||||||
|
self.faceup: dashboardModel.faceup
|
||||||
|
self.chained: dashboardModel.chained
|
||||||
|
self.drank: dashboardModel.drank
|
||||||
|
self.isOwner: dashboardModel.isOwner
|
||||||
|
|
||||||
|
onSelectedChanged: {
|
||||||
|
Logic.updateSelectedTargets(self.playerid, selected);
|
||||||
|
}
|
||||||
|
|
||||||
|
onCardSelected: {
|
||||||
|
Logic.enableTargets(card);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Item {
|
||||||
|
id: controls
|
||||||
|
anchors.bottom: dashboard.top
|
||||||
|
anchors.bottomMargin: -40
|
||||||
|
width: roomScene.width
|
||||||
|
|
||||||
|
Text {
|
||||||
|
id: prompt
|
||||||
|
visible: progress.visible
|
||||||
|
anchors.bottom: progress.top
|
||||||
|
anchors.bottomMargin: 8
|
||||||
|
anchors.horizontalCenter: progress.horizontalCenter
|
||||||
|
}
|
||||||
|
|
||||||
|
ProgressBar {
|
||||||
|
id: progress
|
||||||
|
width: parent.width * 0.6
|
||||||
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
|
anchors.bottom: okCancel.top
|
||||||
|
anchors.bottomMargin: 8
|
||||||
|
from: 0.0
|
||||||
|
to: 100.0
|
||||||
|
|
||||||
|
visible: false
|
||||||
|
NumberAnimation on value {
|
||||||
|
running: progress.visible
|
||||||
|
from: 100.0
|
||||||
|
to: 0.0
|
||||||
|
duration: config.roomTimeout * 1000
|
||||||
|
|
||||||
|
onFinished: {
|
||||||
|
roomScene.state = "notactive"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// tmp
|
|
||||||
Row {
|
Row {
|
||||||
Button{text:"摸1牌"
|
id: okCancel
|
||||||
onClicked:{
|
anchors.bottom: parent.bottom
|
||||||
Logic.moveCards([{
|
anchors.horizontalCenter: progress.horizontalCenter
|
||||||
from:Logic.Player.DrawPile,
|
spacing: 20
|
||||||
to:Logic.Player.PlaceHand,
|
visible: false
|
||||||
cards:[1],
|
|
||||||
}])
|
Button {
|
||||||
}}
|
id: okButton
|
||||||
Button{text:"弃1牌"
|
text: "OK"
|
||||||
onClicked:{Logic.moveCards([{
|
onClicked: Logic.doOkButton();
|
||||||
to:Logic.Player.DrawPile,
|
}
|
||||||
from:Logic.Player.PlaceHand,
|
|
||||||
cards:[1],
|
Button {
|
||||||
}])}}
|
id: cancelButton
|
||||||
|
text: "Cancel"
|
||||||
|
onClicked: Logic.doCancelButton();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Button {
|
Button {
|
||||||
text: "quit"
|
id: endPhaseButton
|
||||||
anchors.top: parent.top
|
text: "End"
|
||||||
anchors.right: parent.right
|
anchors.bottom: parent.bottom
|
||||||
onClicked: {
|
anchors.bottomMargin: 40
|
||||||
ClientInstance.clearPlayers();
|
anchors.right: parent.right
|
||||||
ClientInstance.notifyServer("QuitRoom", "[]");
|
anchors.rightMargin: 30
|
||||||
}
|
visible: false;
|
||||||
|
onClicked: Logic.doCancelButton();
|
||||||
}
|
}
|
||||||
Button {
|
}
|
||||||
text: "add robot"
|
|
||||||
visible: dashboardModel.isOwner && !isStarted
|
Loader {
|
||||||
anchors.centerIn: parent
|
id: popupBox
|
||||||
onClicked: {
|
onSourceChanged: {
|
||||||
ClientInstance.notifyServer("AddRobot", "[]");
|
if (item === null)
|
||||||
}
|
return;
|
||||||
|
item.finished.connect(function(){
|
||||||
|
source = "";
|
||||||
|
});
|
||||||
|
item.widthChanged.connect(function(){
|
||||||
|
popupBox.moveToCenter();
|
||||||
|
});
|
||||||
|
item.heightChanged.connect(function(){
|
||||||
|
popupBox.moveToCenter();
|
||||||
|
});
|
||||||
|
moveToCenter();
|
||||||
}
|
}
|
||||||
|
|
||||||
states: [
|
function moveToCenter()
|
||||||
State { name: "notactive" }, // Normal status
|
{
|
||||||
State { name: "playing" }, // Playing cards in playing phase
|
item.x = Math.round((roomArea.width - item.width) / 2);
|
||||||
State { name: "responding" }, // all requests need to operate dashboard
|
item.y = Math.round(roomArea.height * 0.67 - item.height / 2);
|
||||||
State { name: "replying" } // requests only operate a popup window
|
}
|
||||||
]
|
}
|
||||||
state: "notactive"
|
|
||||||
transitions: [
|
|
||||||
Transition {
|
|
||||||
from: "*"; to: "notactive"
|
|
||||||
ScriptAction {
|
|
||||||
script: {
|
|
||||||
promptText = "";
|
|
||||||
progress.visible = false;
|
|
||||||
okCancel.visible = false;
|
|
||||||
endPhaseButton.visible = false;
|
|
||||||
|
|
||||||
if (popupBox.item != null) {
|
Component.onCompleted: {
|
||||||
popupBox.item.finished();
|
toast.show("Sucesessfully entered room.");
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
Transition {
|
dashboardModel = {
|
||||||
from: "*"; to: "playing"
|
id: Self.id,
|
||||||
ScriptAction {
|
general: Self.avatar,
|
||||||
script: {
|
screenName: Self.screenName,
|
||||||
progress.visible = true;
|
role: "unknown",
|
||||||
okCancel.visible = true;
|
kingdom: "qun",
|
||||||
endPhaseButton.visible = true;
|
netstate: "online",
|
||||||
}
|
maxHp: 0,
|
||||||
}
|
hp: 0,
|
||||||
},
|
seatNumber: 1,
|
||||||
|
isDead: false,
|
||||||
Transition {
|
dying: false,
|
||||||
from: "*"; to: "responding"
|
faceup: true,
|
||||||
ScriptAction {
|
chained: false,
|
||||||
script: {
|
drank: false,
|
||||||
progress.visible = true;
|
isOwner: false
|
||||||
okCancel.visible = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
Transition {
|
|
||||||
from: "*"; to: "replying"
|
|
||||||
ScriptAction {
|
|
||||||
script: {
|
|
||||||
progress.visible = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
|
|
||||||
/* Layout:
|
|
||||||
* +---------------------+
|
|
||||||
* | Photos, get more |
|
|
||||||
* | in arrangePhotos() |
|
|
||||||
* | tablePile |
|
|
||||||
* | progress,prompt,btn |
|
|
||||||
* +---------------------+
|
|
||||||
* | dashboard |
|
|
||||||
* +---------------------+
|
|
||||||
*/
|
|
||||||
|
|
||||||
ListModel {
|
|
||||||
id: photoModel
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Item {
|
playerNum = config.roomCapacity;
|
||||||
id: roomArea
|
|
||||||
width: roomScene.width
|
|
||||||
height: roomScene.height - dashboard.height
|
|
||||||
|
|
||||||
Repeater {
|
let i;
|
||||||
id: photos
|
for (i = 1; i < playerNum; i++) {
|
||||||
model: photoModel
|
photoModel.append({
|
||||||
Photo {
|
id: -1,
|
||||||
general: model.general
|
index: i - 1, // For animating seat swap
|
||||||
screenName: model.screenName
|
general: "",
|
||||||
role: model.role
|
screenName: "",
|
||||||
kingdom: model.kingdom
|
role: "unknown",
|
||||||
netstate: model.netstate
|
kingdom: "qun",
|
||||||
maxHp: model.maxHp
|
netstate: "online",
|
||||||
hp: model.hp
|
maxHp: 0,
|
||||||
seatNumber: model.seatNumber
|
hp: 0,
|
||||||
isDead: model.isDead
|
seatNumber: i + 1,
|
||||||
dying: model.dying
|
isDead: false,
|
||||||
faceup: model.faceup
|
dying: false,
|
||||||
chained: model.chained
|
faceup: true,
|
||||||
drank: model.drank
|
chained: false,
|
||||||
isOwner: model.isOwner
|
drank: false,
|
||||||
}
|
isOwner: false
|
||||||
}
|
});
|
||||||
|
|
||||||
onWidthChanged: Logic.arrangePhotos();
|
|
||||||
onHeightChanged: Logic.arrangePhotos();
|
|
||||||
|
|
||||||
InvisibleCardArea {
|
|
||||||
id: drawPile
|
|
||||||
x: parent.width / 2
|
|
||||||
y: roomScene.height / 2
|
|
||||||
}
|
|
||||||
|
|
||||||
TablePile {
|
|
||||||
id: tablePile
|
|
||||||
width: parent.width * 0.6
|
|
||||||
height: 150
|
|
||||||
x: parent.width * 0.2
|
|
||||||
y: parent.height * 0.6
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Dashboard {
|
Logic.arrangePhotos();
|
||||||
id: dashboard
|
}
|
||||||
width: roomScene.width
|
|
||||||
anchors.top: roomArea.bottom
|
|
||||||
|
|
||||||
self.general: dashboardModel.general
|
|
||||||
self.screenName: dashboardModel.screenName
|
|
||||||
self.role: dashboardModel.role
|
|
||||||
self.kingdom: dashboardModel.kingdom
|
|
||||||
self.netstate: dashboardModel.netstate
|
|
||||||
self.maxHp: dashboardModel.maxHp
|
|
||||||
self.hp: dashboardModel.hp
|
|
||||||
self.seatNumber: dashboardModel.seatNumber
|
|
||||||
self.isDead: dashboardModel.isDead
|
|
||||||
self.dying: dashboardModel.dying
|
|
||||||
self.faceup: dashboardModel.faceup
|
|
||||||
self.chained: dashboardModel.chained
|
|
||||||
self.drank: dashboardModel.drank
|
|
||||||
self.isOwner: dashboardModel.isOwner
|
|
||||||
}
|
|
||||||
|
|
||||||
Item {
|
|
||||||
id: controls
|
|
||||||
anchors.bottom: dashboard.top
|
|
||||||
anchors.bottomMargin: -40
|
|
||||||
width: roomScene.width
|
|
||||||
|
|
||||||
Text {
|
|
||||||
id: prompt
|
|
||||||
visible: progress.visible
|
|
||||||
anchors.bottom: progress.top
|
|
||||||
anchors.bottomMargin: 8
|
|
||||||
anchors.horizontalCenter: progress.horizontalCenter
|
|
||||||
}
|
|
||||||
|
|
||||||
ProgressBar {
|
|
||||||
id: progress
|
|
||||||
width: parent.width * 0.6
|
|
||||||
anchors.horizontalCenter: parent.horizontalCenter
|
|
||||||
anchors.bottom: okCancel.top
|
|
||||||
anchors.bottomMargin: 8
|
|
||||||
from: 0.0
|
|
||||||
to: 100.0
|
|
||||||
|
|
||||||
visible: false
|
|
||||||
NumberAnimation on value {
|
|
||||||
running: progress.visible
|
|
||||||
from: 100.0
|
|
||||||
to: 0.0
|
|
||||||
duration: config.roomTimeout * 1000
|
|
||||||
|
|
||||||
onFinished: {
|
|
||||||
roomScene.state = "notactive"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Row {
|
|
||||||
id: okCancel
|
|
||||||
anchors.bottom: parent.bottom
|
|
||||||
anchors.horizontalCenter: progress.horizontalCenter
|
|
||||||
spacing: 20
|
|
||||||
visible: false
|
|
||||||
|
|
||||||
Button {
|
|
||||||
id: okButton
|
|
||||||
text: "OK"
|
|
||||||
onClicked: Logic.doOkButton();
|
|
||||||
}
|
|
||||||
|
|
||||||
Button {
|
|
||||||
id: cancelButton
|
|
||||||
text: "Cancel"
|
|
||||||
onClicked: Logic.doCancelButton();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Button {
|
|
||||||
id: endPhaseButton
|
|
||||||
text: "End"
|
|
||||||
anchors.bottom: parent.bottom
|
|
||||||
anchors.bottomMargin: 40
|
|
||||||
anchors.right: parent.right
|
|
||||||
anchors.rightMargin: 30
|
|
||||||
visible: false;
|
|
||||||
onClicked: Logic.doCancelButton();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Loader {
|
|
||||||
id: popupBox
|
|
||||||
onSourceChanged: {
|
|
||||||
if (item === null)
|
|
||||||
return;
|
|
||||||
item.finished.connect(function(){
|
|
||||||
source = "";
|
|
||||||
});
|
|
||||||
item.widthChanged.connect(function(){
|
|
||||||
popupBox.moveToCenter();
|
|
||||||
});
|
|
||||||
item.heightChanged.connect(function(){
|
|
||||||
popupBox.moveToCenter();
|
|
||||||
});
|
|
||||||
moveToCenter();
|
|
||||||
}
|
|
||||||
|
|
||||||
function moveToCenter()
|
|
||||||
{
|
|
||||||
item.x = Math.round((roomArea.width - item.width) / 2);
|
|
||||||
item.y = Math.round(roomArea.height * 0.67 - item.height / 2);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Component.onCompleted: {
|
|
||||||
toast.show("Sucesessfully entered room.");
|
|
||||||
|
|
||||||
dashboardModel = {
|
|
||||||
id: Self.id,
|
|
||||||
general: Self.avatar,
|
|
||||||
screenName: Self.screenName,
|
|
||||||
role: "unknown",
|
|
||||||
kingdom: "qun",
|
|
||||||
netstate: "online",
|
|
||||||
maxHp: 0,
|
|
||||||
hp: 0,
|
|
||||||
seatNumber: 1,
|
|
||||||
isDead: false,
|
|
||||||
dying: false,
|
|
||||||
faceup: true,
|
|
||||||
chained: false,
|
|
||||||
drank: false,
|
|
||||||
isOwner: false
|
|
||||||
}
|
|
||||||
|
|
||||||
playerNum = config.roomCapacity;
|
|
||||||
|
|
||||||
let i;
|
|
||||||
for (i = 1; i < playerNum; i++) {
|
|
||||||
photoModel.append({
|
|
||||||
id: -1,
|
|
||||||
index: i - 1, // For animating seat swap
|
|
||||||
general: "",
|
|
||||||
screenName: "",
|
|
||||||
role: "unknown",
|
|
||||||
kingdom: "qun",
|
|
||||||
netstate: "online",
|
|
||||||
maxHp: 0,
|
|
||||||
hp: 0,
|
|
||||||
seatNumber: i + 1,
|
|
||||||
isDead: false,
|
|
||||||
dying: false,
|
|
||||||
faceup: true,
|
|
||||||
chained: false,
|
|
||||||
drank: false,
|
|
||||||
isOwner: false
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
Logic.arrangePhotos();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -3,75 +3,75 @@ import QtQuick 2.15
|
||||||
// CardArea stores CardItem.
|
// CardArea stores CardItem.
|
||||||
|
|
||||||
Item {
|
Item {
|
||||||
property var cards: []
|
property var cards: []
|
||||||
property int length: 0
|
property int length: 0
|
||||||
|
|
||||||
id: root
|
id: root
|
||||||
|
|
||||||
function add(inputs)
|
function add(inputs)
|
||||||
{
|
{
|
||||||
if (inputs instanceof Array) {
|
if (inputs instanceof Array) {
|
||||||
cards.push(...inputs);
|
cards.push(...inputs);
|
||||||
} else {
|
} else {
|
||||||
cards.push(inputs);
|
cards.push(inputs);
|
||||||
|
}
|
||||||
|
length = cards.length;
|
||||||
|
}
|
||||||
|
|
||||||
|
function remove(outputs)
|
||||||
|
{
|
||||||
|
let result = [];
|
||||||
|
for (let i = 0; i < cards.length; i++) {
|
||||||
|
for (let j = 0; j < outputs.length; j++) {
|
||||||
|
if (outputs[j] === cards[i].cid) {
|
||||||
|
result.push(cards[i]);
|
||||||
|
cards.splice(i, 1);
|
||||||
|
i--;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
length = cards.length;
|
}
|
||||||
|
}
|
||||||
|
length = cards.length;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateCardPosition(animated)
|
||||||
|
{
|
||||||
|
let i, card;
|
||||||
|
|
||||||
|
let overflow = false;
|
||||||
|
for (i = 0; i < cards.length; i++) {
|
||||||
|
card = cards[i];
|
||||||
|
card.origX = i * card.width;
|
||||||
|
if (card.origX + card.width >= root.width) {
|
||||||
|
overflow = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
card.origY = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
function remove(outputs)
|
if (overflow) {
|
||||||
{
|
// TODO: Adjust cards in multiple lines if there are too many cards
|
||||||
let result = [];
|
let xLimit = root.width - card.width;
|
||||||
for (let i = 0; i < cards.length; i++) {
|
let spacing = xLimit / (cards.length - 1);
|
||||||
for (let j = 0; j < outputs.length; j++) {
|
for (i = 0; i < cards.length; i++) {
|
||||||
if (outputs[j] === cards[i].cid) {
|
card = cards[i];
|
||||||
result.push(cards[i]);
|
card.origX = i * spacing;
|
||||||
cards.splice(i, 1);
|
card.origY = 0;
|
||||||
i--;
|
}
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
length = cards.length;
|
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function updateCardPosition(animated)
|
let parentPos = roomScene.mapFromItem(root, 0, 0);
|
||||||
{
|
for (i = 0; i < cards.length; i++) {
|
||||||
let i, card;
|
card = cards[i];
|
||||||
|
card.origX += parentPos.x;
|
||||||
let overflow = false;
|
card.origY += parentPos.y;
|
||||||
for (i = 0; i < cards.length; i++) {
|
|
||||||
card = cards[i];
|
|
||||||
card.origX = i * card.width;
|
|
||||||
if (card.origX + card.width >= root.width) {
|
|
||||||
overflow = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
card.origY = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (overflow) {
|
|
||||||
// TODO: Adjust cards in multiple lines if there are too many cards
|
|
||||||
let xLimit = root.width - card.width;
|
|
||||||
let spacing = xLimit / (cards.length - 1);
|
|
||||||
for (i = 0; i < cards.length; i++) {
|
|
||||||
card = cards[i];
|
|
||||||
card.origX = i * spacing;
|
|
||||||
card.origY = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let parentPos = roomScene.mapFromItem(root, 0, 0);
|
|
||||||
for (i = 0; i < cards.length; i++) {
|
|
||||||
card = cards[i];
|
|
||||||
card.origX += parentPos.x;
|
|
||||||
card.origY += parentPos.y;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (animated) {
|
|
||||||
for (i = 0; i < cards.length; i++)
|
|
||||||
cards[i].goBack(true);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (animated) {
|
||||||
|
for (i = 0; i < cards.length; i++)
|
||||||
|
cards[i].goBack(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -13,242 +13,242 @@ import "../skin-bank.js" as SkinBank
|
||||||
*/
|
*/
|
||||||
|
|
||||||
Item {
|
Item {
|
||||||
id: root
|
id: root
|
||||||
width: 93
|
width: 93
|
||||||
height: 130
|
height: 130
|
||||||
|
|
||||||
// properties for the view
|
// properties for the view
|
||||||
property string suit: "club"
|
property string suit: "club"
|
||||||
property int number: 7
|
property int number: 7
|
||||||
property string name: "slash"
|
property string name: "slash"
|
||||||
property string subtype: ""
|
property string subtype: ""
|
||||||
property string color: "" // only use when suit is empty
|
property string color: "" // only use when suit is empty
|
||||||
property string footnote: "" // footnote, e.g. "A use card to B"
|
property string footnote: "" // footnote, e.g. "A use card to B"
|
||||||
property bool footnoteVisible: true
|
property bool footnoteVisible: true
|
||||||
property bool known: true // if false it only show a card back
|
property bool known: true // if false it only show a card back
|
||||||
property bool enabled: true // if false the card will be grey
|
property bool enabled: true // if false the card will be grey
|
||||||
property alias card: cardItem
|
property alias card: cardItem
|
||||||
property alias glow: glowItem
|
property alias glow: glowItem
|
||||||
|
|
||||||
function getColor() {
|
function getColor() {
|
||||||
if (suit != "")
|
if (suit != "")
|
||||||
return (suit == "heart" || suit == "diamond") ? "red" : "black";
|
return (suit == "heart" || suit == "diamond") ? "red" : "black";
|
||||||
else return color;
|
else return color;
|
||||||
|
}
|
||||||
|
|
||||||
|
// properties for animation and game system
|
||||||
|
property int cid: 0
|
||||||
|
property bool selectable: true
|
||||||
|
property bool selected: false
|
||||||
|
property bool draggable: false
|
||||||
|
property bool autoBack: true
|
||||||
|
property int origX: 0
|
||||||
|
property int origY: 0
|
||||||
|
property real origOpacity: 1
|
||||||
|
property bool isClicked: false
|
||||||
|
property bool moveAborted: false
|
||||||
|
property alias goBackAnim: goBackAnimation
|
||||||
|
property int goBackDuration: 500
|
||||||
|
|
||||||
|
signal toggleDiscards()
|
||||||
|
signal clicked()
|
||||||
|
signal doubleClicked()
|
||||||
|
signal thrown()
|
||||||
|
signal released()
|
||||||
|
signal entered()
|
||||||
|
signal exited()
|
||||||
|
signal moveFinished()
|
||||||
|
signal generalChanged() // For choose general freely
|
||||||
|
signal hoverChanged(bool enter)
|
||||||
|
|
||||||
|
RectangularGlow {
|
||||||
|
id: glowItem
|
||||||
|
anchors.fill: parent
|
||||||
|
glowRadius: 8
|
||||||
|
spread: 0
|
||||||
|
color: "#88FFFFFF"
|
||||||
|
cornerRadius: 8
|
||||||
|
visible: false
|
||||||
|
}
|
||||||
|
|
||||||
|
Image {
|
||||||
|
id: cardItem
|
||||||
|
source: known ? (name != "" ? SkinBank.CARD_DIR + name : "")
|
||||||
|
: (SkinBank.CARD_DIR + "card-back")
|
||||||
|
anchors.fill: parent
|
||||||
|
fillMode: Image.PreserveAspectCrop
|
||||||
|
}
|
||||||
|
|
||||||
|
Image {
|
||||||
|
id: suitItem
|
||||||
|
visible: known
|
||||||
|
source: suit != "" ? SkinBank.CARD_SUIT_DIR + suit : ""
|
||||||
|
x: 3
|
||||||
|
y: 19
|
||||||
|
width: 21
|
||||||
|
height: 17
|
||||||
|
}
|
||||||
|
|
||||||
|
Image {
|
||||||
|
id: numberItem
|
||||||
|
visible: known
|
||||||
|
source: (suit != "" && number > 0) ? SkinBank.CARD_DIR
|
||||||
|
+ "number/" + root.getColor() + "/" + number : ""
|
||||||
|
x: 0
|
||||||
|
y: 0
|
||||||
|
width: 27
|
||||||
|
height: 28
|
||||||
|
}
|
||||||
|
|
||||||
|
Image {
|
||||||
|
id: colorItem
|
||||||
|
visible: known && suit == ""
|
||||||
|
source: (visible && color != "") ? SkinBank.CARD_SUIT_DIR + "/" + color : ""
|
||||||
|
x: 1
|
||||||
|
}
|
||||||
|
|
||||||
|
GlowText {
|
||||||
|
id: footnoteItem
|
||||||
|
text: footnote
|
||||||
|
x: 6
|
||||||
|
y: parent.height - height - 6
|
||||||
|
width: root.width - x * 2
|
||||||
|
color: "#E4D5A0"
|
||||||
|
visible: footnoteVisible
|
||||||
|
wrapMode: Text.WrapAnywhere
|
||||||
|
horizontalAlignment: Text.AlignHCenter
|
||||||
|
font.family: "FZLiBian-S02"
|
||||||
|
font.pixelSize: 14
|
||||||
|
glow.color: "black"
|
||||||
|
glow.spread: 1
|
||||||
|
glow.radius: 1
|
||||||
|
glow.samples: 12
|
||||||
|
}
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
visible: !root.selectable
|
||||||
|
anchors.fill: parent
|
||||||
|
color: Qt.rgba(0, 0, 0, 0.5)
|
||||||
|
opacity: 0.7
|
||||||
|
}
|
||||||
|
|
||||||
|
MouseArea {
|
||||||
|
anchors.fill: parent
|
||||||
|
drag.target: draggable ? parent : undefined
|
||||||
|
drag.axis: Drag.XAndYAxis
|
||||||
|
hoverEnabled: true
|
||||||
|
|
||||||
|
onReleased: {
|
||||||
|
root.isClicked = mouse.isClick;
|
||||||
|
parent.released();
|
||||||
|
if (autoBack)
|
||||||
|
goBackAnimation.start();
|
||||||
}
|
}
|
||||||
|
|
||||||
// properties for animation and game system
|
onEntered: {
|
||||||
property int cid: 0
|
parent.entered();
|
||||||
property bool selectable: true
|
if (draggable) {
|
||||||
property bool selected: false
|
glow.visible = true;
|
||||||
property bool draggable: false
|
root.z++;
|
||||||
property bool autoBack: true
|
}
|
||||||
property int origX: 0
|
|
||||||
property int origY: 0
|
|
||||||
property real origOpacity: 1
|
|
||||||
property bool isClicked: false
|
|
||||||
property bool moveAborted: false
|
|
||||||
property alias goBackAnim: goBackAnimation
|
|
||||||
property int goBackDuration: 500
|
|
||||||
|
|
||||||
signal toggleDiscards()
|
|
||||||
signal clicked()
|
|
||||||
signal doubleClicked()
|
|
||||||
signal thrown()
|
|
||||||
signal released()
|
|
||||||
signal entered()
|
|
||||||
signal exited()
|
|
||||||
signal moveFinished()
|
|
||||||
signal generalChanged() // For choose general freely
|
|
||||||
signal hoverChanged(bool enter)
|
|
||||||
|
|
||||||
RectangularGlow {
|
|
||||||
id: glowItem
|
|
||||||
anchors.fill: parent
|
|
||||||
glowRadius: 8
|
|
||||||
spread: 0
|
|
||||||
color: "#88FFFFFF"
|
|
||||||
cornerRadius: 8
|
|
||||||
visible: false
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Image {
|
onExited: {
|
||||||
id: cardItem
|
parent.exited();
|
||||||
source: known ? (name != "" ? SkinBank.CARD_DIR + name : "")
|
if (draggable) {
|
||||||
: (SkinBank.CARD_DIR + "card-back")
|
glow.visible = false;
|
||||||
anchors.fill: parent
|
root.z--;
|
||||||
fillMode: Image.PreserveAspectCrop
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Image {
|
onClicked: {
|
||||||
id: suitItem
|
selected = selectable ? !selected : false;
|
||||||
visible: known
|
parent.clicked();
|
||||||
source: suit != "" ? SkinBank.CARD_SUIT_DIR + suit : ""
|
}
|
||||||
x: 3
|
}
|
||||||
y: 19
|
|
||||||
width: 21
|
ParallelAnimation {
|
||||||
height: 17
|
id: goBackAnimation
|
||||||
|
|
||||||
|
PropertyAnimation {
|
||||||
|
target: root
|
||||||
|
property: "x"
|
||||||
|
to: origX
|
||||||
|
easing.type: Easing.OutQuad
|
||||||
|
duration: goBackDuration
|
||||||
}
|
}
|
||||||
|
|
||||||
Image {
|
PropertyAnimation {
|
||||||
id: numberItem
|
target: root
|
||||||
visible: known
|
property: "y"
|
||||||
source: (suit != "" && number > 0) ? SkinBank.CARD_DIR
|
to: origY
|
||||||
+ "number/" + root.getColor() + "/" + number : ""
|
easing.type: Easing.OutQuad
|
||||||
x: 0
|
duration: goBackDuration
|
||||||
y: 0
|
|
||||||
width: 27
|
|
||||||
height: 28
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Image {
|
SequentialAnimation {
|
||||||
id: colorItem
|
PropertyAnimation {
|
||||||
visible: known && suit == ""
|
target: root
|
||||||
source: (visible && color != "") ? SkinBank.CARD_SUIT_DIR + "/" + color : ""
|
property: "opacity"
|
||||||
x: 1
|
to: 1
|
||||||
|
easing.type: Easing.OutQuad
|
||||||
|
duration: goBackDuration * 0.8
|
||||||
|
}
|
||||||
|
|
||||||
|
PropertyAnimation {
|
||||||
|
target: root
|
||||||
|
property: "opacity"
|
||||||
|
to: origOpacity
|
||||||
|
easing.type: Easing.OutQuad
|
||||||
|
duration: goBackDuration * 0.2
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
GlowText {
|
onStopped: {
|
||||||
id: footnoteItem
|
if (!moveAborted)
|
||||||
text: footnote
|
root.moveFinished();
|
||||||
x: 6
|
|
||||||
y: parent.height - height - 6
|
|
||||||
width: root.width - x * 2
|
|
||||||
color: "#E4D5A0"
|
|
||||||
visible: footnoteVisible
|
|
||||||
wrapMode: Text.WrapAnywhere
|
|
||||||
horizontalAlignment: Text.AlignHCenter
|
|
||||||
font.family: "FZLiBian-S02"
|
|
||||||
font.pixelSize: 14
|
|
||||||
glow.color: "black"
|
|
||||||
glow.spread: 1
|
|
||||||
glow.radius: 1
|
|
||||||
glow.samples: 12
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Rectangle {
|
function setData(data)
|
||||||
visible: !root.selectable
|
{
|
||||||
anchors.fill: parent
|
cid = data.cid;
|
||||||
color: Qt.rgba(0, 0, 0, 0.5)
|
name = data.name;
|
||||||
opacity: 0.7
|
suit = data.suit;
|
||||||
|
number = data.number;
|
||||||
|
color = data.color;
|
||||||
|
}
|
||||||
|
|
||||||
|
function toData()
|
||||||
|
{
|
||||||
|
let data = {
|
||||||
|
cid: cid,
|
||||||
|
name: name,
|
||||||
|
suit: suit,
|
||||||
|
number: number,
|
||||||
|
color: color
|
||||||
|
};
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
function goBack(animated)
|
||||||
|
{
|
||||||
|
if (animated) {
|
||||||
|
moveAborted = true;
|
||||||
|
goBackAnimation.stop();
|
||||||
|
moveAborted = false;
|
||||||
|
goBackAnimation.start();
|
||||||
|
} else {
|
||||||
|
x = origX;
|
||||||
|
y = origY;
|
||||||
|
opacity = origOpacity;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
MouseArea {
|
function destroyOnStop()
|
||||||
anchors.fill: parent
|
{
|
||||||
drag.target: draggable ? parent : undefined
|
root.moveFinished.connect(function(){
|
||||||
drag.axis: Drag.XAndYAxis
|
root.destroy();
|
||||||
hoverEnabled: true
|
});
|
||||||
|
}
|
||||||
onReleased: {
|
|
||||||
root.isClicked = mouse.isClick;
|
|
||||||
parent.released();
|
|
||||||
if (autoBack)
|
|
||||||
goBackAnimation.start();
|
|
||||||
}
|
|
||||||
|
|
||||||
onEntered: {
|
|
||||||
parent.entered();
|
|
||||||
if (draggable) {
|
|
||||||
glow.visible = true;
|
|
||||||
root.z++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
onExited: {
|
|
||||||
parent.exited();
|
|
||||||
if (draggable) {
|
|
||||||
glow.visible = false;
|
|
||||||
root.z--;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
onClicked: {
|
|
||||||
selected = selectable ? !selected : false;
|
|
||||||
parent.clicked();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ParallelAnimation {
|
|
||||||
id: goBackAnimation
|
|
||||||
|
|
||||||
PropertyAnimation {
|
|
||||||
target: root
|
|
||||||
property: "x"
|
|
||||||
to: origX
|
|
||||||
easing.type: Easing.OutQuad
|
|
||||||
duration: goBackDuration
|
|
||||||
}
|
|
||||||
|
|
||||||
PropertyAnimation {
|
|
||||||
target: root
|
|
||||||
property: "y"
|
|
||||||
to: origY
|
|
||||||
easing.type: Easing.OutQuad
|
|
||||||
duration: goBackDuration
|
|
||||||
}
|
|
||||||
|
|
||||||
SequentialAnimation {
|
|
||||||
PropertyAnimation {
|
|
||||||
target: root
|
|
||||||
property: "opacity"
|
|
||||||
to: 1
|
|
||||||
easing.type: Easing.OutQuad
|
|
||||||
duration: goBackDuration * 0.8
|
|
||||||
}
|
|
||||||
|
|
||||||
PropertyAnimation {
|
|
||||||
target: root
|
|
||||||
property: "opacity"
|
|
||||||
to: origOpacity
|
|
||||||
easing.type: Easing.OutQuad
|
|
||||||
duration: goBackDuration * 0.2
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
onStopped: {
|
|
||||||
if (!moveAborted)
|
|
||||||
root.moveFinished();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function setData(data)
|
|
||||||
{
|
|
||||||
cid = data.cid;
|
|
||||||
name = data.name;
|
|
||||||
suit = data.suit;
|
|
||||||
number = data.number;
|
|
||||||
color = data.color;
|
|
||||||
}
|
|
||||||
|
|
||||||
function toData()
|
|
||||||
{
|
|
||||||
let data = {
|
|
||||||
cid: cid,
|
|
||||||
name: name,
|
|
||||||
suit: suit,
|
|
||||||
number: number,
|
|
||||||
color: color
|
|
||||||
};
|
|
||||||
return data;
|
|
||||||
}
|
|
||||||
|
|
||||||
function goBack(animated)
|
|
||||||
{
|
|
||||||
if (animated) {
|
|
||||||
moveAborted = true;
|
|
||||||
goBackAnimation.stop();
|
|
||||||
moveAborted = false;
|
|
||||||
goBackAnimation.start();
|
|
||||||
} else {
|
|
||||||
x = origX;
|
|
||||||
y = origY;
|
|
||||||
opacity = origOpacity;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function destroyOnStop()
|
|
||||||
{
|
|
||||||
root.moveFinished.connect(function(){
|
|
||||||
root.destroy();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,33 +2,33 @@ import QtQuick 2.15
|
||||||
import ".."
|
import ".."
|
||||||
|
|
||||||
GraphicsBox {
|
GraphicsBox {
|
||||||
property var options: []
|
property var options: []
|
||||||
property string skill_name: ""
|
property string skill_name: ""
|
||||||
property int result
|
property int result
|
||||||
|
|
||||||
id: root
|
id: root
|
||||||
title.text: skill_name + ": Please choose"
|
title.text: skill_name + ": Please choose"
|
||||||
width: Math.max(140, body.width + 20)
|
width: Math.max(140, body.width + 20)
|
||||||
height: body.height + title.height + 20
|
height: body.height + title.height + 20
|
||||||
|
|
||||||
Column {
|
Column {
|
||||||
id: body
|
id: body
|
||||||
x: 10
|
x: 10
|
||||||
y: title.height + 5
|
y: title.height + 5
|
||||||
spacing: 10
|
spacing: 10
|
||||||
|
|
||||||
Repeater {
|
Repeater {
|
||||||
model: options
|
model: options
|
||||||
|
|
||||||
MetroButton {
|
MetroButton {
|
||||||
text: modelData
|
text: modelData
|
||||||
anchors.horizontalCenter: parent.horizontalCenter
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
|
|
||||||
onClicked: {
|
onClicked: {
|
||||||
result = index;
|
result = index;
|
||||||
root.close();
|
root.close();
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,175 +3,175 @@ import ".."
|
||||||
import "../skin-bank.js" as SkinBank
|
import "../skin-bank.js" as SkinBank
|
||||||
|
|
||||||
GraphicsBox {
|
GraphicsBox {
|
||||||
property alias generalList: generalList
|
property alias generalList: generalList
|
||||||
// property var generalList: []
|
// property var generalList: []
|
||||||
property int choiceNum: 1
|
property int choiceNum: 1
|
||||||
property var choices: []
|
property var choices: []
|
||||||
property var selectedItem: []
|
property var selectedItem: []
|
||||||
property bool loaded: false
|
property bool loaded: false
|
||||||
|
|
||||||
ListModel {
|
ListModel {
|
||||||
id: generalList
|
id: generalList
|
||||||
|
}
|
||||||
|
|
||||||
|
id: root
|
||||||
|
title.text: qsTr("Please choose ") + choiceNum + qsTr(" general(s)")
|
||||||
|
width: generalArea.width + body.anchors.leftMargin + body.anchors.rightMargin
|
||||||
|
height: body.implicitHeight + body.anchors.topMargin + body.anchors.bottomMargin
|
||||||
|
|
||||||
|
Column {
|
||||||
|
id: body
|
||||||
|
anchors.fill: parent
|
||||||
|
anchors.margins: 40
|
||||||
|
anchors.bottomMargin: 20
|
||||||
|
|
||||||
|
Item {
|
||||||
|
id: generalArea
|
||||||
|
width: (generalList.count >= 5 ? Math.ceil(generalList.count / 2) : Math.max(3, generalList.count)) * 97
|
||||||
|
height: generalList.count >= 5 ? 290 : 150
|
||||||
|
z: 1
|
||||||
|
|
||||||
|
Repeater {
|
||||||
|
id: generalMagnetList
|
||||||
|
model: generalList.count
|
||||||
|
|
||||||
|
Item {
|
||||||
|
width: 93
|
||||||
|
height: 130
|
||||||
|
x: (index % Math.ceil(generalList.count / (generalList.count >= 5 ? 2 : 1))) * 98 + (generalList.count >= 5 && index > generalList.count / 2 && generalList.count % 2 == 1 ? 50 : 0)
|
||||||
|
y: generalList.count < 5 ? 0 : (index < generalList.count / 2 ? 0 : 135)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
id: root
|
Item {
|
||||||
title.text: qsTr("Please choose ") + choiceNum + qsTr(" general(s)")
|
id: splitLine
|
||||||
width: generalArea.width + body.anchors.leftMargin + body.anchors.rightMargin
|
width: parent.width - 80
|
||||||
height: body.implicitHeight + body.anchors.topMargin + body.anchors.bottomMargin
|
height: 6
|
||||||
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
Column {
|
clip: true
|
||||||
id: body
|
|
||||||
anchors.fill: parent
|
|
||||||
anchors.margins: 40
|
|
||||||
anchors.bottomMargin: 20
|
|
||||||
|
|
||||||
Item {
|
|
||||||
id: generalArea
|
|
||||||
width: (generalList.count >= 5 ? Math.ceil(generalList.count / 2) : Math.max(3, generalList.count)) * 97
|
|
||||||
height: generalList.count >= 5 ? 290 : 150
|
|
||||||
z: 1
|
|
||||||
|
|
||||||
Repeater {
|
|
||||||
id: generalMagnetList
|
|
||||||
model: generalList.count
|
|
||||||
|
|
||||||
Item {
|
|
||||||
width: 93
|
|
||||||
height: 130
|
|
||||||
x: (index % Math.ceil(generalList.count / (generalList.count >= 5 ? 2 : 1))) * 98 + (generalList.count >= 5 && index > generalList.count / 2 && generalList.count % 2 == 1 ? 50 : 0)
|
|
||||||
y: generalList.count < 5 ? 0 : (index < generalList.count / 2 ? 0 : 135)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Item {
|
|
||||||
id: splitLine
|
|
||||||
width: parent.width - 80
|
|
||||||
height: 6
|
|
||||||
anchors.horizontalCenter: parent.horizontalCenter
|
|
||||||
clip: true
|
|
||||||
}
|
|
||||||
|
|
||||||
Item {
|
|
||||||
width: parent.width
|
|
||||||
height: 165
|
|
||||||
|
|
||||||
Row {
|
|
||||||
id: resultArea
|
|
||||||
anchors.centerIn: parent
|
|
||||||
spacing: 10
|
|
||||||
|
|
||||||
Repeater {
|
|
||||||
id: resultList
|
|
||||||
model: choiceNum
|
|
||||||
|
|
||||||
Rectangle {
|
|
||||||
color: "#1D1E19"
|
|
||||||
radius: 3
|
|
||||||
width: 93
|
|
||||||
height: 130
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Item {
|
|
||||||
id: buttonArea
|
|
||||||
width: parent.width
|
|
||||||
height: 40
|
|
||||||
|
|
||||||
MetroButton {
|
|
||||||
id: fightButton
|
|
||||||
anchors.horizontalCenter: parent.horizontalCenter
|
|
||||||
anchors.bottom: parent.bottom
|
|
||||||
text: qsTr("Fight")
|
|
||||||
width: 120
|
|
||||||
height: 35
|
|
||||||
enabled: false
|
|
||||||
|
|
||||||
onClicked: close();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Repeater {
|
Item {
|
||||||
id: generalCardList
|
width: parent.width
|
||||||
model: generalList
|
height: 165
|
||||||
|
|
||||||
GeneralCardItem {
|
Row {
|
||||||
name: model.name
|
id: resultArea
|
||||||
selectable: true
|
anchors.centerIn: parent
|
||||||
draggable: true
|
spacing: 10
|
||||||
|
|
||||||
onClicked: {
|
Repeater {
|
||||||
let toSelect = true;
|
id: resultList
|
||||||
for (let i = 0; i < selectedItem.length; i++) {
|
model: choiceNum
|
||||||
if (selectedItem[i] === this) {
|
|
||||||
toSelect = false;
|
|
||||||
selectedItem.splice(i, 1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (toSelect && selectedItem.length < choiceNum)
|
|
||||||
selectedItem.push(this);
|
|
||||||
updatePosition();
|
|
||||||
}
|
|
||||||
|
|
||||||
onReleased: {
|
Rectangle {
|
||||||
if (!isClicked)
|
color: "#1D1E19"
|
||||||
arrangeCards();
|
radius: 3
|
||||||
}
|
width: 93
|
||||||
|
height: 130
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function arrangeCards()
|
Item {
|
||||||
{
|
id: buttonArea
|
||||||
let item, i;
|
width: parent.width
|
||||||
|
height: 40
|
||||||
|
|
||||||
selectedItem = [];
|
MetroButton {
|
||||||
for (i = 0; i < generalList.count; i++) {
|
id: fightButton
|
||||||
item = generalCardList.itemAt(i);
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
if (item.y > splitLine.y)
|
anchors.bottom: parent.bottom
|
||||||
selectedItem.push(item);
|
text: qsTr("Fight")
|
||||||
|
width: 120
|
||||||
|
height: 35
|
||||||
|
enabled: false
|
||||||
|
|
||||||
|
onClicked: close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Repeater {
|
||||||
|
id: generalCardList
|
||||||
|
model: generalList
|
||||||
|
|
||||||
|
GeneralCardItem {
|
||||||
|
name: model.name
|
||||||
|
selectable: true
|
||||||
|
draggable: true
|
||||||
|
|
||||||
|
onClicked: {
|
||||||
|
let toSelect = true;
|
||||||
|
for (let i = 0; i < selectedItem.length; i++) {
|
||||||
|
if (selectedItem[i] === this) {
|
||||||
|
toSelect = false;
|
||||||
|
selectedItem.splice(i, 1);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
if (toSelect && selectedItem.length < choiceNum)
|
||||||
selectedItem.sort((a, b) => a.x - b.x);
|
selectedItem.push(this);
|
||||||
|
|
||||||
if (selectedItem.length > choiceNum)
|
|
||||||
selectedItem.splice(choiceNum, selectedItem.length - choiceNum);
|
|
||||||
|
|
||||||
updatePosition();
|
updatePosition();
|
||||||
|
}
|
||||||
|
|
||||||
|
onReleased: {
|
||||||
|
if (!isClicked)
|
||||||
|
arrangeCards();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function arrangeCards()
|
||||||
|
{
|
||||||
|
let item, i;
|
||||||
|
|
||||||
|
selectedItem = [];
|
||||||
|
for (i = 0; i < generalList.count; i++) {
|
||||||
|
item = generalCardList.itemAt(i);
|
||||||
|
if (item.y > splitLine.y)
|
||||||
|
selectedItem.push(item);
|
||||||
}
|
}
|
||||||
|
|
||||||
function updatePosition()
|
selectedItem.sort((a, b) => a.x - b.x);
|
||||||
{
|
|
||||||
choices = [];
|
|
||||||
let item, magnet, pos, i;
|
|
||||||
for (i = 0; i < selectedItem.length && i < resultList.count; i++) {
|
|
||||||
item = selectedItem[i];
|
|
||||||
choices.push(item.name);
|
|
||||||
magnet = resultList.itemAt(i);
|
|
||||||
pos = root.mapFromItem(resultArea, magnet.x, magnet.y);
|
|
||||||
if (item.origX !== pos.x || item.origY !== item.y) {
|
|
||||||
item.origX = pos.x;
|
|
||||||
item.origY = pos.y;
|
|
||||||
item.goBack(true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fightButton.enabled = (choices.length == choiceNum);
|
if (selectedItem.length > choiceNum)
|
||||||
|
selectedItem.splice(choiceNum, selectedItem.length - choiceNum);
|
||||||
|
|
||||||
for (i = 0; i < generalCardList.count; i++) {
|
updatePosition();
|
||||||
item = generalCardList.itemAt(i);
|
}
|
||||||
if (selectedItem.indexOf(item) != -1)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
magnet = generalMagnetList.itemAt(i);
|
function updatePosition()
|
||||||
pos = root.mapFromItem(generalMagnetList.parent, magnet.x, magnet.y);
|
{
|
||||||
if (item.origX !== pos.x || item.origY !== item.y) {
|
choices = [];
|
||||||
item.origX = pos.x;
|
let item, magnet, pos, i;
|
||||||
item.origY = pos.y;
|
for (i = 0; i < selectedItem.length && i < resultList.count; i++) {
|
||||||
item.goBack(true);
|
item = selectedItem[i];
|
||||||
}
|
choices.push(item.name);
|
||||||
}
|
magnet = resultList.itemAt(i);
|
||||||
|
pos = root.mapFromItem(resultArea, magnet.x, magnet.y);
|
||||||
|
if (item.origX !== pos.x || item.origY !== item.y) {
|
||||||
|
item.origX = pos.x;
|
||||||
|
item.origY = pos.y;
|
||||||
|
item.goBack(true);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fightButton.enabled = (choices.length == choiceNum);
|
||||||
|
|
||||||
|
for (i = 0; i < generalCardList.count; i++) {
|
||||||
|
item = generalCardList.itemAt(i);
|
||||||
|
if (selectedItem.indexOf(item) != -1)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
magnet = generalMagnetList.itemAt(i);
|
||||||
|
pos = root.mapFromItem(generalMagnetList.parent, magnet.x, magnet.y);
|
||||||
|
if (item.origX !== pos.x || item.origY !== item.y) {
|
||||||
|
item.origX = pos.x;
|
||||||
|
item.origY = pos.y;
|
||||||
|
item.goBack(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,29 +3,166 @@ import QtQuick.Layouts 1.1
|
||||||
import QtGraphicalEffects 1.0
|
import QtGraphicalEffects 1.0
|
||||||
|
|
||||||
RowLayout {
|
RowLayout {
|
||||||
id: root
|
id: root
|
||||||
|
|
||||||
property alias self: selfPhoto
|
property alias self: selfPhoto
|
||||||
property alias handcardArea: handcardAreaItem
|
property alias handcardArea: handcardAreaItem
|
||||||
property alias equipArea: selfPhoto.equipArea
|
property alias equipArea: selfPhoto.equipArea
|
||||||
property alias delayedTrickArea: selfPhoto.delayedTrickArea
|
property alias delayedTrickArea: selfPhoto.delayedTrickArea
|
||||||
property alias specialArea: selfPhoto.specialArea
|
property alias specialArea: selfPhoto.specialArea
|
||||||
|
|
||||||
Item {
|
property bool selected: selfPhoto.selected
|
||||||
width: 40
|
|
||||||
|
property bool is_pending: false
|
||||||
|
property string pending_skill: ""
|
||||||
|
property var pending_card
|
||||||
|
property var pendings: [] // int[], store cid
|
||||||
|
property int selected_card: -1
|
||||||
|
|
||||||
|
signal cardSelected(var card)
|
||||||
|
|
||||||
|
Item {
|
||||||
|
width: 40
|
||||||
|
}
|
||||||
|
|
||||||
|
HandcardArea {
|
||||||
|
id: handcardAreaItem
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.preferredHeight: 130
|
||||||
|
Layout.alignment: Qt.AlignVCenter
|
||||||
|
}
|
||||||
|
|
||||||
|
Photo {
|
||||||
|
id: selfPhoto
|
||||||
|
handcards: handcardAreaItem.length
|
||||||
|
}
|
||||||
|
|
||||||
|
Item { width: 5 }
|
||||||
|
|
||||||
|
Connections {
|
||||||
|
target: handcardAreaItem
|
||||||
|
function onCardSelected(cardId, selected) {
|
||||||
|
dashboard.selectCard(cardId, selected);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function disableAllCards() {
|
||||||
|
handcardAreaItem.enableCards([]);
|
||||||
|
}
|
||||||
|
|
||||||
|
function unSelectAll(expectId) {
|
||||||
|
handcardAreaItem.unselectAll(expectId);
|
||||||
|
}
|
||||||
|
|
||||||
|
function enableCards() {
|
||||||
|
// TODO: expand pile
|
||||||
|
let ids = [], cards = handcardAreaItem.cards;
|
||||||
|
for (let i = 0; i < cards.length; i++) {
|
||||||
|
if (JSON.parse(Backend.callLuaFunction("CanUseCard", [cards[i].cid, Self.id])))
|
||||||
|
ids.push(cards[i].cid);
|
||||||
|
}
|
||||||
|
handcardAreaItem.enableCards(ids)
|
||||||
|
}
|
||||||
|
|
||||||
|
function selectCard(cardId, selected) {
|
||||||
|
if (pending_skill !== "") {
|
||||||
|
if (selected) {
|
||||||
|
pendings.push(cardId);
|
||||||
|
} else {
|
||||||
|
pendings.splice(pendings.indexOf(cardId), 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
updatePending();
|
||||||
|
} else {
|
||||||
|
if (selected) {
|
||||||
|
handcardAreaItem.unselectAll(cardId);
|
||||||
|
selected_card = cardId;
|
||||||
|
} else {
|
||||||
|
handcardAreaItem.unselectAll();
|
||||||
|
selected_card = -1;
|
||||||
|
}
|
||||||
|
cardSelected(selected_card);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function getSelectedCard() {
|
||||||
|
if (pending_skill !== "") {
|
||||||
|
return JSON.stringify({
|
||||||
|
skill: pending_skill,
|
||||||
|
subcards: pendings
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
return selected_card;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function updatePending() {
|
||||||
|
if (pending_skill === "") return;
|
||||||
|
|
||||||
|
let enabled_cards = [];
|
||||||
|
|
||||||
|
handcardAreaItem.cards.forEach(function(card) {
|
||||||
|
if (card.selected || Router.vs_view_filter(pending_skill, pendings, card.cid))
|
||||||
|
enabled_cards.push(card.cid);
|
||||||
|
});
|
||||||
|
handcardAreaItem.enableCards(enabled_cards);
|
||||||
|
|
||||||
|
let equip;
|
||||||
|
for (let i = 0; i < 5; i++) {
|
||||||
|
equip = equipAreaItem.equips.itemAt(i);
|
||||||
|
if (equip.selected || equip.cid !== -1 &&
|
||||||
|
Router.vs_view_filter(pending_skill, pendings, equip.cid))
|
||||||
|
enabled_cards.push(equip.cid);
|
||||||
|
}
|
||||||
|
equipAreaItem.enableCards(enabled_cards);
|
||||||
|
|
||||||
|
if (Router.vs_can_view_as(pending_skill, pendings)) {
|
||||||
|
pending_card = {
|
||||||
|
skill: pending_skill,
|
||||||
|
subcards: pendings
|
||||||
|
};
|
||||||
|
cardSelected(JSON.stringify(pending_card));
|
||||||
|
} else {
|
||||||
|
pending_card = -1;
|
||||||
|
cardSelected(pending_card);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function startPending(skill_name) {
|
||||||
|
pending_skill = skill_name;
|
||||||
|
pendings = [];
|
||||||
|
handcardAreaItem.unselectAll();
|
||||||
|
|
||||||
|
// TODO: expand pile
|
||||||
|
|
||||||
|
// TODO: equipment
|
||||||
|
|
||||||
|
updatePending();
|
||||||
|
}
|
||||||
|
|
||||||
|
function deactivateSkillButton() {
|
||||||
|
for (let i = 0; i < headSkills.length; i++) {
|
||||||
|
headSkillButtons.itemAt(i).pressed = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function stopPending() {
|
||||||
|
pending_skill = "";
|
||||||
|
pending_card = -1;
|
||||||
|
|
||||||
|
// TODO: expand pile
|
||||||
|
|
||||||
|
let equip;
|
||||||
|
for (let i = 0; i < 5; i++) {
|
||||||
|
equip = equipAreaItem.equips.itemAt(i);
|
||||||
|
if (equip.name !== "") {
|
||||||
|
equip.selected = false;
|
||||||
|
equip.selectable = false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
HandcardArea {
|
pendings = [];
|
||||||
id: handcardAreaItem
|
handcardAreaItem.adjustCards();
|
||||||
Layout.fillWidth: true
|
cardSelected(-1);
|
||||||
Layout.preferredHeight: 130
|
}
|
||||||
Layout.alignment: Qt.AlignVCenter
|
|
||||||
}
|
|
||||||
|
|
||||||
Photo {
|
|
||||||
id: selfPhoto
|
|
||||||
handcards: handcardAreaItem.length
|
|
||||||
}
|
|
||||||
|
|
||||||
Item { width: 5 }
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,12 +13,12 @@ import "../skin-bank.js" as SkinBank
|
||||||
*/
|
*/
|
||||||
|
|
||||||
CardItem {
|
CardItem {
|
||||||
property string kingdom: "qun"
|
property string kingdom: "qun"
|
||||||
name: "caocao"
|
name: "caocao"
|
||||||
// description: Sanguosha.getGeneralDescription(name)
|
// description: Sanguosha.getGeneralDescription(name)
|
||||||
suit: ""
|
suit: ""
|
||||||
number: 0
|
number: 0
|
||||||
footnote: ""
|
footnote: ""
|
||||||
card.source: SkinBank.GENERAL_DIR + name
|
card.source: SkinBank.GENERAL_DIR + name
|
||||||
glow.color: "white" //Engine.kingdomColor[kingdom]
|
glow.color: "white" //Engine.kingdomColor[kingdom]
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,29 +2,29 @@ import QtQuick 2.15
|
||||||
import QtGraphicalEffects 1.0
|
import QtGraphicalEffects 1.0
|
||||||
|
|
||||||
Item {
|
Item {
|
||||||
property alias text: textItem.text
|
property alias text: textItem.text
|
||||||
property alias color: textItem.color
|
property alias color: textItem.color
|
||||||
property alias font: textItem.font
|
property alias font: textItem.font
|
||||||
property alias fontSizeMode: textItem.fontSizeMode
|
property alias fontSizeMode: textItem.fontSizeMode
|
||||||
property alias horizontalAlignment: textItem.horizontalAlignment
|
property alias horizontalAlignment: textItem.horizontalAlignment
|
||||||
property alias verticalAlignment: textItem.verticalAlignment
|
property alias verticalAlignment: textItem.verticalAlignment
|
||||||
property alias style: textItem.style
|
property alias style: textItem.style
|
||||||
property alias styleColor: textItem.styleColor
|
property alias styleColor: textItem.styleColor
|
||||||
property alias wrapMode: textItem.wrapMode
|
property alias wrapMode: textItem.wrapMode
|
||||||
property alias lineHeight: textItem.lineHeight
|
property alias lineHeight: textItem.lineHeight
|
||||||
property alias glow: glowItem
|
property alias glow: glowItem
|
||||||
|
|
||||||
width: textItem.implicitWidth
|
width: textItem.implicitWidth
|
||||||
height: textItem.implicitHeight
|
height: textItem.implicitHeight
|
||||||
|
|
||||||
Text {
|
Text {
|
||||||
id: textItem
|
id: textItem
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
}
|
}
|
||||||
|
|
||||||
Glow {
|
Glow {
|
||||||
id: glowItem
|
id: glowItem
|
||||||
source: textItem
|
source: textItem
|
||||||
anchors.fill: textItem
|
anchors.fill: textItem
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,52 +2,52 @@ import QtQuick 2.15
|
||||||
import QtGraphicalEffects 1.0
|
import QtGraphicalEffects 1.0
|
||||||
|
|
||||||
Item {
|
Item {
|
||||||
property alias title: titleItem
|
property alias title: titleItem
|
||||||
signal accepted() //Read result
|
signal accepted() //Read result
|
||||||
signal finished() //Close the box
|
signal finished() //Close the box
|
||||||
|
|
||||||
id: root
|
id: root
|
||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
id: background
|
id: background
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
color: "#B0000000"
|
color: "#B0000000"
|
||||||
radius: 5
|
radius: 5
|
||||||
border.color: "#A6967A"
|
border.color: "#A6967A"
|
||||||
border.width: 1
|
border.width: 1
|
||||||
}
|
}
|
||||||
|
|
||||||
DropShadow {
|
DropShadow {
|
||||||
source: background
|
source: background
|
||||||
anchors.fill: background
|
anchors.fill: background
|
||||||
color: "#B0000000"
|
color: "#B0000000"
|
||||||
radius: 5
|
radius: 5
|
||||||
samples: 12
|
samples: 12
|
||||||
spread: 0.2
|
spread: 0.2
|
||||||
horizontalOffset: 5
|
horizontalOffset: 5
|
||||||
verticalOffset: 4
|
verticalOffset: 4
|
||||||
transparentBorder: true
|
transparentBorder: true
|
||||||
}
|
}
|
||||||
|
|
||||||
Text {
|
Text {
|
||||||
id: titleItem
|
id: titleItem
|
||||||
color: "#E4D5A0"
|
color: "#E4D5A0"
|
||||||
font.pixelSize: 18
|
font.pixelSize: 18
|
||||||
horizontalAlignment: Text.AlignHCenter
|
horizontalAlignment: Text.AlignHCenter
|
||||||
anchors.top: parent.top
|
anchors.top: parent.top
|
||||||
anchors.topMargin: 4
|
anchors.topMargin: 4
|
||||||
anchors.horizontalCenter: parent.horizontalCenter
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
}
|
}
|
||||||
|
|
||||||
MouseArea {
|
MouseArea {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
drag.target: parent
|
drag.target: parent
|
||||||
drag.axis: Drag.XAndYAxis
|
drag.axis: Drag.XAndYAxis
|
||||||
}
|
}
|
||||||
|
|
||||||
function close()
|
function close()
|
||||||
{
|
{
|
||||||
accepted();
|
accepted();
|
||||||
finished();
|
finished();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,130 +2,130 @@ import QtQuick 2.15
|
||||||
import "../../util.js" as Utility
|
import "../../util.js" as Utility
|
||||||
|
|
||||||
Item {
|
Item {
|
||||||
property alias cards: cardArea.cards
|
property alias cards: cardArea.cards
|
||||||
property alias length: cardArea.length
|
property alias length: cardArea.length
|
||||||
property var selectedCards: []
|
property var selectedCards: []
|
||||||
|
|
||||||
signal cardSelected(int cardId, bool selected)
|
signal cardSelected(int cardId, bool selected)
|
||||||
|
|
||||||
id: area
|
id: area
|
||||||
|
|
||||||
CardArea {
|
CardArea {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
id: cardArea
|
id: cardArea
|
||||||
onLengthChanged: area.updateCardPosition(true);
|
onLengthChanged: area.updateCardPosition(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
function add(inputs)
|
||||||
|
{
|
||||||
|
cardArea.add(inputs);
|
||||||
|
if (inputs instanceof Array) {
|
||||||
|
for (let i = 0; i < inputs.length; i++)
|
||||||
|
filterInputCard(inputs[i]);
|
||||||
|
} else {
|
||||||
|
filterInputCard(inputs);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function filterInputCard(card)
|
||||||
|
{
|
||||||
|
card.autoBack = true;
|
||||||
|
card.draggable = true;
|
||||||
|
card.selectable = false;
|
||||||
|
card.clicked.connect(adjustCards);
|
||||||
|
}
|
||||||
|
|
||||||
|
function remove(outputs)
|
||||||
|
{
|
||||||
|
let result = cardArea.remove(outputs);
|
||||||
|
let card;
|
||||||
|
for (let i = 0; i < result.length; i++) {
|
||||||
|
card = result[i];
|
||||||
|
card.draggable = false;
|
||||||
|
card.selectable = false;
|
||||||
|
card.selectedChanged.disconnect(adjustCards);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
function enableCards(cardIds)
|
||||||
|
{
|
||||||
|
let card, i;
|
||||||
|
for (i = 0; i < cards.length; i++) {
|
||||||
|
card = cards[i];
|
||||||
|
card.selectable = cardIds.contains(card.cid);
|
||||||
|
if (!card.selectable) {
|
||||||
|
card.selected = false;
|
||||||
|
unselectCard(card);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateCardPosition(animated)
|
||||||
|
{
|
||||||
|
cardArea.updateCardPosition(false);
|
||||||
|
|
||||||
|
let i, card;
|
||||||
|
for (i = 0; i < cards.length; i++) {
|
||||||
|
card = cards[i];
|
||||||
|
if (card.selected)
|
||||||
|
card.origY -= 20;
|
||||||
}
|
}
|
||||||
|
|
||||||
function add(inputs)
|
if (animated) {
|
||||||
{
|
for (i = 0; i < cards.length; i++)
|
||||||
cardArea.add(inputs);
|
cards[i].goBack(true)
|
||||||
if (inputs instanceof Array) {
|
|
||||||
for (let i = 0; i < inputs.length; i++)
|
|
||||||
filterInputCard(inputs[i]);
|
|
||||||
} else {
|
|
||||||
filterInputCard(inputs);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function filterInputCard(card)
|
function adjustCards()
|
||||||
{
|
{
|
||||||
card.autoBack = true;
|
area.updateCardPosition(true);
|
||||||
card.draggable = true;
|
|
||||||
card.selectable = false;
|
for (let i = 0; i < cards.length; i++) {
|
||||||
card.clicked.connect(adjustCards);
|
let card = cards[i];
|
||||||
|
if (card.selected) {
|
||||||
|
if (!selectedCards.contains(card))
|
||||||
|
selectCard(card);
|
||||||
|
} else {
|
||||||
|
if (selectedCards.contains(card))
|
||||||
|
unselectCard(card);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function remove(outputs)
|
function selectCard(card)
|
||||||
{
|
{
|
||||||
let result = cardArea.remove(outputs);
|
selectedCards.push(card);
|
||||||
let card;
|
cardSelected(card.cid, true);
|
||||||
for (let i = 0; i < result.length; i++) {
|
}
|
||||||
card = result[i];
|
|
||||||
card.draggable = false;
|
function unselectCard(card)
|
||||||
card.selectable = false;
|
{
|
||||||
card.selectedChanged.disconnect(adjustCards);
|
for (let i = 0; i < selectedCards.length; i++) {
|
||||||
}
|
if (selectedCards[i] === card) {
|
||||||
return result;
|
selectedCards.splice(i, 1);
|
||||||
|
cardSelected(card.cid, false);
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function enableCards(cardIds)
|
function unselectAll(exceptId) {
|
||||||
{
|
let card = undefined;
|
||||||
let card, i;
|
for (let i = 0; i < selectedCards.length; i++) {
|
||||||
for (i = 0; i < cards.length; i++) {
|
if (selectedCards[i].cid !== exceptId) {
|
||||||
card = cards[i];
|
selectedCards[i].selected = false;
|
||||||
card.selectable = cardIds.contains(card.cid);
|
} else {
|
||||||
if (!card.selectable) {
|
card = selectedCards[i];
|
||||||
card.selected = false;
|
card.selected = true;
|
||||||
unselectCard(card);
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
if (card === undefined) {
|
||||||
function updateCardPosition(animated)
|
selectedCards = [];
|
||||||
{
|
} else {
|
||||||
cardArea.updateCardPosition(false);
|
selectedCards = [card];
|
||||||
|
|
||||||
let i, card;
|
|
||||||
for (i = 0; i < cards.length; i++) {
|
|
||||||
card = cards[i];
|
|
||||||
if (card.selected)
|
|
||||||
card.origY -= 20;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (animated) {
|
|
||||||
for (i = 0; i < cards.length; i++)
|
|
||||||
cards[i].goBack(true)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function adjustCards()
|
|
||||||
{
|
|
||||||
area.updateCardPosition(true);
|
|
||||||
|
|
||||||
for (let i = 0; i < cards.length; i++) {
|
|
||||||
let card = cards[i];
|
|
||||||
if (card.selected) {
|
|
||||||
if (!selectedCards.contains(card))
|
|
||||||
selectCard(card);
|
|
||||||
} else {
|
|
||||||
if (selectedCards.contains(card))
|
|
||||||
unselectCard(card);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function selectCard(card)
|
|
||||||
{
|
|
||||||
selectedCards.push(card);
|
|
||||||
cardSelected(card.cid, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
function unselectCard(card)
|
|
||||||
{
|
|
||||||
for (let i = 0; i < selectedCards.length; i++) {
|
|
||||||
if (selectedCards[i] === card) {
|
|
||||||
selectedCards.splice(i, 1);
|
|
||||||
cardSelected(card.cid, false);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function unselectAll(exceptId) {
|
|
||||||
let card = undefined;
|
|
||||||
for (let i = 0; i < selectedCards.length; i++) {
|
|
||||||
if (selectedCards[i].cid !== exceptId) {
|
|
||||||
selectedCards[i].selected = false;
|
|
||||||
} else {
|
|
||||||
card = selectedCards[i];
|
|
||||||
card.selected = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (card === undefined) {
|
|
||||||
selectedCards = [];
|
|
||||||
} else {
|
|
||||||
selectedCards = [card];
|
|
||||||
}
|
|
||||||
updateCardPosition(true);
|
|
||||||
}
|
}
|
||||||
|
updateCardPosition(true);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,101 +1,101 @@
|
||||||
import QtQuick 2.15
|
import QtQuick 2.15
|
||||||
|
|
||||||
Item {
|
Item {
|
||||||
property point start: Qt.point(0, 0)
|
property point start: Qt.point(0, 0)
|
||||||
property var end: []
|
property var end: []
|
||||||
property alias running: pointToAnimation.running
|
property alias running: pointToAnimation.running
|
||||||
property color color: "#96943D"
|
property color color: "#96943D"
|
||||||
property real ratio: 0
|
property real ratio: 0
|
||||||
property int lineWidth: 6
|
property int lineWidth: 6
|
||||||
|
|
||||||
signal finished()
|
signal finished()
|
||||||
|
|
||||||
id: root
|
id: root
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
|
|
||||||
Repeater {
|
Repeater {
|
||||||
model: end
|
model: end
|
||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
width: 6
|
width: 6
|
||||||
height: Math.sqrt(Math.pow(modelData.x - start.x, 2) + Math.pow(modelData.y - start.y, 2)) * ratio
|
height: Math.sqrt(Math.pow(modelData.x - start.x, 2) + Math.pow(modelData.y - start.y, 2)) * ratio
|
||||||
x: start.x
|
x: start.x
|
||||||
y: start.y
|
y: start.y
|
||||||
antialiasing: true
|
antialiasing: true
|
||||||
|
|
||||||
gradient: Gradient {
|
gradient: Gradient {
|
||||||
GradientStop {
|
GradientStop {
|
||||||
position: 0
|
position: 0
|
||||||
color: Qt.rgba(255, 255, 255, 0)
|
color: Qt.rgba(255, 255, 255, 0)
|
||||||
}
|
|
||||||
GradientStop {
|
|
||||||
position: 1
|
|
||||||
color: Qt.rgba(200, 200, 200, 0.12)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Rectangle {
|
|
||||||
anchors.horizontalCenter: parent.horizontalCenter
|
|
||||||
width: 3
|
|
||||||
height: parent.height
|
|
||||||
antialiasing: true
|
|
||||||
|
|
||||||
gradient: Gradient {
|
|
||||||
GradientStop {
|
|
||||||
position: 0
|
|
||||||
color: Qt.rgba(255, 255, 255, 0)
|
|
||||||
}
|
|
||||||
GradientStop {
|
|
||||||
position: 1
|
|
||||||
color: Qt.lighter(root.color)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
transform: Rotation {
|
|
||||||
angle: 0
|
|
||||||
|
|
||||||
Component.onCompleted: {
|
|
||||||
var dx = modelData.x - start.x;
|
|
||||||
var dy = modelData.y - start.y;
|
|
||||||
if (dx > 0) {
|
|
||||||
angle = Math.atan2(dy, dx) / Math.PI * 180 - 90;
|
|
||||||
} else if (dx < 0) {
|
|
||||||
angle = Math.atan2(dy, dx) / Math.PI * 180 + 270;
|
|
||||||
} else if (dy < 0) {
|
|
||||||
angle = 180;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
GradientStop {
|
||||||
|
position: 1
|
||||||
|
color: Qt.rgba(200, 200, 200, 0.12)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
|
width: 3
|
||||||
|
height: parent.height
|
||||||
|
antialiasing: true
|
||||||
|
|
||||||
|
gradient: Gradient {
|
||||||
|
GradientStop {
|
||||||
|
position: 0
|
||||||
|
color: Qt.rgba(255, 255, 255, 0)
|
||||||
|
}
|
||||||
|
GradientStop {
|
||||||
|
position: 1
|
||||||
|
color: Qt.lighter(root.color)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
transform: Rotation {
|
||||||
|
angle: 0
|
||||||
|
|
||||||
|
Component.onCompleted: {
|
||||||
|
var dx = modelData.x - start.x;
|
||||||
|
var dy = modelData.y - start.y;
|
||||||
|
if (dx > 0) {
|
||||||
|
angle = Math.atan2(dy, dx) / Math.PI * 180 - 90;
|
||||||
|
} else if (dx < 0) {
|
||||||
|
angle = Math.atan2(dy, dx) / Math.PI * 180 + 270;
|
||||||
|
} else if (dy < 0) {
|
||||||
|
angle = 180;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SequentialAnimation {
|
||||||
|
id: pointToAnimation
|
||||||
|
|
||||||
|
PropertyAnimation {
|
||||||
|
target: root
|
||||||
|
property: "ratio"
|
||||||
|
to: 1
|
||||||
|
easing.type: Easing.OutCubic
|
||||||
|
duration: 200
|
||||||
}
|
}
|
||||||
|
|
||||||
SequentialAnimation {
|
PauseAnimation {
|
||||||
id: pointToAnimation
|
duration: 200
|
||||||
|
|
||||||
PropertyAnimation {
|
|
||||||
target: root
|
|
||||||
property: "ratio"
|
|
||||||
to: 1
|
|
||||||
easing.type: Easing.OutCubic
|
|
||||||
duration: 200
|
|
||||||
}
|
|
||||||
|
|
||||||
PauseAnimation {
|
|
||||||
duration: 200
|
|
||||||
}
|
|
||||||
|
|
||||||
PropertyAnimation {
|
|
||||||
target: root
|
|
||||||
property: "opacity"
|
|
||||||
to: 0
|
|
||||||
easing.type: Easing.InQuart
|
|
||||||
duration: 300
|
|
||||||
}
|
|
||||||
|
|
||||||
onStopped: {
|
|
||||||
root.visible = false;
|
|
||||||
root.finished();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
PropertyAnimation {
|
||||||
|
target: root
|
||||||
|
property: "opacity"
|
||||||
|
to: 0
|
||||||
|
easing.type: Easing.InQuart
|
||||||
|
duration: 300
|
||||||
|
}
|
||||||
|
|
||||||
|
onStopped: {
|
||||||
|
root.visible = false;
|
||||||
|
root.finished();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,111 +1,111 @@
|
||||||
import QtQuick 2.15
|
import QtQuick 2.15
|
||||||
|
|
||||||
Item {
|
Item {
|
||||||
property var cards: []
|
property var cards: []
|
||||||
property int length: 0
|
property int length: 0
|
||||||
property var pendingInput: []
|
property var pendingInput: []
|
||||||
property bool checkExisting: false
|
property bool checkExisting: false
|
||||||
|
|
||||||
id: root
|
id: root
|
||||||
|
|
||||||
function add(inputs)
|
function add(inputs)
|
||||||
|
{
|
||||||
|
let card;
|
||||||
|
if (inputs instanceof Array) {
|
||||||
|
for (let i = 0; i < inputs.length; i++) {
|
||||||
|
card = inputs[i];
|
||||||
|
pendingInput.push(card);
|
||||||
|
cards.push(card.toData());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (checkExisting)
|
||||||
|
length = cards.length;
|
||||||
|
else
|
||||||
|
length += inputs.length;
|
||||||
|
} else {
|
||||||
|
pendingInput.push(inputs);
|
||||||
|
cards.push(inputs.toData());
|
||||||
|
|
||||||
|
if (checkExisting)
|
||||||
|
length = cards.length;
|
||||||
|
else
|
||||||
|
length++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function _contains(cid)
|
||||||
|
{
|
||||||
|
if (!checkExisting)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
for (let i = 0; i < cards.length; i++)
|
||||||
{
|
{
|
||||||
let card;
|
if (cards[i].cid === cid)
|
||||||
if (inputs instanceof Array) {
|
return true;
|
||||||
for (let i = 0; i < inputs.length; i++) {
|
}
|
||||||
card = inputs[i];
|
return false;
|
||||||
pendingInput.push(card);
|
}
|
||||||
cards.push(card.toData());
|
|
||||||
}
|
|
||||||
|
|
||||||
if (checkExisting)
|
function remove(outputs)
|
||||||
length = cards.length;
|
{
|
||||||
else
|
let component = Qt.createComponent("CardItem.qml");
|
||||||
length += inputs.length;
|
if (component.status !== Component.Ready)
|
||||||
} else {
|
return [];
|
||||||
pendingInput.push(inputs);
|
|
||||||
cards.push(inputs.toData());
|
|
||||||
|
|
||||||
if (checkExisting)
|
let parentPos = roomScene.mapFromItem(root, 0, 0);
|
||||||
length = cards.length;
|
let card;
|
||||||
else
|
let items = [];
|
||||||
length++;
|
for (let i = 0; i < outputs.length; i++) {
|
||||||
|
if (_contains(outputs[i])) {
|
||||||
|
let state = JSON.parse(Backend.callLuaFunction("GetCardData", [outputs[i]]))
|
||||||
|
state.x = parentPos.x;
|
||||||
|
state.y = parentPos.y;
|
||||||
|
state.opacity = 0;
|
||||||
|
card = component.createObject(roomScene, state);
|
||||||
|
card.x -= card.width / 2;
|
||||||
|
card.x += (i - outputs.length / 2) * 15;
|
||||||
|
card.y -= card.height / 2;
|
||||||
|
items.push(card);
|
||||||
|
if (checkExisting) {
|
||||||
|
//@to-do: remove it from cards
|
||||||
|
cards.splice(i, 1);
|
||||||
|
i--;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (checkExisting)
|
||||||
|
length = cards.length;
|
||||||
|
else
|
||||||
|
length -= outputs.length;
|
||||||
|
return items;
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateCardPosition(animated)
|
||||||
|
{
|
||||||
|
let i, card;
|
||||||
|
|
||||||
|
if (animated) {
|
||||||
|
let parentPos = roomScene.mapFromItem(root, 0, 0);
|
||||||
|
for (i = 0; i < pendingInput.length; i++) {
|
||||||
|
card = pendingInput[i];
|
||||||
|
card.origX = parentPos.x - card.width / 2 + ((i - pendingInput.length / 2) * 15);
|
||||||
|
card.origY = parentPos.y - card.height / 2;
|
||||||
|
card.origOpacity = 0;
|
||||||
|
card.destroyOnStop();
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 0; i < pendingInput.length; i++)
|
||||||
|
pendingInput[i].goBack(true);
|
||||||
|
} else {
|
||||||
|
for (i = 0; i < pendingInput.length; i++) {
|
||||||
|
card = pendingInput[i];
|
||||||
|
card.x = parentPos.x - card.width / 2;
|
||||||
|
card.y = parentPos.y - card.height / 2;
|
||||||
|
card.opacity = 1;
|
||||||
|
card.destroy();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function _contains(cid)
|
pendingInput = [];
|
||||||
{
|
}
|
||||||
if (!checkExisting)
|
|
||||||
return true;
|
|
||||||
|
|
||||||
for (let i = 0; i < cards.length; i++)
|
|
||||||
{
|
|
||||||
if (cards[i].cid === cid)
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
function remove(outputs)
|
|
||||||
{
|
|
||||||
let component = Qt.createComponent("CardItem.qml");
|
|
||||||
if (component.status !== Component.Ready)
|
|
||||||
return [];
|
|
||||||
|
|
||||||
let parentPos = roomScene.mapFromItem(root, 0, 0);
|
|
||||||
let card;
|
|
||||||
let items = [];
|
|
||||||
for (let i = 0; i < outputs.length; i++) {
|
|
||||||
if (_contains(outputs[i])) {
|
|
||||||
let state = JSON.parse(Backend.getCardData(outputs[i]))
|
|
||||||
state.x = parentPos.x;
|
|
||||||
state.y = parentPos.y;
|
|
||||||
state.opacity = 0;
|
|
||||||
card = component.createObject(roomScene, state);
|
|
||||||
card.x -= card.width / 2;
|
|
||||||
card.x += (i - outputs.length / 2) * 15;
|
|
||||||
card.y -= card.height / 2;
|
|
||||||
items.push(card);
|
|
||||||
if (checkExisting) {
|
|
||||||
//@to-do: remove it from cards
|
|
||||||
cards.splice(i, 1);
|
|
||||||
i--;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (checkExisting)
|
|
||||||
length = cards.length;
|
|
||||||
else
|
|
||||||
length -= outputs.length;
|
|
||||||
return items;
|
|
||||||
}
|
|
||||||
|
|
||||||
function updateCardPosition(animated)
|
|
||||||
{
|
|
||||||
let i, card;
|
|
||||||
|
|
||||||
if (animated) {
|
|
||||||
let parentPos = roomScene.mapFromItem(root, 0, 0);
|
|
||||||
for (i = 0; i < pendingInput.length; i++) {
|
|
||||||
card = pendingInput[i];
|
|
||||||
card.origX = parentPos.x - card.width / 2 + ((i - pendingInput.length / 2) * 15);
|
|
||||||
card.origY = parentPos.y - card.height / 2;
|
|
||||||
card.origOpacity = 0;
|
|
||||||
card.destroyOnStop();
|
|
||||||
}
|
|
||||||
|
|
||||||
for (i = 0; i < pendingInput.length; i++)
|
|
||||||
pendingInput[i].goBack(true);
|
|
||||||
} else {
|
|
||||||
for (i = 0; i < pendingInput.length; i++) {
|
|
||||||
card = pendingInput[i];
|
|
||||||
card.x = parentPos.x - card.width / 2;
|
|
||||||
card.y = parentPos.y - card.height / 2;
|
|
||||||
card.opacity = 1;
|
|
||||||
card.destroy();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pendingInput = [];
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,309 +5,377 @@ import "PhotoElement"
|
||||||
import "../skin-bank.js" as SkinBank
|
import "../skin-bank.js" as SkinBank
|
||||||
|
|
||||||
Item {
|
Item {
|
||||||
id: root
|
id: root
|
||||||
width: 175
|
width: 175
|
||||||
height: 233
|
height: 233
|
||||||
scale: 0.8
|
scale: 0.8
|
||||||
property string general: ""
|
property int playerid
|
||||||
property string screenName: ""
|
property string general: ""
|
||||||
property string role: "unknown"
|
property string screenName: ""
|
||||||
property string kingdom: "qun"
|
property string role: "unknown"
|
||||||
property string netstate: "online"
|
property string kingdom: "qun"
|
||||||
property alias handcards: handcardAreaItem.length
|
property string netstate: "online"
|
||||||
property int maxHp: 0
|
property alias handcards: handcardAreaItem.length
|
||||||
property int hp: 0
|
property int maxHp: 0
|
||||||
property int seatNumber: 1
|
property int hp: 0
|
||||||
property bool isDead: false
|
property int seatNumber: 1
|
||||||
property bool dying: false
|
property bool isDead: false
|
||||||
property bool faceup: true
|
property bool dying: false
|
||||||
property bool chained: false
|
property bool faceup: true
|
||||||
property bool drank: false
|
property bool chained: false
|
||||||
property bool isOwner: false
|
property bool drank: false
|
||||||
property string status: "normal"
|
property bool isOwner: false
|
||||||
|
property string status: "normal"
|
||||||
|
|
||||||
property alias handcardArea: handcardAreaItem
|
property alias handcardArea: handcardAreaItem
|
||||||
property alias equipArea: equipAreaItem
|
property alias equipArea: equipAreaItem
|
||||||
property alias delayedTrickArea: delayedTrickAreaItem
|
property alias delayedTrickArea: delayedTrickAreaItem
|
||||||
property alias specialArea: handcardAreaItem
|
property alias specialArea: handcardAreaItem
|
||||||
|
|
||||||
property alias progressBar: progressBar
|
property alias progressBar: progressBar
|
||||||
property alias progressTip: progressTip.text
|
property alias progressTip: progressTip.text
|
||||||
|
|
||||||
property bool selectable: false
|
property bool selectable: false
|
||||||
property bool selected: false
|
property bool selected: false
|
||||||
|
|
||||||
Behavior on x {
|
Behavior on x {
|
||||||
NumberAnimation { duration: 600; easing.type: Easing.InOutQuad }
|
NumberAnimation { duration: 600; easing.type: Easing.InOutQuad }
|
||||||
|
}
|
||||||
|
|
||||||
|
Behavior on y {
|
||||||
|
NumberAnimation { duration: 600; easing.type: Easing.InOutQuad }
|
||||||
|
}
|
||||||
|
|
||||||
|
states: [
|
||||||
|
State { name: "normal" },
|
||||||
|
State { name: "candidate" },
|
||||||
|
State { name: "playing" }
|
||||||
|
//State { name: "responding" },
|
||||||
|
//State { name: "sos" }
|
||||||
|
]
|
||||||
|
|
||||||
|
state: "normal"
|
||||||
|
transitions: [
|
||||||
|
Transition {
|
||||||
|
from: "*"; to: "normal"
|
||||||
|
ScriptAction {
|
||||||
|
script: {
|
||||||
|
animPlaying.stop();
|
||||||
|
animSelectable.stop();
|
||||||
|
animSelected.stop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
Transition {
|
||||||
|
from: "*"; to: "playing"
|
||||||
|
ScriptAction {
|
||||||
|
script: {
|
||||||
|
animPlaying.start();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
Transition {
|
||||||
|
from: "*"; to: "candidate"
|
||||||
|
ScriptAction {
|
||||||
|
script: {
|
||||||
|
animSelectable.start();
|
||||||
|
animSelected.start();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
]
|
||||||
|
|
||||||
Behavior on y {
|
PixmapAnimation {
|
||||||
NumberAnimation { duration: 600; easing.type: Easing.InOutQuad }
|
id: animPlaying
|
||||||
}
|
source: "playing"
|
||||||
|
anchors.centerIn: parent
|
||||||
|
loop: true
|
||||||
|
scale: 1.1
|
||||||
|
visible: root.state === "playing"
|
||||||
|
}
|
||||||
|
|
||||||
PixmapAnimation {
|
PixmapAnimation {
|
||||||
id: animFrame
|
id: animSelected
|
||||||
source: "selected"
|
source: "selected"
|
||||||
anchors.centerIn: parent
|
anchors.centerIn: parent
|
||||||
loop: true
|
loop: true
|
||||||
scale: 1.1
|
scale: 1.1
|
||||||
}
|
visible: root.state === "candidate" && selected
|
||||||
|
}
|
||||||
|
|
||||||
Image {
|
Image {
|
||||||
id: back
|
id: back
|
||||||
source: SkinBank.PHOTO_BACK_DIR + root.kingdom
|
source: SkinBank.PHOTO_BACK_DIR + root.kingdom
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Text {
|
||||||
|
id: generalName
|
||||||
|
x: 5
|
||||||
|
y: 28
|
||||||
|
font.family: "FZLiBian-S02"
|
||||||
|
font.pixelSize: 22
|
||||||
|
opacity: 0.7
|
||||||
|
horizontalAlignment: Text.AlignHCenter
|
||||||
|
lineHeight: 18
|
||||||
|
lineHeightMode: Text.FixedHeight
|
||||||
|
color: "white"
|
||||||
|
width: 24
|
||||||
|
wrapMode: Text.WordWrap
|
||||||
|
text: ""
|
||||||
|
}
|
||||||
|
|
||||||
|
HpBar {
|
||||||
|
id: hp
|
||||||
|
x: 8
|
||||||
|
value: root.hp
|
||||||
|
maxValue: root.maxHp
|
||||||
|
anchors.bottom: parent.bottom
|
||||||
|
anchors.bottomMargin: 36
|
||||||
|
}
|
||||||
|
|
||||||
|
Image {
|
||||||
|
id: generalImage
|
||||||
|
width: 138
|
||||||
|
height: 222
|
||||||
|
smooth: true
|
||||||
|
visible: false
|
||||||
|
fillMode: Image.PreserveAspectCrop
|
||||||
|
source: (general != "") ? SkinBank.GENERAL_DIR + general : ""
|
||||||
|
}
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
id: photoMask
|
||||||
|
x: 31
|
||||||
|
y: 5
|
||||||
|
width: 138
|
||||||
|
height: 222
|
||||||
|
radius: 8
|
||||||
|
visible: false
|
||||||
|
}
|
||||||
|
|
||||||
|
OpacityMask {
|
||||||
|
anchors.fill: photoMask
|
||||||
|
source: generalImage
|
||||||
|
maskSource: photoMask
|
||||||
|
}
|
||||||
|
|
||||||
|
Colorize {
|
||||||
|
anchors.fill: photoMask
|
||||||
|
source: generalImage
|
||||||
|
saturation: 0
|
||||||
|
visible: root.isDead
|
||||||
|
}
|
||||||
|
|
||||||
|
Image {
|
||||||
|
anchors.bottom: parent.bottom
|
||||||
|
anchors.right: parent.right
|
||||||
|
anchors.bottomMargin: 8
|
||||||
|
anchors.rightMargin: 4
|
||||||
|
source: SkinBank.PHOTO_DIR + (isOwner ? "owner" : "ready")
|
||||||
|
visible: screenName != "" && !roomScene.isStarted
|
||||||
|
}
|
||||||
|
|
||||||
|
Image {
|
||||||
|
visible: equipAreaItem.length > 0
|
||||||
|
source: SkinBank.PHOTO_DIR + "equipbg"
|
||||||
|
x: 31
|
||||||
|
y: 121
|
||||||
|
}
|
||||||
|
|
||||||
|
Image {
|
||||||
|
source: root.status != "normal" ? SkinBank.STATUS_DIR + root.status : ""
|
||||||
|
x: -6
|
||||||
|
}
|
||||||
|
|
||||||
|
Image {
|
||||||
|
id: turnedOver
|
||||||
|
visible: !root.faceup
|
||||||
|
source: SkinBank.PHOTO_DIR + "faceturned"
|
||||||
|
x: 29; y: 5
|
||||||
|
}
|
||||||
|
|
||||||
|
EquipArea {
|
||||||
|
id: equipAreaItem
|
||||||
|
|
||||||
|
x: 31
|
||||||
|
y: 139
|
||||||
|
}
|
||||||
|
|
||||||
|
Image {
|
||||||
|
id: chain
|
||||||
|
visible: root.chained
|
||||||
|
source: SkinBank.PHOTO_DIR + "chain"
|
||||||
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
|
y: 72
|
||||||
|
}
|
||||||
|
|
||||||
|
Image {
|
||||||
|
// id: saveme
|
||||||
|
visible: root.isDead || root.dying
|
||||||
|
source: SkinBank.DEATH_DIR + (root.isDead ? root.role : "saveme")
|
||||||
|
anchors.centerIn: photoMask
|
||||||
|
}
|
||||||
|
|
||||||
|
Image {
|
||||||
|
id: netstat
|
||||||
|
source: SkinBank.STATE_DIR + root.netstate
|
||||||
|
x: photoMask.x
|
||||||
|
y: photoMask.y
|
||||||
|
}
|
||||||
|
|
||||||
|
Image {
|
||||||
|
id: handcardNum
|
||||||
|
source: SkinBank.PHOTO_DIR + "handcard"
|
||||||
|
anchors.bottom: parent.bottom
|
||||||
|
anchors.bottomMargin: -6
|
||||||
|
x: -6
|
||||||
|
|
||||||
Text {
|
Text {
|
||||||
id: generalName
|
text: root.handcards
|
||||||
x: 5
|
font.family: "FZLiBian-S02"
|
||||||
y: 28
|
font.pixelSize: 32
|
||||||
font.family: "FZLiBian-S02"
|
//font.weight: 30
|
||||||
font.pixelSize: 22
|
color: "white"
|
||||||
opacity: 0.7
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
horizontalAlignment: Text.AlignHCenter
|
anchors.bottom: parent.bottom
|
||||||
lineHeight: 18
|
anchors.bottomMargin: 4
|
||||||
lineHeightMode: Text.FixedHeight
|
style: Text.Outline
|
||||||
color: "white"
|
|
||||||
width: 24
|
|
||||||
wrapMode: Text.WordWrap
|
|
||||||
text: ""
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
HpBar {
|
MouseArea {
|
||||||
id: hp
|
anchors.fill: parent
|
||||||
x: 8
|
onClicked: {
|
||||||
value: root.hp
|
if (parent.state != "candidate" || !parent.selectable)
|
||||||
maxValue: root.maxHp
|
return;
|
||||||
anchors.bottom: parent.bottom
|
parent.selected = !parent.selected;
|
||||||
anchors.bottomMargin: 36
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Image {
|
RoleComboBox {
|
||||||
id: generalImage
|
id: role
|
||||||
width: 138
|
value: root.role
|
||||||
height: 222
|
anchors.top: parent.top
|
||||||
smooth: true
|
anchors.topMargin: -4
|
||||||
visible: false
|
anchors.right: parent.right
|
||||||
fillMode: Image.PreserveAspectCrop
|
anchors.rightMargin: -4
|
||||||
source: (general != "") ? SkinBank.GENERAL_DIR + general : ""
|
}
|
||||||
|
|
||||||
|
Image {
|
||||||
|
visible: root.state === "candidate" && !selectable && !selected
|
||||||
|
source: SkinBank.PHOTO_DIR + "disable"
|
||||||
|
x: 31; y: -21
|
||||||
|
}
|
||||||
|
|
||||||
|
GlowText {
|
||||||
|
id: seatNum
|
||||||
|
visible: !progressBar.visible
|
||||||
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
|
anchors.bottom: parent.bottom
|
||||||
|
anchors.bottomMargin: -32
|
||||||
|
property var seatChr: ["一", "二", "三", "四", "五", "六", "七", "八"]
|
||||||
|
font.family: "FZLiShu II-S06S"
|
||||||
|
font.pixelSize: 32
|
||||||
|
text: seatChr[seatNumber - 1]
|
||||||
|
|
||||||
|
glow.color: "brown"
|
||||||
|
glow.spread: 0.2
|
||||||
|
glow.radius: 8
|
||||||
|
glow.samples: 12
|
||||||
|
}
|
||||||
|
|
||||||
|
SequentialAnimation {
|
||||||
|
id: trembleAnimation
|
||||||
|
running: false
|
||||||
|
PropertyAnimation {
|
||||||
|
target: root
|
||||||
|
property: "x"
|
||||||
|
to: root.x - 20
|
||||||
|
easing.type: Easing.InQuad
|
||||||
|
duration: 100
|
||||||
}
|
}
|
||||||
|
PropertyAnimation {
|
||||||
Rectangle {
|
target: root
|
||||||
id: photoMask
|
property: "x"
|
||||||
x: 31
|
to: root.x
|
||||||
y: 5
|
easing.type: Easing.OutQuad
|
||||||
width: 138
|
duration: 100
|
||||||
height: 222
|
|
||||||
radius: 8
|
|
||||||
visible: false
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function tremble() {
|
||||||
|
trembleAnimation.start()
|
||||||
|
}
|
||||||
|
|
||||||
OpacityMask {
|
ProgressBar {
|
||||||
anchors.fill: photoMask
|
id: progressBar
|
||||||
source: generalImage
|
width: parent.width
|
||||||
maskSource: photoMask
|
height: 4
|
||||||
|
anchors.bottom: parent.bottom
|
||||||
|
anchors.bottomMargin: -4
|
||||||
|
from: 0.0
|
||||||
|
to: 100.0
|
||||||
|
|
||||||
|
visible: false
|
||||||
|
NumberAnimation on value {
|
||||||
|
running: progressBar.visible
|
||||||
|
from: 100.0
|
||||||
|
to: 0.0
|
||||||
|
duration: config.roomTimeout * 1000
|
||||||
|
|
||||||
|
onFinished: {
|
||||||
|
progressBar.visible = false;
|
||||||
|
root.progressTip = "";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Colorize {
|
Image {
|
||||||
anchors.fill: photoMask
|
anchors.top: progressBar.bottom
|
||||||
source: generalImage
|
anchors.topMargin: 1
|
||||||
saturation: 0
|
source: SkinBank.PHOTO_DIR + "control/tip"
|
||||||
visible: root.isDead
|
visible: progressTip.text != ""
|
||||||
|
Text {
|
||||||
|
id: progressTip
|
||||||
|
font.family: "FZLiBian-S02"
|
||||||
|
font.pixelSize: 18
|
||||||
|
x: 18
|
||||||
|
color: "white"
|
||||||
|
text: ""
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Image {
|
PixmapAnimation {
|
||||||
anchors.bottom: parent.bottom
|
id: animSelectable
|
||||||
anchors.right: parent.right
|
source: "selectable"
|
||||||
anchors.bottomMargin: 8
|
anchors.centerIn: parent
|
||||||
anchors.rightMargin: 4
|
loop: true
|
||||||
source: SkinBank.PHOTO_DIR + (isOwner ? "owner" : "ready")
|
visible: root.state === "candidate" && selectable
|
||||||
visible: screenName != "" && !roomScene.isStarted
|
}
|
||||||
}
|
|
||||||
|
|
||||||
Image {
|
InvisibleCardArea {
|
||||||
visible: equipAreaItem.length > 0
|
id: handcardAreaItem
|
||||||
source: SkinBank.PHOTO_DIR + "equipbg"
|
anchors.centerIn: parent
|
||||||
x: 31
|
}
|
||||||
y: 121
|
|
||||||
}
|
|
||||||
|
|
||||||
Image {
|
DelayedTrickArea {
|
||||||
source: root.status != "normal" ? SkinBank.STATUS_DIR + root.status : ""
|
id: delayedTrickAreaItem
|
||||||
x: -6
|
rows: 1
|
||||||
}
|
anchors.bottom: parent.bottom
|
||||||
|
anchors.bottomMargin: 8
|
||||||
|
}
|
||||||
|
|
||||||
Image {
|
InvisibleCardArea {
|
||||||
id: turnedOver
|
id: defaultArea
|
||||||
visible: !root.faceup
|
anchors.centerIn: parent
|
||||||
source: SkinBank.PHOTO_DIR + "faceturned"
|
}
|
||||||
x: 29; y: 5
|
|
||||||
}
|
|
||||||
|
|
||||||
EquipArea {
|
onGeneralChanged: {
|
||||||
id: equipAreaItem
|
if (!roomScene.isStarted) return;
|
||||||
|
generalName.text = Backend.translate(general);
|
||||||
x: 31
|
let data = JSON.parse(Backend.callLuaFunction("GetGeneralData", [general]));
|
||||||
y: 139
|
kingdom = data.kingdom;
|
||||||
}
|
}
|
||||||
|
|
||||||
Image {
|
|
||||||
id: chain
|
|
||||||
visible: root.chained
|
|
||||||
source: SkinBank.PHOTO_DIR + "chain"
|
|
||||||
anchors.horizontalCenter: parent.horizontalCenter
|
|
||||||
y: 72
|
|
||||||
}
|
|
||||||
|
|
||||||
Image {
|
|
||||||
// id: saveme
|
|
||||||
visible: root.isDead || root.dying
|
|
||||||
source: SkinBank.DEATH_DIR + (root.isDead ? root.role : "saveme")
|
|
||||||
anchors.centerIn: photoMask
|
|
||||||
}
|
|
||||||
|
|
||||||
Image {
|
|
||||||
id: netstat
|
|
||||||
source: SkinBank.STATE_DIR + root.netstate
|
|
||||||
x: photoMask.x
|
|
||||||
y: photoMask.y
|
|
||||||
}
|
|
||||||
|
|
||||||
Image {
|
|
||||||
id: handcardNum
|
|
||||||
source: SkinBank.PHOTO_DIR + "handcard"
|
|
||||||
anchors.bottom: parent.bottom
|
|
||||||
anchors.bottomMargin: -6
|
|
||||||
x: -6
|
|
||||||
|
|
||||||
Text {
|
|
||||||
text: root.handcards
|
|
||||||
font.family: "FZLiBian-S02"
|
|
||||||
font.pixelSize: 32
|
|
||||||
//font.weight: 30
|
|
||||||
color: "white"
|
|
||||||
anchors.horizontalCenter: parent.horizontalCenter
|
|
||||||
anchors.bottom: parent.bottom
|
|
||||||
anchors.bottomMargin: 4
|
|
||||||
style: Text.Outline
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
RoleComboBox {
|
|
||||||
id: role
|
|
||||||
value: root.role
|
|
||||||
anchors.top: parent.top
|
|
||||||
anchors.topMargin: -4
|
|
||||||
anchors.right: parent.right
|
|
||||||
anchors.rightMargin: -4
|
|
||||||
}
|
|
||||||
|
|
||||||
GlowText {
|
|
||||||
id: seatNum
|
|
||||||
visible: !progressBar.visible
|
|
||||||
anchors.horizontalCenter: parent.horizontalCenter
|
|
||||||
anchors.bottom: parent.bottom
|
|
||||||
anchors.bottomMargin: -32
|
|
||||||
property var seatChr: ["一", "二", "三", "四", "五", "六", "七", "八"]
|
|
||||||
font.family: "FZLiShu II-S06S"
|
|
||||||
font.pixelSize: 32
|
|
||||||
text: seatChr[seatNumber - 1]
|
|
||||||
|
|
||||||
glow.color: "brown"
|
|
||||||
glow.spread: 0.2
|
|
||||||
glow.radius: 8
|
|
||||||
glow.samples: 12
|
|
||||||
}
|
|
||||||
|
|
||||||
SequentialAnimation {
|
|
||||||
id: trembleAnimation
|
|
||||||
running: false
|
|
||||||
PropertyAnimation {
|
|
||||||
target: root
|
|
||||||
property: "x"
|
|
||||||
to: root.x - 20
|
|
||||||
easing.type: Easing.InQuad
|
|
||||||
duration: 100
|
|
||||||
}
|
|
||||||
PropertyAnimation {
|
|
||||||
target: root
|
|
||||||
property: "x"
|
|
||||||
to: root.x
|
|
||||||
easing.type: Easing.OutQuad
|
|
||||||
duration: 100
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function tremble() {
|
|
||||||
trembleAnimation.start()
|
|
||||||
}
|
|
||||||
|
|
||||||
ProgressBar {
|
|
||||||
id: progressBar
|
|
||||||
width: parent.width
|
|
||||||
height: 4
|
|
||||||
anchors.bottom: parent.bottom
|
|
||||||
anchors.bottomMargin: -4
|
|
||||||
from: 0.0
|
|
||||||
to: 100.0
|
|
||||||
|
|
||||||
visible: false
|
|
||||||
NumberAnimation on value {
|
|
||||||
running: progressBar.visible
|
|
||||||
from: 100.0
|
|
||||||
to: 0.0
|
|
||||||
duration: config.roomTimeout * 1000
|
|
||||||
|
|
||||||
onFinished: {
|
|
||||||
progressBar.visible = false;
|
|
||||||
root.progressTip = "";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Image {
|
|
||||||
anchors.top: progressBar.bottom
|
|
||||||
anchors.topMargin: 1
|
|
||||||
source: SkinBank.PHOTO_DIR + "control/tip"
|
|
||||||
visible: progressTip.text != ""
|
|
||||||
Text {
|
|
||||||
id: progressTip
|
|
||||||
font.family: "FZLiBian-S02"
|
|
||||||
font.pixelSize: 18
|
|
||||||
x: 18
|
|
||||||
color: "white"
|
|
||||||
text: ""
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
PixmapAnimation {
|
|
||||||
id: animSelectable
|
|
||||||
source: "selectable"
|
|
||||||
anchors.centerIn: parent
|
|
||||||
loop: true
|
|
||||||
}
|
|
||||||
|
|
||||||
InvisibleCardArea {
|
|
||||||
id: handcardAreaItem
|
|
||||||
anchors.centerIn: parent
|
|
||||||
}
|
|
||||||
|
|
||||||
DelayedTrickArea {
|
|
||||||
id: delayedTrickAreaItem
|
|
||||||
rows: 1
|
|
||||||
anchors.bottom: parent.bottom
|
|
||||||
anchors.bottomMargin: 8
|
|
||||||
}
|
|
||||||
|
|
||||||
InvisibleCardArea {
|
|
||||||
id: defaultArea
|
|
||||||
anchors.centerIn: parent
|
|
||||||
}
|
|
||||||
|
|
||||||
onGeneralChanged: {
|
|
||||||
if (!roomScene.isStarted) return;
|
|
||||||
generalName.text = Backend.translate(general);
|
|
||||||
let data = JSON.parse(Backend.getGeneralData(general));
|
|
||||||
kingdom = data.kingdom;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,62 +4,62 @@ import ".."
|
||||||
import "../../skin-bank.js" as SkinBank
|
import "../../skin-bank.js" as SkinBank
|
||||||
|
|
||||||
Item {
|
Item {
|
||||||
property alias rows: grid.rows
|
property alias rows: grid.rows
|
||||||
property alias columns: grid.columns
|
property alias columns: grid.columns
|
||||||
|
|
||||||
InvisibleCardArea {
|
InvisibleCardArea {
|
||||||
id: area
|
id: area
|
||||||
checkExisting: true
|
checkExisting: true
|
||||||
|
}
|
||||||
|
|
||||||
|
ListModel {
|
||||||
|
id: cards
|
||||||
|
}
|
||||||
|
|
||||||
|
Grid {
|
||||||
|
id: grid
|
||||||
|
anchors.fill: parent
|
||||||
|
rows: 100
|
||||||
|
columns: 100
|
||||||
|
|
||||||
|
Repeater {
|
||||||
|
model: cards
|
||||||
|
|
||||||
|
Image {
|
||||||
|
source: SkinBank.DELAYED_TRICK_DIR + name
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
ListModel {
|
function add(inputs)
|
||||||
id: cards
|
{
|
||||||
|
area.add(inputs);
|
||||||
|
if (inputs instanceof Array) {
|
||||||
|
cards.append(...inputs);
|
||||||
|
} else {
|
||||||
|
cards.append(inputs);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Grid {
|
function remove(outputs)
|
||||||
id: grid
|
{
|
||||||
anchors.fill: parent
|
let result = area.remove(outputs);
|
||||||
rows: 100
|
for (let i = 0; i < result.length; i++) {
|
||||||
columns: 100
|
let item = result[i];
|
||||||
|
for (let j = 0; j < cards.count; j++) {
|
||||||
Repeater {
|
let icon = cards.get(j);
|
||||||
model: cards
|
if (icon.cid === item.cid) {
|
||||||
|
cards.remove(j, 1);
|
||||||
Image {
|
break;
|
||||||
source: SkinBank.DELAYED_TRICK_DIR + name
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function add(inputs)
|
return result;
|
||||||
{
|
}
|
||||||
area.add(inputs);
|
|
||||||
if (inputs instanceof Array) {
|
|
||||||
cards.append(...inputs);
|
|
||||||
} else {
|
|
||||||
cards.append(inputs);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function remove(outputs)
|
function updateCardPosition(animated)
|
||||||
{
|
{
|
||||||
let result = area.remove(outputs);
|
area.updateCardPosition(animated);
|
||||||
for (let i = 0; i < result.length; i++) {
|
}
|
||||||
let item = result[i];
|
|
||||||
for (let j = 0; j < cards.count; j++) {
|
|
||||||
let icon = cards.get(j);
|
|
||||||
if (icon.cid === item.cid) {
|
|
||||||
cards.remove(j, 1);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
function updateCardPosition(animated)
|
|
||||||
{
|
|
||||||
area.updateCardPosition(animated);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,110 +11,110 @@ import "../../skin-bank.js" as SkinBank
|
||||||
*/
|
*/
|
||||||
|
|
||||||
Column {
|
Column {
|
||||||
height: 88
|
height: 88
|
||||||
width: 138
|
width: 138
|
||||||
property int itemHeight: Math.floor(height / 4)
|
property int itemHeight: Math.floor(height / 4)
|
||||||
property var items: [treasureItem, weaponItem, armorItem, defensiveHorseItem, offensiveHorseItem]
|
property var items: [treasureItem, weaponItem, armorItem, defensiveHorseItem, offensiveHorseItem]
|
||||||
property var subtypes: ["treasure", "weapon", "armor", "defensive_horse", "offensive_horse"]
|
property var subtypes: ["treasure", "weapon", "armor", "defensive_horse", "offensive_horse"]
|
||||||
property int length: area.length
|
property int length: area.length
|
||||||
|
|
||||||
InvisibleCardArea {
|
InvisibleCardArea {
|
||||||
id: area
|
id: area
|
||||||
checkExisting: true
|
checkExisting: true
|
||||||
}
|
}
|
||||||
|
|
||||||
EquipItem {
|
EquipItem {
|
||||||
id: treasureItem
|
id: treasureItem
|
||||||
|
width: parent.width
|
||||||
|
height: itemHeight
|
||||||
|
opacity: 0
|
||||||
|
}
|
||||||
|
|
||||||
|
EquipItem {
|
||||||
|
id: weaponItem
|
||||||
|
width: parent.width
|
||||||
|
height: itemHeight
|
||||||
|
opacity: 0
|
||||||
|
}
|
||||||
|
|
||||||
|
EquipItem {
|
||||||
|
id: armorItem
|
||||||
|
width: parent.width
|
||||||
|
height: itemHeight
|
||||||
|
opacity: 0
|
||||||
|
}
|
||||||
|
|
||||||
|
Row {
|
||||||
|
width: parent.width
|
||||||
|
height: itemHeight
|
||||||
|
|
||||||
|
Item {
|
||||||
|
width: Math.ceil(parent.width / 2)
|
||||||
|
height: itemHeight
|
||||||
|
|
||||||
|
EquipItem {
|
||||||
|
id: defensiveHorseItem
|
||||||
width: parent.width
|
width: parent.width
|
||||||
height: itemHeight
|
height: itemHeight
|
||||||
|
icon: "horse"
|
||||||
opacity: 0
|
opacity: 0
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
EquipItem {
|
Item {
|
||||||
id: weaponItem
|
width: Math.floor(parent.width / 2)
|
||||||
|
height: itemHeight
|
||||||
|
|
||||||
|
EquipItem {
|
||||||
|
id: offensiveHorseItem
|
||||||
width: parent.width
|
width: parent.width
|
||||||
height: itemHeight
|
height: itemHeight
|
||||||
|
icon: "horse"
|
||||||
opacity: 0
|
opacity: 0
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
EquipItem {
|
function add(inputs)
|
||||||
id: armorItem
|
{
|
||||||
width: parent.width
|
area.add(inputs);
|
||||||
height: itemHeight
|
|
||||||
opacity: 0
|
let card, item;
|
||||||
|
if (inputs instanceof Array) {
|
||||||
|
for (let i = 0; i < inputs.length; i++) {
|
||||||
|
card = inputs[i];
|
||||||
|
item = items[subtypes.indexOf(card.subtype)];
|
||||||
|
item.setCard(card);
|
||||||
|
item.show();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
card = inputs;
|
||||||
|
item = items[subtypes.indexOf(card.subtype)];
|
||||||
|
item.setCard(card);
|
||||||
|
item.show();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Row {
|
function remove(outputs)
|
||||||
width: parent.width
|
{
|
||||||
height: itemHeight
|
let result = area.remove(outputs);
|
||||||
|
for (let i = 0; i < result.length; i++) {
|
||||||
Item {
|
let card = result[i];
|
||||||
width: Math.ceil(parent.width / 2)
|
for (let j = 0; j < items.length; j++) {
|
||||||
height: itemHeight
|
let item = items[j];
|
||||||
|
if (item.cid === card.cid) {
|
||||||
EquipItem {
|
item.reset();
|
||||||
id: defensiveHorseItem
|
item.hide();
|
||||||
width: parent.width
|
|
||||||
height: itemHeight
|
|
||||||
icon: "horse"
|
|
||||||
opacity: 0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Item {
|
|
||||||
width: Math.floor(parent.width / 2)
|
|
||||||
height: itemHeight
|
|
||||||
|
|
||||||
EquipItem {
|
|
||||||
id: offensiveHorseItem
|
|
||||||
width: parent.width
|
|
||||||
height: itemHeight
|
|
||||||
icon: "horse"
|
|
||||||
opacity: 0
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function add(inputs)
|
return result;
|
||||||
{
|
}
|
||||||
area.add(inputs);
|
|
||||||
|
|
||||||
let card, item;
|
function updateCardPosition(animated)
|
||||||
if (inputs instanceof Array) {
|
{
|
||||||
for (let i = 0; i < inputs.length; i++) {
|
area.updateCardPosition(animated);
|
||||||
card = inputs[i];
|
}
|
||||||
item = items[subtypes.indexOf(card.subtype)];
|
|
||||||
item.setCard(card);
|
|
||||||
item.show();
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
card = inputs;
|
|
||||||
item = items[subtypes.indexOf(card.subtype)];
|
|
||||||
item.setCard(card);
|
|
||||||
item.show();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function remove(outputs)
|
|
||||||
{
|
|
||||||
let result = area.remove(outputs);
|
|
||||||
for (let i = 0; i < result.length; i++) {
|
|
||||||
let card = result[i];
|
|
||||||
for (let j = 0; j < items.length; j++) {
|
|
||||||
let item = items[j];
|
|
||||||
if (item.cid === card.cid) {
|
|
||||||
item.reset();
|
|
||||||
item.hide();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
function updateCardPosition(animated)
|
|
||||||
{
|
|
||||||
area.updateCardPosition(animated);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -4,139 +4,139 @@ import "../../../util.js" as Utility
|
||||||
import "../../skin-bank.js" as SkinBank
|
import "../../skin-bank.js" as SkinBank
|
||||||
|
|
||||||
Item {
|
Item {
|
||||||
property int cid: 0
|
property int cid: 0
|
||||||
property string name: ""
|
property string name: ""
|
||||||
property string suit: ""
|
property string suit: ""
|
||||||
property int number: 0
|
property int number: 0
|
||||||
|
|
||||||
property string icon: ""
|
property string icon: ""
|
||||||
property alias text: textItem.text
|
property alias text: textItem.text
|
||||||
|
|
||||||
id: root
|
id: root
|
||||||
|
|
||||||
Image {
|
Image {
|
||||||
id: iconItem
|
id: iconItem
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
x: 3
|
x: 3
|
||||||
|
|
||||||
source: icon ? SkinBank.EQUIP_ICON_DIR + icon : ""
|
source: icon ? SkinBank.EQUIP_ICON_DIR + icon : ""
|
||||||
|
}
|
||||||
|
|
||||||
|
Image {
|
||||||
|
id: suitItem
|
||||||
|
anchors.right: parent.right
|
||||||
|
source: suit ? SkinBank.CARD_SUIT_DIR + suit : ""
|
||||||
|
width: implicitWidth / implicitHeight * height
|
||||||
|
height: 16
|
||||||
|
}
|
||||||
|
|
||||||
|
GlowText {
|
||||||
|
id: numberItem
|
||||||
|
visible: number > 0 && number < 14
|
||||||
|
text: Utility.convertNumber(number)
|
||||||
|
color: "white"
|
||||||
|
font.family: "FZLiBian-S02"
|
||||||
|
font.pixelSize: 16
|
||||||
|
glow.color: "black"
|
||||||
|
glow.spread: 0.75
|
||||||
|
glow.radius: 2
|
||||||
|
glow.samples: 4
|
||||||
|
x: parent.width - 24
|
||||||
|
y: 1
|
||||||
|
}
|
||||||
|
|
||||||
|
GlowText {
|
||||||
|
id: textItem
|
||||||
|
font.family: "FZLiBian-S02"
|
||||||
|
color: "white"
|
||||||
|
font.pixelSize: 18
|
||||||
|
glow.color: "black"
|
||||||
|
glow.spread: 0.9
|
||||||
|
glow.radius: 2
|
||||||
|
glow.samples: 6
|
||||||
|
anchors.left: iconItem.right
|
||||||
|
anchors.leftMargin: -8
|
||||||
|
verticalAlignment: Text.AlignVCenter
|
||||||
|
}
|
||||||
|
|
||||||
|
ParallelAnimation {
|
||||||
|
id: showAnime
|
||||||
|
|
||||||
|
NumberAnimation {
|
||||||
|
target: root
|
||||||
|
property: "x"
|
||||||
|
duration: 200
|
||||||
|
easing.type: Easing.InOutQuad
|
||||||
|
from: 10
|
||||||
|
to: 0
|
||||||
}
|
}
|
||||||
|
|
||||||
Image {
|
NumberAnimation {
|
||||||
id: suitItem
|
target: root
|
||||||
anchors.right: parent.right
|
property: "opacity"
|
||||||
source: suit ? SkinBank.CARD_SUIT_DIR + suit : ""
|
duration: 200
|
||||||
width: implicitWidth / implicitHeight * height
|
easing.type: Easing.InOutQuad
|
||||||
height: 16
|
from: 0
|
||||||
|
to: 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ParallelAnimation {
|
||||||
|
id: hideAnime
|
||||||
|
|
||||||
|
NumberAnimation {
|
||||||
|
target: root
|
||||||
|
property: "x"
|
||||||
|
duration: 200
|
||||||
|
easing.type: Easing.InOutQuad
|
||||||
|
from: 0
|
||||||
|
to: 10
|
||||||
}
|
}
|
||||||
|
|
||||||
GlowText {
|
NumberAnimation {
|
||||||
id: numberItem
|
target: root
|
||||||
visible: number > 0 && number < 14
|
property: "opacity"
|
||||||
text: Utility.convertNumber(number)
|
duration: 200
|
||||||
color: "white"
|
easing.type: Easing.InOutQuad
|
||||||
font.family: "FZLiBian-S02"
|
from: 1
|
||||||
font.pixelSize: 16
|
to: 0
|
||||||
glow.color: "black"
|
|
||||||
glow.spread: 0.75
|
|
||||||
glow.radius: 2
|
|
||||||
glow.samples: 4
|
|
||||||
x: parent.width - 24
|
|
||||||
y: 1
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
GlowText {
|
function reset()
|
||||||
id: textItem
|
{
|
||||||
font.family: "FZLiBian-S02"
|
cid = 0;
|
||||||
color: "white"
|
name = "";
|
||||||
font.pixelSize: 18
|
suit = "";
|
||||||
glow.color: "black"
|
number = 0;
|
||||||
glow.spread: 0.9
|
text = "";
|
||||||
glow.radius: 2
|
}
|
||||||
glow.samples: 6
|
|
||||||
anchors.left: iconItem.right
|
function setCard(card)
|
||||||
anchors.leftMargin: -8
|
{
|
||||||
verticalAlignment: Text.AlignVCenter
|
cid = card.cid;
|
||||||
|
name = card.name;
|
||||||
|
suit = card.suit;
|
||||||
|
number = card.number;
|
||||||
|
if (card.subtype === "defensive_horse") {
|
||||||
|
text = "+1";
|
||||||
|
icon = "horse";
|
||||||
|
} else if (card.subtype === "offensive_horse") {
|
||||||
|
text = "-1"
|
||||||
|
icon = "horse";
|
||||||
|
} else {
|
||||||
|
text = Backend.translate(name);
|
||||||
|
icon = name;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
ParallelAnimation {
|
function show()
|
||||||
id: showAnime
|
{
|
||||||
|
showAnime.start();
|
||||||
|
}
|
||||||
|
|
||||||
NumberAnimation {
|
function hide()
|
||||||
target: root
|
{
|
||||||
property: "x"
|
hideAnime.start();
|
||||||
duration: 200
|
}
|
||||||
easing.type: Easing.InOutQuad
|
|
||||||
from: 10
|
|
||||||
to: 0
|
|
||||||
}
|
|
||||||
|
|
||||||
NumberAnimation {
|
|
||||||
target: root
|
|
||||||
property: "opacity"
|
|
||||||
duration: 200
|
|
||||||
easing.type: Easing.InOutQuad
|
|
||||||
from: 0
|
|
||||||
to: 1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ParallelAnimation {
|
|
||||||
id: hideAnime
|
|
||||||
|
|
||||||
NumberAnimation {
|
|
||||||
target: root
|
|
||||||
property: "x"
|
|
||||||
duration: 200
|
|
||||||
easing.type: Easing.InOutQuad
|
|
||||||
from: 0
|
|
||||||
to: 10
|
|
||||||
}
|
|
||||||
|
|
||||||
NumberAnimation {
|
|
||||||
target: root
|
|
||||||
property: "opacity"
|
|
||||||
duration: 200
|
|
||||||
easing.type: Easing.InOutQuad
|
|
||||||
from: 1
|
|
||||||
to: 0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function reset()
|
|
||||||
{
|
|
||||||
cid = 0;
|
|
||||||
name = "";
|
|
||||||
suit = "";
|
|
||||||
number = 0;
|
|
||||||
text = "";
|
|
||||||
}
|
|
||||||
|
|
||||||
function setCard(card)
|
|
||||||
{
|
|
||||||
cid = card.cid;
|
|
||||||
name = card.name;
|
|
||||||
suit = card.suit;
|
|
||||||
number = card.number;
|
|
||||||
if (card.subtype === "defensive_horse") {
|
|
||||||
text = "+1";
|
|
||||||
icon = "horse";
|
|
||||||
} else if (card.subtype === "offensive_horse") {
|
|
||||||
text = "-1"
|
|
||||||
icon = "horse";
|
|
||||||
} else {
|
|
||||||
text = name;
|
|
||||||
icon = name;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function show()
|
|
||||||
{
|
|
||||||
showAnime.start();
|
|
||||||
}
|
|
||||||
|
|
||||||
function hide()
|
|
||||||
{
|
|
||||||
hideAnime.start();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,71 +2,71 @@ import QtQuick 2.15
|
||||||
import ".."
|
import ".."
|
||||||
|
|
||||||
Column {
|
Column {
|
||||||
id: root
|
id: root
|
||||||
property int maxValue: 4
|
property int maxValue: 4
|
||||||
property int value: 4
|
property int value: 4
|
||||||
property var colors: ["#F4180E", "#F4180E", "#E3B006", "#25EC27"]
|
property var colors: ["#F4180E", "#F4180E", "#E3B006", "#25EC27"]
|
||||||
|
|
||||||
Repeater {
|
Repeater {
|
||||||
id: repeater
|
id: repeater
|
||||||
model: maxValue <= 4 ? maxValue : 0
|
model: maxValue <= 4 ? maxValue : 0
|
||||||
Magatama {
|
Magatama {
|
||||||
state: (maxValue - 1 - index) >= value ? 0 : (value >= 3 || value >= maxValue ? 3 : (value <= 0 ? 0 : value))
|
state: (maxValue - 1 - index) >= value ? 0 : (value >= 3 || value >= maxValue ? 3 : (value <= 0 ? 0 : value))
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Column {
|
||||||
|
visible: maxValue > 4
|
||||||
|
spacing: -4
|
||||||
|
|
||||||
|
Magatama {
|
||||||
|
state: (value >= 3 || value >= maxValue) ? 3 : (value <= 0 ? 0 : value)
|
||||||
}
|
}
|
||||||
|
|
||||||
Column {
|
GlowText {
|
||||||
visible: maxValue > 4
|
id: hpItem
|
||||||
spacing: -4
|
width: root.width
|
||||||
|
text: value
|
||||||
|
color: root.colors[(value >= 3 || value >= maxValue) ? 3 : (value <= 0 ? 0 : value)]
|
||||||
|
font.family: "FZLiBian-S02"
|
||||||
|
font.pixelSize: 22
|
||||||
|
font.bold: true
|
||||||
|
horizontalAlignment: Text.AlignHCenter
|
||||||
|
|
||||||
Magatama {
|
glow.color: "#3E3F47"
|
||||||
state: (value >= 3 || value >= maxValue) ? 3 : (value <= 0 ? 0 : value)
|
glow.spread: 0.8
|
||||||
}
|
glow.radius: 8
|
||||||
|
glow.samples: 12
|
||||||
GlowText {
|
|
||||||
id: hpItem
|
|
||||||
width: root.width
|
|
||||||
text: value
|
|
||||||
color: root.colors[(value >= 3 || value >= maxValue) ? 3 : (value <= 0 ? 0 : value)]
|
|
||||||
font.family: "FZLiBian-S02"
|
|
||||||
font.pixelSize: 22
|
|
||||||
font.bold: true
|
|
||||||
horizontalAlignment: Text.AlignHCenter
|
|
||||||
|
|
||||||
glow.color: "#3E3F47"
|
|
||||||
glow.spread: 0.8
|
|
||||||
glow.radius: 8
|
|
||||||
glow.samples: 12
|
|
||||||
}
|
|
||||||
|
|
||||||
GlowText {
|
|
||||||
id: splitter
|
|
||||||
width: root.width
|
|
||||||
text: "/"
|
|
||||||
z: -10
|
|
||||||
color: hpItem.color
|
|
||||||
font: hpItem.font
|
|
||||||
horizontalAlignment: hpItem.horizontalAlignment
|
|
||||||
|
|
||||||
glow.color: hpItem.glow.color
|
|
||||||
glow.spread: hpItem.glow.spread
|
|
||||||
glow.radius: hpItem.glow.radius
|
|
||||||
glow.samples: hpItem.glow.samples
|
|
||||||
}
|
|
||||||
|
|
||||||
GlowText {
|
|
||||||
id: maxHpItem
|
|
||||||
width: root.width
|
|
||||||
text: maxValue
|
|
||||||
color: hpItem.color
|
|
||||||
font: hpItem.font
|
|
||||||
horizontalAlignment: hpItem.horizontalAlignment
|
|
||||||
|
|
||||||
glow.color: hpItem.glow.color
|
|
||||||
glow.spread: hpItem.glow.spread
|
|
||||||
glow.radius: hpItem.glow.radius
|
|
||||||
glow.samples: hpItem.glow.samples
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
GlowText {
|
||||||
|
id: splitter
|
||||||
|
width: root.width
|
||||||
|
text: "/"
|
||||||
|
z: -10
|
||||||
|
color: hpItem.color
|
||||||
|
font: hpItem.font
|
||||||
|
horizontalAlignment: hpItem.horizontalAlignment
|
||||||
|
|
||||||
|
glow.color: hpItem.glow.color
|
||||||
|
glow.spread: hpItem.glow.spread
|
||||||
|
glow.radius: hpItem.glow.radius
|
||||||
|
glow.samples: hpItem.glow.samples
|
||||||
|
}
|
||||||
|
|
||||||
|
GlowText {
|
||||||
|
id: maxHpItem
|
||||||
|
width: root.width
|
||||||
|
text: maxValue
|
||||||
|
color: hpItem.color
|
||||||
|
font: hpItem.font
|
||||||
|
horizontalAlignment: hpItem.horizontalAlignment
|
||||||
|
|
||||||
|
glow.color: hpItem.glow.color
|
||||||
|
glow.spread: hpItem.glow.spread
|
||||||
|
glow.radius: hpItem.glow.radius
|
||||||
|
glow.samples: hpItem.glow.samples
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2,57 +2,57 @@ import QtQuick 2.15
|
||||||
import "../../skin-bank.js" as SkinBank
|
import "../../skin-bank.js" as SkinBank
|
||||||
|
|
||||||
Image {
|
Image {
|
||||||
source: SkinBank.MAGATAMA_DIR + "0"
|
source: SkinBank.MAGATAMA_DIR + "0"
|
||||||
state: "3"
|
state: "3"
|
||||||
|
|
||||||
states: [
|
states: [
|
||||||
State {
|
State {
|
||||||
name: "3"
|
name: "3"
|
||||||
PropertyChanges {
|
PropertyChanges {
|
||||||
target: main
|
target: main
|
||||||
source: SkinBank.MAGATAMA_DIR + "3"
|
source: SkinBank.MAGATAMA_DIR + "3"
|
||||||
opacity: 1
|
opacity: 1
|
||||||
scale: 1
|
scale: 1
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
State {
|
State {
|
||||||
name: "2"
|
name: "2"
|
||||||
PropertyChanges {
|
PropertyChanges {
|
||||||
target: main
|
target: main
|
||||||
source: SkinBank.MAGATAMA_DIR + "2"
|
source: SkinBank.MAGATAMA_DIR + "2"
|
||||||
opacity: 1
|
opacity: 1
|
||||||
scale: 1
|
scale: 1
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
State {
|
State {
|
||||||
name: "1"
|
name: "1"
|
||||||
PropertyChanges {
|
PropertyChanges {
|
||||||
target: main
|
target: main
|
||||||
source: SkinBank.MAGATAMA_DIR + "1"
|
source: SkinBank.MAGATAMA_DIR + "1"
|
||||||
opacity: 1
|
opacity: 1
|
||||||
scale: 1
|
scale: 1
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
State {
|
State {
|
||||||
name: "0"
|
name: "0"
|
||||||
PropertyChanges {
|
PropertyChanges {
|
||||||
target: main
|
target: main
|
||||||
source: SkinBank.MAGATAMA_DIR + "0"
|
source: SkinBank.MAGATAMA_DIR + "0"
|
||||||
opacity: 0
|
opacity: 0
|
||||||
scale: 4
|
scale: 4
|
||||||
}
|
}
|
||||||
}
|
|
||||||
]
|
|
||||||
|
|
||||||
transitions: Transition {
|
|
||||||
PropertyAnimation {
|
|
||||||
properties: "opacity,scale"
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
]
|
||||||
|
|
||||||
Image {
|
transitions: Transition {
|
||||||
id: main
|
PropertyAnimation {
|
||||||
anchors.centerIn: parent
|
properties: "opacity,scale"
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Image {
|
||||||
|
id: main
|
||||||
|
anchors.centerIn: parent
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -3,45 +3,45 @@ import QtQuick 2.15
|
||||||
import "../../skin-bank.js" as SkinBank
|
import "../../skin-bank.js" as SkinBank
|
||||||
|
|
||||||
Image {
|
Image {
|
||||||
|
property string value: "unknown"
|
||||||
|
property var options: ["unknown", "loyalist", "rebel", "renegade"]
|
||||||
|
|
||||||
|
id: root
|
||||||
|
source: visible ? SkinBank.ROLE_DIR + value : ""
|
||||||
|
visible: value != "hidden"
|
||||||
|
|
||||||
|
Image {
|
||||||
property string value: "unknown"
|
property string value: "unknown"
|
||||||
property var options: ["unknown", "loyalist", "rebel", "renegade"]
|
|
||||||
|
|
||||||
id: root
|
id: assumptionBox
|
||||||
source: visible ? SkinBank.ROLE_DIR + value : ""
|
source: SkinBank.ROLE_DIR + value
|
||||||
visible: value != "hidden"
|
visible: root.value == "unknown"
|
||||||
|
|
||||||
Image {
|
MouseArea {
|
||||||
property string value: "unknown"
|
anchors.fill: parent
|
||||||
|
onClicked: optionPopupBox.visible = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
id: assumptionBox
|
Column {
|
||||||
source: SkinBank.ROLE_DIR + value
|
id: optionPopupBox
|
||||||
visible: root.value == "unknown"
|
visible: false
|
||||||
|
spacing: 2
|
||||||
|
|
||||||
|
Repeater {
|
||||||
|
model: options
|
||||||
|
|
||||||
|
Image {
|
||||||
|
source: SkinBank.ROLE_DIR + modelData
|
||||||
|
|
||||||
MouseArea {
|
MouseArea {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
onClicked: optionPopupBox.visible = true;
|
onClicked: {
|
||||||
}
|
optionPopupBox.visible = false;
|
||||||
}
|
assumptionBox.value = modelData;
|
||||||
|
}
|
||||||
Column {
|
|
||||||
id: optionPopupBox
|
|
||||||
visible: false
|
|
||||||
spacing: 2
|
|
||||||
|
|
||||||
Repeater {
|
|
||||||
model: options
|
|
||||||
|
|
||||||
Image {
|
|
||||||
source: SkinBank.ROLE_DIR + modelData
|
|
||||||
|
|
||||||
MouseArea {
|
|
||||||
anchors.fill: parent
|
|
||||||
onClicked: {
|
|
||||||
optionPopupBox.visible = false;
|
|
||||||
assumptionBox.value = modelData;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,87 +3,87 @@ import Qt.labs.folderlistmodel 2.15
|
||||||
import "../skin-bank.js" as SkinBank
|
import "../skin-bank.js" as SkinBank
|
||||||
|
|
||||||
Item {
|
Item {
|
||||||
property string source: ""
|
property string source: ""
|
||||||
property int currentFrame: 0
|
property int currentFrame: 0
|
||||||
property alias interval: timer.interval
|
property alias interval: timer.interval
|
||||||
property int loadedFrameCount: 0
|
property int loadedFrameCount: 0
|
||||||
property bool autoStart: false
|
property bool autoStart: false
|
||||||
property bool loop: false
|
property bool loop: false
|
||||||
|
|
||||||
signal loaded()
|
signal loaded()
|
||||||
signal started()
|
signal started()
|
||||||
signal finished()
|
signal finished()
|
||||||
|
|
||||||
id: root
|
id: root
|
||||||
width: childrenRect.width
|
width: childrenRect.width
|
||||||
height: childrenRect.height
|
height: childrenRect.height
|
||||||
|
|
||||||
FolderListModel {
|
FolderListModel {
|
||||||
id: fileModel
|
id: fileModel
|
||||||
folder: SkinBank.PIXANIM_DIR + source
|
folder: SkinBank.PIXANIM_DIR + source
|
||||||
nameFilters: ["*.png"]
|
nameFilters: ["*.png"]
|
||||||
showDirs: false
|
showDirs: false
|
||||||
}
|
}
|
||||||
|
|
||||||
Repeater {
|
Repeater {
|
||||||
id: frames
|
id: frames
|
||||||
model: fileModel
|
model: fileModel
|
||||||
|
|
||||||
Image {
|
Image {
|
||||||
source: SkinBank.PIXANIM_DIR + root.source + "/" + index
|
source: SkinBank.PIXANIM_DIR + root.source + "/" + index
|
||||||
visible: false
|
visible: false
|
||||||
onStatusChanged: {
|
onStatusChanged: {
|
||||||
if (status == Image.Ready) {
|
if (status == Image.Ready) {
|
||||||
loadedFrameCount++;
|
loadedFrameCount++;
|
||||||
if (loadedFrameCount == fileModel.count)
|
if (loadedFrameCount == fileModel.count)
|
||||||
root.loaded();
|
root.loaded();
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
onLoaded: {
|
onLoaded: {
|
||||||
if (autoStart)
|
if (autoStart)
|
||||||
timer.start();
|
timer.start();
|
||||||
}
|
}
|
||||||
|
|
||||||
Timer {
|
Timer {
|
||||||
id: timer
|
id: timer
|
||||||
interval: 50
|
interval: 50
|
||||||
repeat: true
|
repeat: true
|
||||||
onTriggered: {
|
onTriggered: {
|
||||||
if (currentFrame >= fileModel.count) {
|
if (currentFrame >= fileModel.count) {
|
||||||
frames.itemAt(fileModel.count - 1).visible = false;
|
frames.itemAt(fileModel.count - 1).visible = false;
|
||||||
if (loop) {
|
if (loop) {
|
||||||
currentFrame = 0;
|
currentFrame = 0;
|
||||||
} else {
|
|
||||||
timer.stop();
|
|
||||||
root.finished();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (currentFrame > 0)
|
|
||||||
frames.itemAt(currentFrame - 1).visible = false;
|
|
||||||
frames.itemAt(currentFrame).visible = true;
|
|
||||||
|
|
||||||
currentFrame++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function start()
|
|
||||||
{
|
|
||||||
if (loadedFrameCount == fileModel.count) {
|
|
||||||
timer.start();
|
|
||||||
} else {
|
} else {
|
||||||
root.loaded.connect(function(){
|
timer.stop();
|
||||||
timer.start();
|
root.finished();
|
||||||
});
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function stop()
|
if (currentFrame > 0)
|
||||||
{
|
frames.itemAt(currentFrame - 1).visible = false;
|
||||||
timer.stop();
|
frames.itemAt(currentFrame).visible = true;
|
||||||
|
|
||||||
|
currentFrame++;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function start()
|
||||||
|
{
|
||||||
|
if (loadedFrameCount == fileModel.count) {
|
||||||
|
timer.start();
|
||||||
|
} else {
|
||||||
|
root.loaded.connect(function(){
|
||||||
|
timer.start();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function stop()
|
||||||
|
{
|
||||||
|
timer.stop();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import QtQuick 2.15
|
import QtQuick 2.15
|
||||||
|
|
||||||
Flickable {
|
Flickable {
|
||||||
id: root
|
id: root
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,135 +1,135 @@
|
||||||
import QtQuick 2.15
|
import QtQuick 2.15
|
||||||
|
|
||||||
Item {
|
Item {
|
||||||
property var discardedCards: []
|
property var discardedCards: []
|
||||||
property alias cards: area.cards
|
property alias cards: area.cards
|
||||||
property bool toVanish: false
|
property bool toVanish: false
|
||||||
|
|
||||||
id: root
|
id: root
|
||||||
|
|
||||||
CardArea {
|
CardArea {
|
||||||
id: area
|
id: area
|
||||||
}
|
}
|
||||||
|
|
||||||
InvisibleCardArea {
|
InvisibleCardArea {
|
||||||
id: invisibleArea
|
id: invisibleArea
|
||||||
}
|
}
|
||||||
|
|
||||||
Timer {
|
Timer {
|
||||||
id: vanishTimer
|
id: vanishTimer
|
||||||
interval: 1500
|
interval: 1500
|
||||||
repeat: true
|
repeat: true
|
||||||
running: true
|
running: true
|
||||||
triggeredOnStart: true
|
triggeredOnStart: true
|
||||||
onTriggered: {
|
onTriggered: {
|
||||||
let i, card;
|
let i, card;
|
||||||
if (toVanish) {
|
if (toVanish) {
|
||||||
for (i = 0; i < discardedCards.length; i++) {
|
for (i = 0; i < discardedCards.length; i++) {
|
||||||
card = discardedCards[i];
|
card = discardedCards[i];
|
||||||
card.origOpacity = 0;
|
card.origOpacity = 0;
|
||||||
card.goBack(true);
|
card.goBack(true);
|
||||||
card.destroyOnStop()
|
card.destroyOnStop()
|
||||||
}
|
|
||||||
|
|
||||||
cards.splice(0, discardedCards.length);
|
|
||||||
updateCardPosition(true);
|
|
||||||
|
|
||||||
discardedCards = new Array(cards.length);
|
|
||||||
for (i = 0; i < cards.length; i++)
|
|
||||||
discardedCards[i] = cards[i];
|
|
||||||
toVanish = false
|
|
||||||
} else {
|
|
||||||
for (i = 0; i < discardedCards.length; i++) {
|
|
||||||
discardedCards[i].selectable = false
|
|
||||||
}
|
|
||||||
toVanish = true
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
function add(inputs)
|
cards.splice(0, discardedCards.length);
|
||||||
{
|
|
||||||
area.add(inputs);
|
|
||||||
// if (!inputs instanceof Array)
|
|
||||||
for (let i = 0; i < inputs.length; i++) {
|
|
||||||
inputs[i].footnoteVisible = true
|
|
||||||
inputs[i].selectable = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function remove(outputs)
|
|
||||||
{
|
|
||||||
let i, j;
|
|
||||||
|
|
||||||
let result = area.remove(outputs);
|
|
||||||
let vanished = [];
|
|
||||||
if (result.length < outputs.length) {
|
|
||||||
for (i = 0; i < outputs.length; i++) {
|
|
||||||
let exists = false;
|
|
||||||
for (j = 0; j < result.length; j++) {
|
|
||||||
if (result[j].cid === outputs[i]) {
|
|
||||||
exists = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!exists)
|
|
||||||
vanished.push(outputs[i]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
result = result.concat(invisibleArea.remove(vanished));
|
|
||||||
|
|
||||||
for (i = 0; i < result.length; i++) {
|
|
||||||
for (j = 0; j < discardedCards.length; j++) {
|
|
||||||
if (result[i].cid === discardedCards[j].cid) {
|
|
||||||
discardedCards.splice(j, 1);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
updateCardPosition(true);
|
updateCardPosition(true);
|
||||||
return result;
|
|
||||||
|
discardedCards = new Array(cards.length);
|
||||||
|
for (i = 0; i < cards.length; i++)
|
||||||
|
discardedCards[i] = cards[i];
|
||||||
|
toVanish = false
|
||||||
|
} else {
|
||||||
|
for (i = 0; i < discardedCards.length; i++) {
|
||||||
|
discardedCards[i].selectable = false
|
||||||
|
}
|
||||||
|
toVanish = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function add(inputs)
|
||||||
|
{
|
||||||
|
area.add(inputs);
|
||||||
|
// if (!inputs instanceof Array)
|
||||||
|
for (let i = 0; i < inputs.length; i++) {
|
||||||
|
inputs[i].footnoteVisible = true
|
||||||
|
inputs[i].selectable = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function remove(outputs)
|
||||||
|
{
|
||||||
|
let i, j;
|
||||||
|
|
||||||
|
let result = area.remove(outputs);
|
||||||
|
let vanished = [];
|
||||||
|
if (result.length < outputs.length) {
|
||||||
|
for (i = 0; i < outputs.length; i++) {
|
||||||
|
let exists = false;
|
||||||
|
for (j = 0; j < result.length; j++) {
|
||||||
|
if (result[j].cid === outputs[i]) {
|
||||||
|
exists = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!exists)
|
||||||
|
vanished.push(outputs[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
result = result.concat(invisibleArea.remove(vanished));
|
||||||
|
|
||||||
|
for (i = 0; i < result.length; i++) {
|
||||||
|
for (j = 0; j < discardedCards.length; j++) {
|
||||||
|
if (result[i].cid === discardedCards[j].cid) {
|
||||||
|
discardedCards.splice(j, 1);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
updateCardPosition(true);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateCardPosition(animated)
|
||||||
|
{
|
||||||
|
if (cards.length <= 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
let i, card;
|
||||||
|
|
||||||
|
let overflow = false;
|
||||||
|
for (i = 0; i < cards.length; i++) {
|
||||||
|
card = cards[i];
|
||||||
|
card.origX = i * card.width;
|
||||||
|
if (card.origX + card.width >= root.width) {
|
||||||
|
overflow = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
card.origY = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
function updateCardPosition(animated)
|
if (overflow) {
|
||||||
{
|
//@to-do: Adjust cards in multiple lines if there are too many cards
|
||||||
if (cards.length <= 0)
|
let xLimit = root.width - card.width;
|
||||||
return;
|
let spacing = xLimit / (cards.length - 1);
|
||||||
|
for (i = 0; i < cards.length; i++) {
|
||||||
let i, card;
|
card = cards[i];
|
||||||
|
card.origX = i * spacing;
|
||||||
let overflow = false;
|
card.origY = 0;
|
||||||
for (i = 0; i < cards.length; i++) {
|
}
|
||||||
card = cards[i];
|
|
||||||
card.origX = i * card.width;
|
|
||||||
if (card.origX + card.width >= root.width) {
|
|
||||||
overflow = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
card.origY = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (overflow) {
|
|
||||||
//@to-do: Adjust cards in multiple lines if there are too many cards
|
|
||||||
let xLimit = root.width - card.width;
|
|
||||||
let spacing = xLimit / (cards.length - 1);
|
|
||||||
for (i = 0; i < cards.length; i++) {
|
|
||||||
card = cards[i];
|
|
||||||
card.origX = i * spacing;
|
|
||||||
card.origY = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let offsetX = Math.max(0, (root.width - cards.length * card.width) / 2);
|
|
||||||
let parentPos = roomScene.mapFromItem(root, 0, 0);
|
|
||||||
for (i = 0; i < cards.length; i++) {
|
|
||||||
card = cards[i];
|
|
||||||
card.origX += parentPos.x + offsetX;
|
|
||||||
card.origY += parentPos.y;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (animated) {
|
|
||||||
for (i = 0; i < cards.length; i++)
|
|
||||||
cards[i].goBack(true)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let offsetX = Math.max(0, (root.width - cards.length * card.width) / 2);
|
||||||
|
let parentPos = roomScene.mapFromItem(root, 0, 0);
|
||||||
|
for (i = 0; i < cards.length; i++) {
|
||||||
|
card = cards[i];
|
||||||
|
card.origX += parentPos.x + offsetX;
|
||||||
|
card.origY += parentPos.y;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (animated) {
|
||||||
|
for (i = 0; i < cards.length; i++)
|
||||||
|
cards[i].goBack(true)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,377 +1,465 @@
|
||||||
var Card = {
|
var Card = {
|
||||||
Unknown : 0,
|
Unknown : 0,
|
||||||
PlayerHand : 1,
|
PlayerHand : 1,
|
||||||
PlayerEquip : 2,
|
PlayerEquip : 2,
|
||||||
PlayerJudge : 3,
|
PlayerJudge : 3,
|
||||||
PlayerSpecial : 4,
|
PlayerSpecial : 4,
|
||||||
Processing : 5,
|
Processing : 5,
|
||||||
DrawPile : 6,
|
DrawPile : 6,
|
||||||
DiscardPile : 7,
|
DiscardPile : 7,
|
||||||
Void : 8
|
Void : 8
|
||||||
}
|
}
|
||||||
|
|
||||||
function arrangePhotos() {
|
function arrangePhotos() {
|
||||||
/* Layout of photos:
|
/* Layout of photos:
|
||||||
* +---------------+
|
* +---------------+
|
||||||
* | 6 5 4 3 2 |
|
* | 6 5 4 3 2 |
|
||||||
* | 7 1 |
|
* | 7 1 |
|
||||||
* | dashboard |
|
* | dashboard |
|
||||||
* +---------------+
|
* +---------------+
|
||||||
*/
|
*/
|
||||||
|
|
||||||
const photoWidth = 175;
|
const photoWidth = 175;
|
||||||
const roomAreaPadding = 10;
|
const roomAreaPadding = 10;
|
||||||
let verticalPadding = Math.max(10, roomArea.width * 0.01);
|
let verticalPadding = Math.max(10, roomArea.width * 0.01);
|
||||||
let horizontalSpacing = Math.max(30, roomArea.height * 0.1);
|
let horizontalSpacing = Math.max(30, roomArea.height * 0.1);
|
||||||
let verticalSpacing = (roomArea.width - photoWidth * 7 - verticalPadding * 2) / 6;
|
let verticalSpacing = (roomArea.width - photoWidth * 7 - verticalPadding * 2) / 6;
|
||||||
|
|
||||||
// Position 1-7
|
// Position 1-7
|
||||||
const regions = [
|
const regions = [
|
||||||
{ x: verticalPadding + (photoWidth + verticalSpacing) * 6, y: roomAreaPadding + horizontalSpacing * 2 },
|
{ x: verticalPadding + (photoWidth + verticalSpacing) * 6, y: roomAreaPadding + horizontalSpacing * 2 },
|
||||||
{ x: verticalPadding + (photoWidth + verticalSpacing) * 5, y: roomAreaPadding + horizontalSpacing },
|
{ x: verticalPadding + (photoWidth + verticalSpacing) * 5, y: roomAreaPadding + horizontalSpacing },
|
||||||
{ x: verticalPadding + (photoWidth + verticalSpacing) * 4, y: roomAreaPadding },
|
{ x: verticalPadding + (photoWidth + verticalSpacing) * 4, y: roomAreaPadding },
|
||||||
{ x: verticalPadding + (photoWidth + verticalSpacing) * 3, y: roomAreaPadding },
|
{ x: verticalPadding + (photoWidth + verticalSpacing) * 3, y: roomAreaPadding },
|
||||||
{ x: verticalPadding + (photoWidth + verticalSpacing) * 2, y: roomAreaPadding },
|
{ x: verticalPadding + (photoWidth + verticalSpacing) * 2, y: roomAreaPadding },
|
||||||
{ x: verticalPadding + photoWidth + verticalSpacing, y: roomAreaPadding + horizontalSpacing },
|
{ x: verticalPadding + photoWidth + verticalSpacing, y: roomAreaPadding + horizontalSpacing },
|
||||||
{ x: verticalPadding, y: roomAreaPadding + horizontalSpacing * 2 },
|
{ x: verticalPadding, y: roomAreaPadding + horizontalSpacing * 2 },
|
||||||
];
|
];
|
||||||
|
|
||||||
const regularSeatIndex = [
|
const regularSeatIndex = [
|
||||||
[4],
|
[4],
|
||||||
[3, 5],
|
[3, 5],
|
||||||
[1, 4, 7],
|
[1, 4, 7],
|
||||||
[1, 3, 5, 7],
|
[1, 3, 5, 7],
|
||||||
[1, 3, 4, 5, 7],
|
[1, 3, 4, 5, 7],
|
||||||
[1, 2, 3, 5, 6, 7],
|
[1, 2, 3, 5, 6, 7],
|
||||||
[1, 2, 3, 4, 5, 6, 7],
|
[1, 2, 3, 4, 5, 6, 7],
|
||||||
];
|
];
|
||||||
let seatIndex = regularSeatIndex[playerNum - 2];
|
let seatIndex = regularSeatIndex[playerNum - 2];
|
||||||
|
|
||||||
let item, region, i;
|
let item, region, i;
|
||||||
|
|
||||||
for (i = 0; i < playerNum - 1; i++) {
|
for (i = 0; i < playerNum - 1; i++) {
|
||||||
item = photos.itemAt(i);
|
item = photos.itemAt(i);
|
||||||
if (!item)
|
if (!item)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
region = regions[seatIndex[photoModel.get(i).index] - 1];
|
region = regions[seatIndex[photoModel.get(i).index] - 1];
|
||||||
item.x = region.x;
|
item.x = region.x;
|
||||||
item.y = region.y;
|
item.y = region.y;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function doOkButton() {
|
function doOkButton() {
|
||||||
replyToServer("1");
|
if (roomScene.state == "playing") {
|
||||||
|
replyToServer(JSON.stringify(
|
||||||
|
{
|
||||||
|
card: dashboard.getSelectedCard(),
|
||||||
|
targets: selected_targets
|
||||||
|
}
|
||||||
|
));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
replyToServer("1");
|
||||||
}
|
}
|
||||||
|
|
||||||
function doCancelButton() {
|
function doCancelButton() {
|
||||||
replyToServer("");
|
replyToServer("");
|
||||||
}
|
}
|
||||||
|
|
||||||
function replyToServer(jsonData) {
|
function replyToServer(jsonData) {
|
||||||
roomScene.state = "notactive";
|
roomScene.state = "notactive";
|
||||||
ClientInstance.replyToServer("", jsonData);
|
ClientInstance.replyToServer("", jsonData);
|
||||||
}
|
}
|
||||||
|
|
||||||
function getPhotoModel(id) {
|
function getPhotoModel(id) {
|
||||||
for (let i = 0; i < photoModel.count; i++) {
|
for (let i = 0; i < photoModel.count; i++) {
|
||||||
let item = photoModel.get(i);
|
let item = photoModel.get(i);
|
||||||
if (item.id === id) {
|
if (item.id === id) {
|
||||||
return item;
|
return item;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return undefined;
|
}
|
||||||
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
function getPhoto(id) {
|
function getPhoto(id) {
|
||||||
for (let i = 0; i < photoModel.count; i++) {
|
for (let i = 0; i < photoModel.count; i++) {
|
||||||
let item = photoModel.get(i);
|
let item = photoModel.get(i);
|
||||||
if (item.id === id) {
|
if (item.id === id) {
|
||||||
return photos.itemAt(i);
|
return photos.itemAt(i);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return undefined;
|
}
|
||||||
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
function getPhotoOrDashboard(id) {
|
function getPhotoOrDashboard(id) {
|
||||||
let photo = getPhoto(id);
|
let photo = getPhoto(id);
|
||||||
if (!photo) {
|
if (!photo) {
|
||||||
if (id === Self.id)
|
if (id === Self.id)
|
||||||
return dashboard;
|
return dashboard;
|
||||||
}
|
}
|
||||||
return photo;
|
return photo;
|
||||||
}
|
}
|
||||||
|
|
||||||
function getAreaItem(area, id) {
|
function getAreaItem(area, id) {
|
||||||
if (area === Card.DrawPile) {
|
if (area === Card.DrawPile) {
|
||||||
return drawPile;
|
return drawPile;
|
||||||
} else if (area === Card.DiscardPile || area === Card.Processing) {
|
} else if (area === Card.DiscardPile || area === Card.Processing) {
|
||||||
return tablePile;
|
return tablePile;
|
||||||
} else if (area === Card.AG) {
|
} else if (area === Card.AG) {
|
||||||
return popupBox.item;
|
return popupBox.item;
|
||||||
}
|
}
|
||||||
|
|
||||||
let photo = getPhotoOrDashboard(id);
|
|
||||||
if (!photo) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (area === Card.PlayerHand) {
|
|
||||||
return photo.handcardArea;
|
|
||||||
} else if (area === Card.PlayerEquip)
|
|
||||||
return photo.equipArea;
|
|
||||||
else if (area === Card.PlayerJudge)
|
|
||||||
return photo.delayedTrickArea;
|
|
||||||
else if (area === Card.PlayerSpecial)
|
|
||||||
return photo.specialArea;
|
|
||||||
|
|
||||||
|
let photo = getPhotoOrDashboard(id);
|
||||||
|
if (!photo) {
|
||||||
return null;
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (area === Card.PlayerHand) {
|
||||||
|
return photo.handcardArea;
|
||||||
|
} else if (area === Card.PlayerEquip)
|
||||||
|
return photo.equipArea;
|
||||||
|
else if (area === Card.PlayerJudge)
|
||||||
|
return photo.delayedTrickArea;
|
||||||
|
else if (area === Card.PlayerSpecial)
|
||||||
|
return photo.specialArea;
|
||||||
|
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
function moveCards(moves) {
|
function moveCards(moves) {
|
||||||
for (let i = 0; i < moves.length; i++) {
|
for (let i = 0; i < moves.length; i++) {
|
||||||
let move = moves[i];
|
let move = moves[i];
|
||||||
let from = getAreaItem(move.fromArea, move.from);
|
let from = getAreaItem(move.fromArea, move.from);
|
||||||
let to = getAreaItem(move.toArea, move.to);
|
let to = getAreaItem(move.toArea, move.to);
|
||||||
if (!from || !to || from === to)
|
if (!from || !to || from === to)
|
||||||
continue;
|
continue;
|
||||||
let items = from.remove(move.ids);
|
let items = from.remove(move.ids);
|
||||||
if (items.length > 0)
|
if (items.length > 0)
|
||||||
to.add(items);
|
to.add(items);
|
||||||
to.updateCardPosition(true);
|
to.updateCardPosition(true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function setEmotion(id, emotion) {
|
function setEmotion(id, emotion) {
|
||||||
let component = Qt.createComponent("RoomElement/PixmapAnimation.qml");
|
let component = Qt.createComponent("RoomElement/PixmapAnimation.qml");
|
||||||
if (component.status !== Component.Ready)
|
if (component.status !== Component.Ready)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
let photo = getPhoto(id);
|
let photo = getPhoto(id);
|
||||||
if (!photo) {
|
if (!photo) {
|
||||||
if (id === dashboardModel.id) {
|
if (id === dashboardModel.id) {
|
||||||
photo = dashboard.self;
|
photo = dashboard.self;
|
||||||
} else {
|
} else {
|
||||||
return null;
|
return null;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let animation = component.createObject(photo, {source: emotion, anchors: {centerIn: photo}});
|
let animation = component.createObject(photo, {source: emotion, anchors: {centerIn: photo}});
|
||||||
animation.finished.connect(() => animation.destroy());
|
animation.finished.connect(() => animation.destroy());
|
||||||
animation.start();
|
animation.start();
|
||||||
}
|
}
|
||||||
|
|
||||||
function changeHp(id, delta, losthp) {
|
function changeHp(id, delta, losthp) {
|
||||||
let photo = getPhoto(id);
|
let photo = getPhoto(id);
|
||||||
if (!photo) {
|
if (!photo) {
|
||||||
if (id === dashboardModel.id) {
|
if (id === dashboardModel.id) {
|
||||||
photo = dashboard.self;
|
photo = dashboard.self;
|
||||||
} else {
|
} else {
|
||||||
return null;
|
return null;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if (delta < 0) {
|
}
|
||||||
if (!losthp) {
|
if (delta < 0) {
|
||||||
setEmotion(id, "damage")
|
if (!losthp) {
|
||||||
photo.tremble()
|
setEmotion(id, "damage")
|
||||||
}
|
photo.tremble()
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function doIndicate(from, tos) {
|
function doIndicate(from, tos) {
|
||||||
let component = Qt.createComponent("RoomElement/IndicatorLine.qml");
|
let component = Qt.createComponent("RoomElement/IndicatorLine.qml");
|
||||||
if (component.status !== Component.Ready)
|
if (component.status !== Component.Ready)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
let fromItem = getPhotoOrDashboard(from);
|
let fromItem = getPhotoOrDashboard(from);
|
||||||
let fromPos = mapFromItem(fromItem, fromItem.width / 2, fromItem.height / 2);
|
let fromPos = mapFromItem(fromItem, fromItem.width / 2, fromItem.height / 2);
|
||||||
|
|
||||||
let end = [];
|
let end = [];
|
||||||
for (let i = 0; i < tos.length; i++) {
|
for (let i = 0; i < tos.length; i++) {
|
||||||
if (from === tos[i])
|
if (from === tos[i])
|
||||||
continue;
|
continue;
|
||||||
let toItem = getPhotoOrDashboard(tos[i]);
|
let toItem = getPhotoOrDashboard(tos[i]);
|
||||||
let toPos = mapFromItem(toItem, toItem.width / 2, toItem.height / 2);
|
let toPos = mapFromItem(toItem, toItem.width / 2, toItem.height / 2);
|
||||||
end.push(toPos);
|
end.push(toPos);
|
||||||
}
|
}
|
||||||
|
|
||||||
let color = "#96943D";
|
let color = "#96943D";
|
||||||
let line = component.createObject(roomScene, {start: fromPos, end: end, color: color});
|
let line = component.createObject(roomScene, {start: fromPos, end: end, color: color});
|
||||||
line.finished.connect(() => line.destroy());
|
line.finished.connect(() => line.destroy());
|
||||||
line.running = true;
|
line.running = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
callbacks["AddPlayer"] = function(jsonData) {
|
callbacks["AddPlayer"] = function(jsonData) {
|
||||||
// jsonData: int id, string screenName, string avatar
|
// jsonData: int id, string screenName, string avatar
|
||||||
for (let i = 0; i < photoModel.count; i++) {
|
for (let i = 0; i < photoModel.count; i++) {
|
||||||
let item = photoModel.get(i);
|
let item = photoModel.get(i);
|
||||||
if (item.id === -1) {
|
if (item.id === -1) {
|
||||||
let data = JSON.parse(jsonData);
|
let data = JSON.parse(jsonData);
|
||||||
let uid = data[0];
|
let uid = data[0];
|
||||||
let name = data[1];
|
let name = data[1];
|
||||||
let avatar = data[2];
|
let avatar = data[2];
|
||||||
item.id = uid;
|
item.id = uid;
|
||||||
item.screenName = name;
|
item.screenName = name;
|
||||||
item.general = avatar;
|
item.general = avatar;
|
||||||
return;
|
return;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function enableTargets(card) { // card: int | { skill: string, subcards: int[] }
|
||||||
|
let i = 0;
|
||||||
|
let candidate = (!isNaN(card) && card !== -1) || typeof(card) === "string";
|
||||||
|
let all_photos = [dashboard.self];
|
||||||
|
for (i = 0; i < playerNum - 1; i++) {
|
||||||
|
all_photos.push(photos.itemAt(i))
|
||||||
|
}
|
||||||
|
selected_targets = [];
|
||||||
|
for (i = 0; i < playerNum; i++) {
|
||||||
|
all_photos[i].selected = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (candidate) {
|
||||||
|
let data = {
|
||||||
|
ok_enabled: false,
|
||||||
|
enabled_targets: []
|
||||||
|
}
|
||||||
|
|
||||||
|
all_photos.forEach(photo => {
|
||||||
|
photo.state = "candidate";
|
||||||
|
let id = photo.playerid;
|
||||||
|
let ret = JSON.parse(Backend.callLuaFunction(
|
||||||
|
"CanUseCardToTarget",
|
||||||
|
[card, id, selected_targets]
|
||||||
|
));
|
||||||
|
photo.selectable = ret;
|
||||||
|
})
|
||||||
|
|
||||||
|
okButton.enabled = JSON.parse(Backend.callLuaFunction(
|
||||||
|
"CardFeasible", [card, selected_targets]
|
||||||
|
));
|
||||||
|
} else {
|
||||||
|
all_photos.forEach(photo => {
|
||||||
|
photo.state = "normal";
|
||||||
|
photo.selected = false;
|
||||||
|
});
|
||||||
|
|
||||||
|
okButton.enabled = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateSelectedTargets(playerid, selected) {
|
||||||
|
let i = 0;
|
||||||
|
let card = dashboard.getSelectedCard();
|
||||||
|
let all_photos = [dashboard.self]
|
||||||
|
for (i = 0; i < playerNum - 1; i++) {
|
||||||
|
all_photos.push(photos.itemAt(i))
|
||||||
|
}
|
||||||
|
|
||||||
|
if (selected) {
|
||||||
|
selected_targets.push(playerid);
|
||||||
|
} else {
|
||||||
|
selected_targets.splice(selected_targets.indexOf(playerid), 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
all_photos.forEach(photo => {
|
||||||
|
if (photo.selected) return;
|
||||||
|
let id = photo.playerid;
|
||||||
|
let ret = JSON.parse(Backend.callLuaFunction(
|
||||||
|
"CanUseCardToTarget",
|
||||||
|
[card, id, selected_targets]
|
||||||
|
));
|
||||||
|
photo.selectable = ret;
|
||||||
|
})
|
||||||
|
|
||||||
|
okButton.enabled = JSON.parse(Backend.callLuaFunction(
|
||||||
|
"CardFeasible", [card, selected_targets]
|
||||||
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
callbacks["RemovePlayer"] = function(jsonData) {
|
callbacks["RemovePlayer"] = function(jsonData) {
|
||||||
// jsonData: int uid
|
// jsonData: int uid
|
||||||
let uid = JSON.parse(jsonData)[0];
|
let uid = JSON.parse(jsonData)[0];
|
||||||
let model = getPhotoModel(uid);
|
let model = getPhotoModel(uid);
|
||||||
if (typeof(model) !== "undefined") {
|
if (typeof(model) !== "undefined") {
|
||||||
model.id = -1;
|
model.id = -1;
|
||||||
model.screenName = "";
|
model.screenName = "";
|
||||||
model.general = "";
|
model.general = "";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
callbacks["RoomOwner"] = function(jsonData) {
|
callbacks["RoomOwner"] = function(jsonData) {
|
||||||
// jsonData: int uid of the owner
|
// jsonData: int uid of the owner
|
||||||
let uid = JSON.parse(jsonData)[0];
|
let uid = JSON.parse(jsonData)[0];
|
||||||
|
|
||||||
if (dashboardModel.id === uid) {
|
if (dashboardModel.id === uid) {
|
||||||
dashboardModel.isOwner = true;
|
dashboardModel.isOwner = true;
|
||||||
roomScene.dashboardModelChanged();
|
roomScene.dashboardModelChanged();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
let model = getPhotoModel(uid);
|
let model = getPhotoModel(uid);
|
||||||
if (typeof(model) !== "undefined") {
|
if (typeof(model) !== "undefined") {
|
||||||
model.isOwner = true;
|
model.isOwner = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
callbacks["PropertyUpdate"] = function(jsonData) {
|
callbacks["PropertyUpdate"] = function(jsonData) {
|
||||||
// jsonData: int id, string property_name, value
|
// jsonData: int id, string property_name, value
|
||||||
let data = JSON.parse(jsonData);
|
let data = JSON.parse(jsonData);
|
||||||
let uid = data[0];
|
let uid = data[0];
|
||||||
let property_name = data[1];
|
let property_name = data[1];
|
||||||
let value = data[2];
|
let value = data[2];
|
||||||
|
|
||||||
if (Self.id === uid) {
|
if (Self.id === uid) {
|
||||||
dashboardModel[property_name] = value;
|
dashboardModel[property_name] = value;
|
||||||
roomScene.dashboardModelChanged();
|
roomScene.dashboardModelChanged();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
let model = getPhotoModel(uid);
|
let model = getPhotoModel(uid);
|
||||||
if (typeof(model) !== "undefined") {
|
if (typeof(model) !== "undefined") {
|
||||||
model[property_name] = value;
|
model[property_name] = value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
callbacks["ArrangeSeats"] = function(jsonData) {
|
callbacks["ArrangeSeats"] = function(jsonData) {
|
||||||
// jsonData: seat order
|
// jsonData: seat order
|
||||||
let order = JSON.parse(jsonData);
|
let order = JSON.parse(jsonData);
|
||||||
roomScene.isStarted = true;
|
roomScene.isStarted = true;
|
||||||
|
|
||||||
for (let i = 0; i < photoModel.count; i++) {
|
for (let i = 0; i < photoModel.count; i++) {
|
||||||
let item = photoModel.get(i);
|
let item = photoModel.get(i);
|
||||||
item.seatNumber = order.indexOf(item.id) + 1;
|
item.seatNumber = order.indexOf(item.id) + 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
dashboardModel.seatNumber = order.indexOf(Self.id) + 1;
|
dashboardModel.seatNumber = order.indexOf(Self.id) + 1;
|
||||||
roomScene.dashboardModelChanged();
|
roomScene.dashboardModelChanged();
|
||||||
|
|
||||||
// make Self to the first of list, then reorder photomodel
|
// make Self to the first of list, then reorder photomodel
|
||||||
let selfIndex = order.indexOf(Self.id);
|
let selfIndex = order.indexOf(Self.id);
|
||||||
let after = order.splice(selfIndex);
|
let after = order.splice(selfIndex);
|
||||||
after.push(...order);
|
after.push(...order);
|
||||||
let photoOrder = after.slice(1);
|
let photoOrder = after.slice(1);
|
||||||
|
|
||||||
for (let i = 0; i < photoModel.count; i++) {
|
for (let i = 0; i < photoModel.count; i++) {
|
||||||
let item = photoModel.get(i);
|
let item = photoModel.get(i);
|
||||||
item.index = photoOrder.indexOf(item.id);
|
item.index = photoOrder.indexOf(item.id);
|
||||||
}
|
}
|
||||||
|
|
||||||
arrangePhotos();
|
arrangePhotos();
|
||||||
}
|
}
|
||||||
|
|
||||||
function cancelAllFocus() {
|
function cancelAllFocus() {
|
||||||
let item;
|
let item;
|
||||||
for (let i = 0; i < playerNum - 1; i++) {
|
for (let i = 0; i < playerNum - 1; i++) {
|
||||||
item = photos.itemAt(i);
|
item = photos.itemAt(i);
|
||||||
item.progressBar.visible = false;
|
item.progressBar.visible = false;
|
||||||
item.progressTip = "";
|
item.progressTip = "";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
callbacks["MoveFocus"] = function(jsonData) {
|
callbacks["MoveFocus"] = function(jsonData) {
|
||||||
// jsonData: int[] focuses, string command
|
// jsonData: int[] focuses, string command
|
||||||
cancelAllFocus();
|
cancelAllFocus();
|
||||||
let data = JSON.parse(jsonData);
|
let data = JSON.parse(jsonData);
|
||||||
let focuses = data[0];
|
let focuses = data[0];
|
||||||
let command = data[1];
|
let command = data[1];
|
||||||
|
|
||||||
let item, model;
|
let item, model;
|
||||||
for (let i = 0; i < playerNum - 1; i++) {
|
for (let i = 0; i < playerNum - 1; i++) {
|
||||||
model = photoModel.get(i);
|
model = photoModel.get(i);
|
||||||
if (focuses.indexOf(model.id) != -1) {
|
if (focuses.indexOf(model.id) != -1) {
|
||||||
item = photos.itemAt(i);
|
item = photos.itemAt(i);
|
||||||
item.progressBar.visible = true;
|
item.progressBar.visible = true;
|
||||||
item.progressTip = command + " thinking...";
|
item.progressTip = command + " thinking...";
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
callbacks["PlayerRunned"] = function(jsonData) {
|
callbacks["PlayerRunned"] = function(jsonData) {
|
||||||
// jsonData: int runner, int robot
|
// jsonData: int runner, int robot
|
||||||
let data = JSON.parse(jsonData);
|
let data = JSON.parse(jsonData);
|
||||||
let runner = data[0];
|
let runner = data[0];
|
||||||
let robot = data[1];
|
let robot = data[1];
|
||||||
|
|
||||||
let model = getPhotoModel(runner);
|
let model = getPhotoModel(runner);
|
||||||
if (typeof(model) !== "undefined") {
|
if (typeof(model) !== "undefined") {
|
||||||
model.id = robot;
|
model.id = robot;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
callbacks["AskForGeneral"] = function(jsonData) {
|
callbacks["AskForGeneral"] = function(jsonData) {
|
||||||
// jsonData: string[] Generals
|
// jsonData: string[] Generals
|
||||||
// TODO: choose multiple generals
|
// TODO: choose multiple generals
|
||||||
let data = JSON.parse(jsonData);
|
let data = JSON.parse(jsonData);
|
||||||
roomScene.promptText = "Please choose 1 general";
|
roomScene.promptText = "Please choose 1 general";
|
||||||
roomScene.state = "replying";
|
roomScene.state = "replying";
|
||||||
roomScene.popupBox.source = "RoomElement/ChooseGeneralBox.qml";
|
roomScene.popupBox.source = "RoomElement/ChooseGeneralBox.qml";
|
||||||
let box = roomScene.popupBox.item;
|
let box = roomScene.popupBox.item;
|
||||||
box.choiceNum = 1;
|
box.choiceNum = 1;
|
||||||
box.accepted.connect(() => {
|
box.accepted.connect(() => {
|
||||||
replyToServer(JSON.stringify([box.choices[0]]));
|
replyToServer(JSON.stringify([box.choices[0]]));
|
||||||
});
|
});
|
||||||
for (let i = 0; i < data.length; i++)
|
for (let i = 0; i < data.length; i++)
|
||||||
box.generalList.append({ "name": data[i] });
|
box.generalList.append({ "name": data[i] });
|
||||||
box.updatePosition();
|
box.updatePosition();
|
||||||
}
|
}
|
||||||
|
|
||||||
callbacks["AskForSkillInvoke"] = function(jsonData) {
|
callbacks["AskForSkillInvoke"] = function(jsonData) {
|
||||||
// jsonData: string name
|
// jsonData: string name
|
||||||
roomScene.promptText = "Do you want to invoke '" + jsonData + "' ?";
|
roomScene.promptText = "Do you want to invoke '" + jsonData + "' ?";
|
||||||
roomScene.state = "responding";
|
roomScene.state = "responding";
|
||||||
}
|
}
|
||||||
|
|
||||||
callbacks["AskForChoice"] = function(jsonData) {
|
callbacks["AskForChoice"] = function(jsonData) {
|
||||||
// jsonData: [ string[] choices, string skill ]
|
// jsonData: [ string[] choices, string skill ]
|
||||||
// TODO: multiple choices, e.g. benxi_ol
|
// TODO: multiple choices, e.g. benxi_ol
|
||||||
let data = JSON.parse(jsonData);
|
let data = JSON.parse(jsonData);
|
||||||
let choices = data[0];
|
let choices = data[0];
|
||||||
let skill_name = data[1];
|
let skill_name = data[1];
|
||||||
roomScene.promptText = skill_name + ": Please make choice";
|
roomScene.promptText = skill_name + ": Please make choice";
|
||||||
roomScene.state = "replying";
|
roomScene.state = "replying";
|
||||||
roomScene.popupBox.source = "RoomElement/ChoiceBox.qml";
|
roomScene.popupBox.source = "RoomElement/ChoiceBox.qml";
|
||||||
let box = roomScene.popupBox.item;
|
let box = roomScene.popupBox.item;
|
||||||
box.options = choices;
|
box.options = choices;
|
||||||
box.skill_name = skill_name;
|
box.skill_name = skill_name;
|
||||||
box.accepted.connect(() => {
|
box.accepted.connect(() => {
|
||||||
replyToServer(choices[box.result]);
|
replyToServer(choices[box.result]);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
callbacks["MoveCards"] = function(jsonData) {
|
callbacks["MoveCards"] = function(jsonData) {
|
||||||
// jsonData: merged moves
|
// jsonData: merged moves
|
||||||
let moves = JSON.parse(jsonData);
|
let moves = JSON.parse(jsonData);
|
||||||
moveCards(moves);
|
moveCards(moves);
|
||||||
|
}
|
||||||
|
|
||||||
|
callbacks["PlayCard"] = function(jsonData) {
|
||||||
|
// jsonData: int playerId
|
||||||
|
let playerId = parseInt(jsonData);
|
||||||
|
if (playerId == Self.id) {
|
||||||
|
roomScene.promptText = "Please use a card";
|
||||||
|
roomScene.state = "playing";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,56 +1,56 @@
|
||||||
import QtQuick 2.15
|
import QtQuick 2.15
|
||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
function show(text, duration) {
|
function show(text, duration) {
|
||||||
message.text = text;
|
message.text = text;
|
||||||
time = Math.max(duration, 2 * fadeTime);
|
time = Math.max(duration, 2 * fadeTime);
|
||||||
animation.start();
|
animation.start();
|
||||||
|
}
|
||||||
|
|
||||||
|
id: root
|
||||||
|
|
||||||
|
readonly property real defaultTime: 3000
|
||||||
|
property real time: defaultTime
|
||||||
|
readonly property real fadeTime: 300
|
||||||
|
|
||||||
|
anchors.horizontalCenter: parent != null ? parent.horizontalCenter : undefined
|
||||||
|
height: message.height + 20
|
||||||
|
width: message.width + 40
|
||||||
|
radius: 16
|
||||||
|
|
||||||
|
opacity: 0
|
||||||
|
color: "#F2808A87"
|
||||||
|
|
||||||
|
Text {
|
||||||
|
id: message
|
||||||
|
color: "white"
|
||||||
|
horizontalAlignment: Text.AlignHCenter
|
||||||
|
anchors.centerIn: parent
|
||||||
|
}
|
||||||
|
|
||||||
|
SequentialAnimation on opacity {
|
||||||
|
id: animation
|
||||||
|
running: false
|
||||||
|
|
||||||
|
|
||||||
|
NumberAnimation {
|
||||||
|
to: .9
|
||||||
|
duration: fadeTime
|
||||||
}
|
}
|
||||||
|
|
||||||
id: root
|
PauseAnimation {
|
||||||
|
duration: time - 2 * fadeTime
|
||||||
readonly property real defaultTime: 3000
|
|
||||||
property real time: defaultTime
|
|
||||||
readonly property real fadeTime: 300
|
|
||||||
|
|
||||||
anchors.horizontalCenter: parent != null ? parent.horizontalCenter : undefined
|
|
||||||
height: message.height + 20
|
|
||||||
width: message.width + 40
|
|
||||||
radius: 16
|
|
||||||
|
|
||||||
opacity: 0
|
|
||||||
color: "#F2808A87"
|
|
||||||
|
|
||||||
Text {
|
|
||||||
id: message
|
|
||||||
color: "white"
|
|
||||||
horizontalAlignment: Text.AlignHCenter
|
|
||||||
anchors.centerIn: parent
|
|
||||||
}
|
}
|
||||||
|
|
||||||
SequentialAnimation on opacity {
|
NumberAnimation {
|
||||||
id: animation
|
to: 0
|
||||||
running: false
|
duration: fadeTime
|
||||||
|
|
||||||
|
|
||||||
NumberAnimation {
|
|
||||||
to: .9
|
|
||||||
duration: fadeTime
|
|
||||||
}
|
|
||||||
|
|
||||||
PauseAnimation {
|
|
||||||
duration: time - 2 * fadeTime
|
|
||||||
}
|
|
||||||
|
|
||||||
NumberAnimation {
|
|
||||||
to: 0
|
|
||||||
duration: fadeTime
|
|
||||||
}
|
|
||||||
|
|
||||||
onRunningChanged: {
|
|
||||||
if (!running) {
|
|
||||||
toast.model.remove(index);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
onRunningChanged: {
|
||||||
|
if (!running) {
|
||||||
|
toast.model.remove(index);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,35 +3,35 @@ import QtQuick 2.15
|
||||||
// copy from https://gist.github.com/jonmcclung/bae669101d17b103e94790341301c129
|
// copy from https://gist.github.com/jonmcclung/bae669101d17b103e94790341301c129
|
||||||
// and modified some code
|
// and modified some code
|
||||||
ListView {
|
ListView {
|
||||||
function show(text, duration) {
|
function show(text, duration) {
|
||||||
if (duration === undefined) {
|
if (duration === undefined) {
|
||||||
duration = 3000;
|
duration = 3000;
|
||||||
}
|
|
||||||
model.insert(0, {text: text, duration: duration});
|
|
||||||
}
|
}
|
||||||
|
model.insert(0, {text: text, duration: duration});
|
||||||
|
}
|
||||||
|
|
||||||
id: root
|
id: root
|
||||||
|
|
||||||
z: Infinity
|
z: Infinity
|
||||||
spacing: 5
|
spacing: 5
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
anchors.bottomMargin: 10
|
anchors.bottomMargin: 10
|
||||||
verticalLayoutDirection: ListView.BottomToTop
|
verticalLayoutDirection: ListView.BottomToTop
|
||||||
|
|
||||||
interactive: false
|
interactive: false
|
||||||
|
|
||||||
displaced: Transition {
|
displaced: Transition {
|
||||||
NumberAnimation {
|
NumberAnimation {
|
||||||
properties: "y"
|
properties: "y"
|
||||||
easing.type: Easing.InOutQuad
|
easing.type: Easing.InOutQuad
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
delegate: Toast {
|
|
||||||
Component.onCompleted: {
|
delegate: Toast {
|
||||||
show(text, duration);
|
Component.onCompleted: {
|
||||||
}
|
show(text, duration);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
model: ListModel {id: model}
|
model: ListModel {id: model}
|
||||||
}
|
}
|
||||||
|
|
198
qml/main.qml
198
qml/main.qml
|
@ -5,116 +5,116 @@ import "Logic.js" as Logic
|
||||||
import "Pages"
|
import "Pages"
|
||||||
|
|
||||||
Window {
|
Window {
|
||||||
id: mainWindow
|
id: mainWindow
|
||||||
visible: true
|
visible: true
|
||||||
width: 720
|
width: 720
|
||||||
height: 480
|
height: 480
|
||||||
property var callbacks: Logic.callbacks
|
property var callbacks: Logic.callbacks
|
||||||
|
|
||||||
StackView {
|
StackView {
|
||||||
id: mainStack
|
id: mainStack
|
||||||
visible: !mainWindow.busy
|
visible: !mainWindow.busy
|
||||||
initialItem: init
|
initialItem: init
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
|
}
|
||||||
|
|
||||||
|
Component { id: init; Init {} }
|
||||||
|
Component { id: lobby; Lobby {} }
|
||||||
|
Component { id: generalsOverview; GeneralsOverview {} }
|
||||||
|
Component { id: cardsOverview; CardsOverview {} }
|
||||||
|
Component { id: room; Room {} }
|
||||||
|
|
||||||
|
property bool busy: false
|
||||||
|
BusyIndicator {
|
||||||
|
running: true
|
||||||
|
anchors.centerIn: parent
|
||||||
|
visible: mainWindow.busy === true
|
||||||
|
}
|
||||||
|
|
||||||
|
Config {
|
||||||
|
id: config
|
||||||
|
}
|
||||||
|
|
||||||
|
// global popup. it is modal and just lower than toast
|
||||||
|
Rectangle {
|
||||||
|
id: globalPopupDim
|
||||||
|
anchors.fill: parent
|
||||||
|
color: "black"
|
||||||
|
opacity: 0
|
||||||
|
visible: !mainWindow.busy
|
||||||
|
|
||||||
|
property bool stateVisible: false
|
||||||
|
states: [
|
||||||
|
State {
|
||||||
|
when: globalPopupDim.stateVisible
|
||||||
|
PropertyChanges { target: globalPopupDim; opacity: 0.5 }
|
||||||
|
},
|
||||||
|
State {
|
||||||
|
when: !globalPopupDim.stateVisible
|
||||||
|
PropertyChanges { target: globalPopupDim; opacity: 0.0 }
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
transitions: Transition {
|
||||||
|
NumberAnimation { properties: "opacity"; easing.type: Easing.InOutQuad }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Popup {
|
||||||
|
id: globalPopup
|
||||||
|
property string source: ""
|
||||||
|
modal: true
|
||||||
|
dim: false // cannot animate the dim
|
||||||
|
focus: true
|
||||||
|
opacity: mainWindow.busy ? 0 : 1
|
||||||
|
closePolicy: Popup.CloseOnEscape
|
||||||
|
anchors.centerIn: parent
|
||||||
|
|
||||||
|
onAboutToShow: {
|
||||||
|
globalPopupDim.stateVisible = true
|
||||||
}
|
}
|
||||||
|
|
||||||
Component { id: init; Init {} }
|
enter: Transition {
|
||||||
Component { id: lobby; Lobby {} }
|
NumberAnimation { properties: "opacity"; from: 0; to: 1 }
|
||||||
Component { id: generalsOverview; GeneralsOverview {} }
|
NumberAnimation { properties: "scale"; from: 0.4; to: 1 }
|
||||||
Component { id: cardsOverview; CardsOverview {} }
|
|
||||||
Component { id: room; Room {} }
|
|
||||||
|
|
||||||
property bool busy: false
|
|
||||||
BusyIndicator {
|
|
||||||
running: true
|
|
||||||
anchors.centerIn: parent
|
|
||||||
visible: mainWindow.busy === true
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Config {
|
onAboutToHide: {
|
||||||
id: config
|
globalPopupDim.stateVisible = false
|
||||||
}
|
}
|
||||||
|
|
||||||
// global popup. it is modal and just lower than toast
|
exit: Transition {
|
||||||
Rectangle {
|
NumberAnimation { properties: "opacity"; from: 1; to: 0 }
|
||||||
id: globalPopupDim
|
NumberAnimation { properties: "scale"; from: 1; to: 0.4 }
|
||||||
anchors.fill: parent
|
|
||||||
color: "black"
|
|
||||||
opacity: 0
|
|
||||||
visible: !mainWindow.busy
|
|
||||||
|
|
||||||
property bool stateVisible: false
|
|
||||||
states: [
|
|
||||||
State {
|
|
||||||
when: globalPopupDim.stateVisible
|
|
||||||
PropertyChanges { target: globalPopupDim; opacity: 0.5 }
|
|
||||||
},
|
|
||||||
State {
|
|
||||||
when: !globalPopupDim.stateVisible
|
|
||||||
PropertyChanges { target: globalPopupDim; opacity: 0.0 }
|
|
||||||
}
|
|
||||||
]
|
|
||||||
|
|
||||||
transitions: Transition {
|
|
||||||
NumberAnimation { properties: "opacity"; easing.type: Easing.InOutQuad }
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Popup {
|
Loader {
|
||||||
id: globalPopup
|
visible: !mainWindow.busy
|
||||||
property string source: ""
|
source: globalPopup.source === "" ? "" : "GlobalPopups/" + globalPopup.source
|
||||||
modal: true
|
onSourceChanged: {
|
||||||
dim: false // cannot animate the dim
|
if (item === null)
|
||||||
focus: true
|
return;
|
||||||
opacity: mainWindow.busy ? 0 : 1
|
item.finished.connect(() => {
|
||||||
closePolicy: Popup.CloseOnEscape
|
globalPopup.close();
|
||||||
anchors.centerIn: parent
|
globalPopup.source = "";
|
||||||
|
});
|
||||||
onAboutToShow: {
|
}
|
||||||
globalPopupDim.stateVisible = true
|
|
||||||
}
|
|
||||||
|
|
||||||
enter: Transition {
|
|
||||||
NumberAnimation { properties: "opacity"; from: 0; to: 1 }
|
|
||||||
NumberAnimation { properties: "scale"; from: 0.4; to: 1 }
|
|
||||||
}
|
|
||||||
|
|
||||||
onAboutToHide: {
|
|
||||||
globalPopupDim.stateVisible = false
|
|
||||||
}
|
|
||||||
|
|
||||||
exit: Transition {
|
|
||||||
NumberAnimation { properties: "opacity"; from: 1; to: 0 }
|
|
||||||
NumberAnimation { properties: "scale"; from: 1; to: 0.4 }
|
|
||||||
}
|
|
||||||
|
|
||||||
Loader {
|
|
||||||
visible: !mainWindow.busy
|
|
||||||
source: globalPopup.source === "" ? "" : "GlobalPopups/" + globalPopup.source
|
|
||||||
onSourceChanged: {
|
|
||||||
if (item === null)
|
|
||||||
return;
|
|
||||||
item.finished.connect(() => {
|
|
||||||
globalPopup.close();
|
|
||||||
globalPopup.source = "";
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
ToastManager {
|
ToastManager {
|
||||||
id: toast
|
id: toast
|
||||||
}
|
}
|
||||||
|
|
||||||
Connections {
|
Connections {
|
||||||
target: Backend
|
target: Backend
|
||||||
function onNotifyUI(command, jsonData) {
|
function onNotifyUI(command, jsonData) {
|
||||||
let cb = callbacks[command]
|
let cb = callbacks[command]
|
||||||
if (typeof(cb) === "function") {
|
if (typeof(cb) === "function") {
|
||||||
cb(jsonData);
|
cb(jsonData);
|
||||||
} else {
|
} else {
|
||||||
callbacks["ErrorMsg"]("Unknown command " + command + "!");
|
callbacks["ErrorMsg"]("Unknown command " + command + "!");
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
22
qml/util.js
22
qml/util.js
|
@ -1,21 +1,21 @@
|
||||||
.pragma library
|
.pragma library
|
||||||
|
|
||||||
function convertNumber(number) {
|
function convertNumber(number) {
|
||||||
if (number === 1)
|
if (number === 1)
|
||||||
return "A";
|
return "A";
|
||||||
if (number >= 2 && number <= 10)
|
if (number >= 2 && number <= 10)
|
||||||
return number;
|
return number;
|
||||||
if (number >= 11 && number <= 13) {
|
if (number >= 11 && number <= 13) {
|
||||||
const strs = ["J", "Q", "K"];
|
const strs = ["J", "Q", "K"];
|
||||||
return strs[number - 11];
|
return strs[number - 11];
|
||||||
}
|
}
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
|
|
||||||
Array.prototype.contains = function(element) {
|
Array.prototype.contains = function(element) {
|
||||||
return this.indexOf(element) != -1;
|
return this.indexOf(element) != -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
Array.prototype.prepend = function() {
|
Array.prototype.prepend = function() {
|
||||||
this.splice(0, 0, ...arguments);
|
this.splice(0, 0, ...arguments);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,12 +1,12 @@
|
||||||
CREATE TABLE userinfo (
|
CREATE TABLE userinfo (
|
||||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||||
name VARCHAR(255),
|
name VARCHAR(255),
|
||||||
password CHAR(64),
|
password CHAR(64),
|
||||||
avatar VARCHAR(64),
|
avatar VARCHAR(64),
|
||||||
lastLoginIp VARCHAR(64),
|
lastLoginIp VARCHAR(64),
|
||||||
banned BOOLEAN
|
banned BOOLEAN
|
||||||
);
|
);
|
||||||
|
|
||||||
CREATE TABLE banip (
|
CREATE TABLE banip (
|
||||||
ip VARCHAR(64)
|
ip VARCHAR(64)
|
||||||
);
|
);
|
||||||
|
|
|
@ -1,39 +1,39 @@
|
||||||
set(freekill_SRCS
|
set(freekill_SRCS
|
||||||
"main.cpp"
|
"main.cpp"
|
||||||
"core/player.cpp"
|
"core/player.cpp"
|
||||||
"core/util.cpp"
|
"core/util.cpp"
|
||||||
"network/server_socket.cpp"
|
"network/server_socket.cpp"
|
||||||
"network/client_socket.cpp"
|
"network/client_socket.cpp"
|
||||||
"network/router.cpp"
|
"network/router.cpp"
|
||||||
"server/server.cpp"
|
"server/server.cpp"
|
||||||
"server/serverplayer.cpp"
|
"server/serverplayer.cpp"
|
||||||
"server/room.cpp"
|
"server/room.cpp"
|
||||||
"client/client.cpp"
|
"client/client.cpp"
|
||||||
"client/clientplayer.cpp"
|
"client/clientplayer.cpp"
|
||||||
"ui/qmlbackend.cpp"
|
"ui/qmlbackend.cpp"
|
||||||
"swig/freekill-wrap.cxx"
|
"swig/freekill-wrap.cxx"
|
||||||
)
|
)
|
||||||
|
|
||||||
set(freekill_HEADERS
|
set(freekill_HEADERS
|
||||||
"core/util.h"
|
"core/util.h"
|
||||||
"core/player.h"
|
"core/player.h"
|
||||||
"network/server_socket.h"
|
"network/server_socket.h"
|
||||||
"network/client_socket.h"
|
"network/client_socket.h"
|
||||||
"network/router.h"
|
"network/router.h"
|
||||||
"server/server.h"
|
"server/server.h"
|
||||||
"server/serverplayer.h"
|
"server/serverplayer.h"
|
||||||
"server/room.h"
|
"server/room.h"
|
||||||
"client/client.h"
|
"client/client.h"
|
||||||
"client/clientplayer.h"
|
"client/clientplayer.h"
|
||||||
"ui/qmlbackend.h"
|
"ui/qmlbackend.h"
|
||||||
)
|
)
|
||||||
|
|
||||||
if (WIN32)
|
if (WIN32)
|
||||||
set(LUA_LIB ${PROJECT_SOURCE_DIR}/lib/win/lua54.dll)
|
set(LUA_LIB ${PROJECT_SOURCE_DIR}/lib/win/lua54.dll)
|
||||||
set(SQLITE3_LIB ${PROJECT_SOURCE_DIR}/lib/win/sqlite3.dll)
|
set(SQLITE3_LIB ${PROJECT_SOURCE_DIR}/lib/win/sqlite3.dll)
|
||||||
else ()
|
else ()
|
||||||
set(LUA_LIB lua5.4)
|
set(LUA_LIB lua5.4)
|
||||||
set(SQLITE3_LIB sqlite3)
|
set(SQLITE3_LIB sqlite3)
|
||||||
endif ()
|
endif ()
|
||||||
|
|
||||||
source_group("Include" FILES ${freekill_HEADERS})
|
source_group("Include" FILES ${freekill_HEADERS})
|
||||||
|
@ -42,10 +42,10 @@ target_precompile_headers(FreeKill PRIVATE "pch.h")
|
||||||
target_link_libraries(FreeKill ${LUA_LIB} ${SQLITE3_LIB} Qt5::Qml Qt5::Gui Qt5::Network Qt5::Multimedia)
|
target_link_libraries(FreeKill ${LUA_LIB} ${SQLITE3_LIB} Qt5::Qml Qt5::Gui Qt5::Network Qt5::Multimedia)
|
||||||
file(GLOB SWIG_FILES "${PROJECT_SOURCE_DIR}/src/swig/*.i")
|
file(GLOB SWIG_FILES "${PROJECT_SOURCE_DIR}/src/swig/*.i")
|
||||||
add_custom_command(
|
add_custom_command(
|
||||||
OUTPUT ${PROJECT_SOURCE_DIR}/src/swig/freekill-wrap.cxx
|
OUTPUT ${PROJECT_SOURCE_DIR}/src/swig/freekill-wrap.cxx
|
||||||
DEPENDS ${SWIG_FILES}
|
DEPENDS ${SWIG_FILES}
|
||||||
COMMENT "Generating freekill-wrap.cxx"
|
COMMENT "Generating freekill-wrap.cxx"
|
||||||
COMMAND swig -c++ -lua -Wall -o
|
COMMAND swig -c++ -lua -Wall -o
|
||||||
${PROJECT_SOURCE_DIR}/src/swig/freekill-wrap.cxx
|
${PROJECT_SOURCE_DIR}/src/swig/freekill-wrap.cxx
|
||||||
${PROJECT_SOURCE_DIR}/src/swig/freekill.i
|
${PROJECT_SOURCE_DIR}/src/swig/freekill.i
|
||||||
)
|
)
|
||||||
|
|
|
@ -7,67 +7,67 @@ Client *ClientInstance;
|
||||||
ClientPlayer *Self;
|
ClientPlayer *Self;
|
||||||
|
|
||||||
Client::Client(QObject* parent)
|
Client::Client(QObject* parent)
|
||||||
: QObject(parent), callback(0)
|
: QObject(parent), callback(0)
|
||||||
{
|
{
|
||||||
ClientInstance = this;
|
ClientInstance = this;
|
||||||
Self = new ClientPlayer(0, this);
|
Self = new ClientPlayer(0, this);
|
||||||
QQmlApplicationEngine *engine = Backend->getEngine();
|
QQmlApplicationEngine *engine = Backend->getEngine();
|
||||||
engine->rootContext()->setContextProperty("ClientInstance", ClientInstance);
|
engine->rootContext()->setContextProperty("ClientInstance", ClientInstance);
|
||||||
engine->rootContext()->setContextProperty("Self", Self);
|
engine->rootContext()->setContextProperty("Self", Self);
|
||||||
|
|
||||||
ClientSocket *socket = new ClientSocket;
|
ClientSocket *socket = new ClientSocket;
|
||||||
connect(socket, &ClientSocket::error_message, this, &Client::error_message);
|
connect(socket, &ClientSocket::error_message, this, &Client::error_message);
|
||||||
router = new Router(this, socket, Router::TYPE_CLIENT);
|
router = new Router(this, socket, Router::TYPE_CLIENT);
|
||||||
|
|
||||||
L = CreateLuaState();
|
L = CreateLuaState();
|
||||||
DoLuaScript(L, "lua/freekill.lua");
|
DoLuaScript(L, "lua/freekill.lua");
|
||||||
DoLuaScript(L, "lua/client/client.lua");
|
DoLuaScript(L, "lua/client/client.lua");
|
||||||
}
|
}
|
||||||
|
|
||||||
Client::~Client()
|
Client::~Client()
|
||||||
{
|
{
|
||||||
ClientInstance = nullptr;
|
ClientInstance = nullptr;
|
||||||
lua_close(L);
|
lua_close(L);
|
||||||
router->getSocket()->disconnectFromHost();
|
router->getSocket()->disconnectFromHost();
|
||||||
router->getSocket()->deleteLater();
|
router->getSocket()->deleteLater();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Client::connectToHost(const QHostAddress& server, ushort port)
|
void Client::connectToHost(const QHostAddress& server, ushort port)
|
||||||
{
|
{
|
||||||
router->getSocket()->connectToHost(server, port);
|
router->getSocket()->connectToHost(server, port);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Client::replyToServer(const QString& command, const QString& jsonData)
|
void Client::replyToServer(const QString& command, const QString& jsonData)
|
||||||
{
|
{
|
||||||
int type = Router::TYPE_REPLY | Router::SRC_CLIENT | Router::DEST_SERVER;
|
int type = Router::TYPE_REPLY | Router::SRC_CLIENT | Router::DEST_SERVER;
|
||||||
router->reply(type, command, jsonData);
|
router->reply(type, command, jsonData);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Client::notifyServer(const QString& command, const QString& jsonData)
|
void Client::notifyServer(const QString& command, const QString& jsonData)
|
||||||
{
|
{
|
||||||
int type = Router::TYPE_NOTIFICATION | Router::SRC_CLIENT | Router::DEST_SERVER;
|
int type = Router::TYPE_NOTIFICATION | Router::SRC_CLIENT | Router::DEST_SERVER;
|
||||||
router->notify(type, command, jsonData);
|
router->notify(type, command, jsonData);
|
||||||
}
|
}
|
||||||
|
|
||||||
ClientPlayer *Client::addPlayer(int id, const QString &name, const QString &avatar) {
|
ClientPlayer *Client::addPlayer(int id, const QString &name, const QString &avatar) {
|
||||||
ClientPlayer *player = new ClientPlayer(id);
|
ClientPlayer *player = new ClientPlayer(id);
|
||||||
player->setScreenName(name);
|
player->setScreenName(name);
|
||||||
player->setAvatar(avatar);
|
player->setAvatar(avatar);
|
||||||
|
|
||||||
players[id] = player;
|
players[id] = player;
|
||||||
return player;
|
return player;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Client::removePlayer(int id) {
|
void Client::removePlayer(int id) {
|
||||||
ClientPlayer *p = players[id];
|
ClientPlayer *p = players[id];
|
||||||
p->deleteLater();
|
p->deleteLater();
|
||||||
players[id] = nullptr;
|
players[id] = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Client::clearPlayers() {
|
void Client::clearPlayers() {
|
||||||
players.clear();
|
players.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
lua_State *Client::getLuaState() {
|
lua_State *Client::getLuaState() {
|
||||||
return L;
|
return L;
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,33 +6,33 @@
|
||||||
#include "qmlbackend.h"
|
#include "qmlbackend.h"
|
||||||
|
|
||||||
class Client : public QObject {
|
class Client : public QObject {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
public:
|
public:
|
||||||
Client(QObject *parent = nullptr);
|
Client(QObject *parent = nullptr);
|
||||||
~Client();
|
~Client();
|
||||||
|
|
||||||
void connectToHost(const QHostAddress &server, ushort port);
|
void connectToHost(const QHostAddress &server, ushort port);
|
||||||
|
|
||||||
Q_INVOKABLE void replyToServer(const QString &command, const QString &jsonData);
|
Q_INVOKABLE void replyToServer(const QString &command, const QString &jsonData);
|
||||||
Q_INVOKABLE void notifyServer(const QString &command, const QString &jsonData);
|
Q_INVOKABLE void notifyServer(const QString &command, const QString &jsonData);
|
||||||
|
|
||||||
Q_INVOKABLE void callLua(const QString &command, const QString &jsonData);
|
Q_INVOKABLE void callLua(const QString &command, const QString &jsonData);
|
||||||
LuaFunction callback;
|
LuaFunction callback;
|
||||||
|
|
||||||
ClientPlayer *addPlayer(int id, const QString &name, const QString &avatar);
|
ClientPlayer *addPlayer(int id, const QString &name, const QString &avatar);
|
||||||
void removePlayer(int id);
|
void removePlayer(int id);
|
||||||
Q_INVOKABLE void clearPlayers();
|
Q_INVOKABLE void clearPlayers();
|
||||||
|
|
||||||
lua_State *getLuaState();
|
lua_State *getLuaState();
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void error_message(const QString &msg);
|
void error_message(const QString &msg);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Router *router;
|
Router *router;
|
||||||
QMap<int, ClientPlayer *> players;
|
QMap<int, ClientPlayer *> players;
|
||||||
|
|
||||||
lua_State *L;
|
lua_State *L;
|
||||||
};
|
};
|
||||||
|
|
||||||
extern Client *ClientInstance;
|
extern Client *ClientInstance;
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
#include "clientplayer.h"
|
#include "clientplayer.h"
|
||||||
|
|
||||||
ClientPlayer::ClientPlayer(int id, QObject* parent)
|
ClientPlayer::ClientPlayer(int id, QObject* parent)
|
||||||
: Player(parent)
|
: Player(parent)
|
||||||
{
|
{
|
||||||
setId(id);
|
setId(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
ClientPlayer::~ClientPlayer()
|
ClientPlayer::~ClientPlayer()
|
||||||
|
|
|
@ -4,23 +4,23 @@
|
||||||
#include "player.h"
|
#include "player.h"
|
||||||
|
|
||||||
class ClientPlayer : public Player {
|
class ClientPlayer : public Player {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
Q_PROPERTY(int id READ getId CONSTANT)
|
Q_PROPERTY(int id READ getId CONSTANT)
|
||||||
Q_PROPERTY(QString screenName
|
Q_PROPERTY(QString screenName
|
||||||
READ getScreenName
|
READ getScreenName
|
||||||
WRITE setScreenName
|
WRITE setScreenName
|
||||||
NOTIFY screenNameChanged
|
NOTIFY screenNameChanged
|
||||||
)
|
)
|
||||||
Q_PROPERTY(QString avatar
|
Q_PROPERTY(QString avatar
|
||||||
READ getAvatar
|
READ getAvatar
|
||||||
WRITE setAvatar
|
WRITE setAvatar
|
||||||
NOTIFY avatarChanged
|
NOTIFY avatarChanged
|
||||||
)
|
)
|
||||||
|
|
||||||
public:
|
public:
|
||||||
ClientPlayer(int id, QObject *parent = nullptr);
|
ClientPlayer(int id, QObject *parent = nullptr);
|
||||||
~ClientPlayer();
|
~ClientPlayer();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
#include "player.h"
|
#include "player.h"
|
||||||
|
|
||||||
Player::Player(QObject* parent)
|
Player::Player(QObject* parent)
|
||||||
: QObject(parent)
|
: QObject(parent)
|
||||||
, id(0)
|
, id(0)
|
||||||
, state(Player::Invalid)
|
, state(Player::Invalid)
|
||||||
, ready(false)
|
, ready(false)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -14,85 +14,85 @@ Player::~Player()
|
||||||
|
|
||||||
int Player::getId() const
|
int Player::getId() const
|
||||||
{
|
{
|
||||||
return id;
|
return id;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Player::setId(int id)
|
void Player::setId(int id)
|
||||||
{
|
{
|
||||||
this->id = id;
|
this->id = id;
|
||||||
}
|
}
|
||||||
|
|
||||||
QString Player::getScreenName() const
|
QString Player::getScreenName() const
|
||||||
{
|
{
|
||||||
return screenName;
|
return screenName;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Player::setScreenName(const QString& name)
|
void Player::setScreenName(const QString& name)
|
||||||
{
|
{
|
||||||
this->screenName = name;
|
this->screenName = name;
|
||||||
emit screenNameChanged();
|
emit screenNameChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
QString Player::getAvatar() const
|
QString Player::getAvatar() const
|
||||||
{
|
{
|
||||||
return avatar;
|
return avatar;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Player::setAvatar(const QString& avatar)
|
void Player::setAvatar(const QString& avatar)
|
||||||
{
|
{
|
||||||
this->avatar = avatar;
|
this->avatar = avatar;
|
||||||
emit avatarChanged();
|
emit avatarChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
Player::State Player::getState() const
|
Player::State Player::getState() const
|
||||||
{
|
{
|
||||||
return state;
|
return state;
|
||||||
}
|
}
|
||||||
|
|
||||||
QString Player::getStateString() const
|
QString Player::getStateString() const
|
||||||
{
|
{
|
||||||
switch (state) {
|
switch (state) {
|
||||||
case Online:
|
case Online:
|
||||||
return QStringLiteral("online");
|
return QStringLiteral("online");
|
||||||
case Trust:
|
case Trust:
|
||||||
return QStringLiteral("trust");
|
return QStringLiteral("trust");
|
||||||
case Robot:
|
case Robot:
|
||||||
return QStringLiteral("robot");
|
return QStringLiteral("robot");
|
||||||
case Offline:
|
case Offline:
|
||||||
return QStringLiteral("offline");
|
return QStringLiteral("offline");
|
||||||
default:
|
default:
|
||||||
return QStringLiteral("invalid");
|
return QStringLiteral("invalid");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Player::setState(Player::State state)
|
void Player::setState(Player::State state)
|
||||||
{
|
{
|
||||||
this->state = state;
|
this->state = state;
|
||||||
emit stateChanged();
|
emit stateChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Player::setStateString(const QString &state)
|
void Player::setStateString(const QString &state)
|
||||||
{
|
{
|
||||||
if (state == QStringLiteral("online"))
|
if (state == QStringLiteral("online"))
|
||||||
setState(Online);
|
setState(Online);
|
||||||
else if (state == QStringLiteral("trust"))
|
else if (state == QStringLiteral("trust"))
|
||||||
setState(Trust);
|
setState(Trust);
|
||||||
else if (state == QStringLiteral("robot"))
|
else if (state == QStringLiteral("robot"))
|
||||||
setState(Robot);
|
setState(Robot);
|
||||||
else if (state == QStringLiteral("offline"))
|
else if (state == QStringLiteral("offline"))
|
||||||
setState(Offline);
|
setState(Offline);
|
||||||
else
|
else
|
||||||
setState(Invalid);
|
setState(Invalid);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Player::isReady() const
|
bool Player::isReady() const
|
||||||
{
|
{
|
||||||
return ready;
|
return ready;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Player::setReady(bool ready)
|
void Player::setReady(bool ready)
|
||||||
{
|
{
|
||||||
this->ready = ready;
|
this->ready = ready;
|
||||||
emit readyChanged();
|
emit readyChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -4,49 +4,49 @@
|
||||||
// Common part of ServerPlayer and ClientPlayer
|
// Common part of ServerPlayer and ClientPlayer
|
||||||
// dont initialize it directly
|
// dont initialize it directly
|
||||||
class Player : public QObject {
|
class Player : public QObject {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
public:
|
public:
|
||||||
enum State{
|
enum State{
|
||||||
Invalid,
|
Invalid,
|
||||||
Online,
|
Online,
|
||||||
Trust, // Trust or run
|
Trust, // Trust or run
|
||||||
Robot, // only for real robot
|
Robot, // only for real robot
|
||||||
Offline
|
Offline
|
||||||
};
|
};
|
||||||
|
|
||||||
explicit Player(QObject *parent = nullptr);
|
explicit Player(QObject *parent = nullptr);
|
||||||
~Player();
|
~Player();
|
||||||
|
|
||||||
int getId() const;
|
int getId() const;
|
||||||
void setId(int id);
|
void setId(int id);
|
||||||
|
|
||||||
QString getScreenName() const;
|
QString getScreenName() const;
|
||||||
void setScreenName(const QString &name);
|
void setScreenName(const QString &name);
|
||||||
|
|
||||||
QString getAvatar() const;
|
QString getAvatar() const;
|
||||||
void setAvatar(const QString &avatar);
|
void setAvatar(const QString &avatar);
|
||||||
|
|
||||||
State getState() const;
|
State getState() const;
|
||||||
QString getStateString() const;
|
QString getStateString() const;
|
||||||
void setState(State state);
|
void setState(State state);
|
||||||
void setStateString(const QString &state);
|
void setStateString(const QString &state);
|
||||||
|
|
||||||
bool isReady() const;
|
bool isReady() const;
|
||||||
void setReady(bool ready);
|
void setReady(bool ready);
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void screenNameChanged();
|
void screenNameChanged();
|
||||||
void avatarChanged();
|
void avatarChanged();
|
||||||
void stateChanged();
|
void stateChanged();
|
||||||
void readyChanged();
|
void readyChanged();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
int id;
|
int id;
|
||||||
QString screenName; // screenName should not be same.
|
QString screenName; // screenName should not be same.
|
||||||
QString avatar;
|
QString avatar;
|
||||||
State state;
|
State state;
|
||||||
bool ready;
|
bool ready;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // _PLAYER_H
|
#endif // _PLAYER_H
|
||||||
|
|
|
@ -1,123 +1,123 @@
|
||||||
#include "util.h"
|
#include "util.h"
|
||||||
|
|
||||||
extern "C" {
|
extern "C" {
|
||||||
int luaopen_fk(lua_State *);
|
int luaopen_fk(lua_State *);
|
||||||
}
|
}
|
||||||
|
|
||||||
lua_State *CreateLuaState()
|
lua_State *CreateLuaState()
|
||||||
{
|
{
|
||||||
lua_State *L = luaL_newstate();
|
lua_State *L = luaL_newstate();
|
||||||
luaL_openlibs(L);
|
luaL_openlibs(L);
|
||||||
luaopen_fk(L);
|
luaopen_fk(L);
|
||||||
|
|
||||||
return L;
|
return L;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool DoLuaScript(lua_State *L, const char *script)
|
bool DoLuaScript(lua_State *L, const char *script)
|
||||||
{
|
{
|
||||||
lua_getglobal(L, "debug");
|
lua_getglobal(L, "debug");
|
||||||
lua_getfield(L, -1, "traceback");
|
lua_getfield(L, -1, "traceback");
|
||||||
lua_replace(L, -2);
|
lua_replace(L, -2);
|
||||||
|
|
||||||
luaL_loadfile(L, script);
|
luaL_loadfile(L, script);
|
||||||
int error = lua_pcall(L, 0, LUA_MULTRET, -2);
|
int error = lua_pcall(L, 0, LUA_MULTRET, -2);
|
||||||
|
|
||||||
if (error) {
|
if (error) {
|
||||||
const char *error_msg = lua_tostring(L, -1);
|
const char *error_msg = lua_tostring(L, -1);
|
||||||
qDebug() << error_msg;
|
qDebug() << error_msg;
|
||||||
lua_pop(L, 2);
|
lua_pop(L, 2);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
lua_pop(L, 1);
|
lua_pop(L, 1);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// For Lua debugging
|
// For Lua debugging
|
||||||
void Dumpstack(lua_State *L)
|
void Dumpstack(lua_State *L)
|
||||||
{
|
{
|
||||||
int top = lua_gettop(L);
|
int top = lua_gettop(L);
|
||||||
for (int i = 1; i <= top; i++) {
|
for (int i = 1; i <= top; i++) {
|
||||||
printf("%d\t%s\t", i, luaL_typename(L, i));
|
printf("%d\t%s\t", i, luaL_typename(L, i));
|
||||||
switch (lua_type(L, i)) {
|
switch (lua_type(L, i)) {
|
||||||
case LUA_TNUMBER:
|
case LUA_TNUMBER:
|
||||||
printf("%g\n",lua_tonumber(L, i));
|
printf("%g\n",lua_tonumber(L, i));
|
||||||
break;
|
break;
|
||||||
case LUA_TSTRING:
|
case LUA_TSTRING:
|
||||||
printf("%s\n",lua_tostring(L, i));
|
printf("%s\n",lua_tostring(L, i));
|
||||||
break;
|
break;
|
||||||
case LUA_TBOOLEAN:
|
case LUA_TBOOLEAN:
|
||||||
printf("%s\n", (lua_toboolean(L, i) ? "true" : "false"));
|
printf("%s\n", (lua_toboolean(L, i) ? "true" : "false"));
|
||||||
break;
|
break;
|
||||||
case LUA_TNIL:
|
case LUA_TNIL:
|
||||||
printf("%s\n", "nil");
|
printf("%s\n", "nil");
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
printf("%p\n",lua_topointer(L, i));
|
printf("%p\n",lua_topointer(L, i));
|
||||||
break;
|
break;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
sqlite3 *OpenDatabase(const QString &filename)
|
sqlite3 *OpenDatabase(const QString &filename)
|
||||||
{
|
{
|
||||||
sqlite3 *ret;
|
sqlite3 *ret;
|
||||||
int rc;
|
int rc;
|
||||||
if (!QFile::exists(filename)) {
|
if (!QFile::exists(filename)) {
|
||||||
QFile file("./server/init.sql");
|
QFile file("./server/init.sql");
|
||||||
if (!file.open(QIODevice::ReadOnly)) {
|
if (!file.open(QIODevice::ReadOnly)) {
|
||||||
qDebug() << "cannot open init.sql. Quit now.";
|
qDebug() << "cannot open init.sql. Quit now.";
|
||||||
qApp->exit(1);
|
qApp->exit(1);
|
||||||
}
|
|
||||||
|
|
||||||
QTextStream in(&file);
|
|
||||||
char *err_msg;
|
|
||||||
sqlite3_open(filename.toLatin1().data(), &ret);
|
|
||||||
rc = sqlite3_exec(ret, in.readAll().toLatin1().data(), nullptr, nullptr, &err_msg);
|
|
||||||
|
|
||||||
if (rc != SQLITE_OK ) {
|
|
||||||
qDebug() << "sqlite error:" << err_msg;
|
|
||||||
sqlite3_free(err_msg);
|
|
||||||
sqlite3_close(ret);
|
|
||||||
qApp->exit(1);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
rc = sqlite3_open(filename.toLatin1().data(), &ret);
|
|
||||||
if (rc != SQLITE_OK) {
|
|
||||||
qDebug() << "Cannot open database:" << sqlite3_errmsg(ret);
|
|
||||||
sqlite3_close(ret);
|
|
||||||
qApp->exit(1);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return ret;
|
|
||||||
|
QTextStream in(&file);
|
||||||
|
char *err_msg;
|
||||||
|
sqlite3_open(filename.toLatin1().data(), &ret);
|
||||||
|
rc = sqlite3_exec(ret, in.readAll().toLatin1().data(), nullptr, nullptr, &err_msg);
|
||||||
|
|
||||||
|
if (rc != SQLITE_OK ) {
|
||||||
|
qDebug() << "sqlite error:" << err_msg;
|
||||||
|
sqlite3_free(err_msg);
|
||||||
|
sqlite3_close(ret);
|
||||||
|
qApp->exit(1);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
rc = sqlite3_open(filename.toLatin1().data(), &ret);
|
||||||
|
if (rc != SQLITE_OK) {
|
||||||
|
qDebug() << "Cannot open database:" << sqlite3_errmsg(ret);
|
||||||
|
sqlite3_close(ret);
|
||||||
|
qApp->exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
// callback for handling SELECT expression
|
// callback for handling SELECT expression
|
||||||
static int callback(void *jsonDoc, int argc, char **argv, char **cols) {
|
static int callback(void *jsonDoc, int argc, char **argv, char **cols) {
|
||||||
QJsonObject obj;
|
QJsonObject obj;
|
||||||
for (int i = 0; i < argc; i++) {
|
for (int i = 0; i < argc; i++) {
|
||||||
QJsonArray arr = obj[QString(cols[i])].toArray();
|
QJsonArray arr = obj[QString(cols[i])].toArray();
|
||||||
arr << QString(argv[i] ? argv[i] : "#null");
|
arr << QString(argv[i] ? argv[i] : "#null");
|
||||||
obj[QString(cols[i])] = arr;
|
obj[QString(cols[i])] = arr;
|
||||||
}
|
}
|
||||||
((QJsonObject *)jsonDoc)->swap(obj);
|
((QJsonObject *)jsonDoc)->swap(obj);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
QJsonObject SelectFromDatabase(sqlite3 *db, const QString &sql) {
|
QJsonObject SelectFromDatabase(sqlite3 *db, const QString &sql) {
|
||||||
QJsonObject obj;
|
QJsonObject obj;
|
||||||
sqlite3_exec(db, sql.toUtf8().data(), callback, (void *)&obj, nullptr);
|
sqlite3_exec(db, sql.toUtf8().data(), callback, (void *)&obj, nullptr);
|
||||||
return obj;
|
return obj;
|
||||||
}
|
}
|
||||||
|
|
||||||
QString SelectFromDb(sqlite3 *db, const QString &sql) {
|
QString SelectFromDb(sqlite3 *db, const QString &sql) {
|
||||||
QJsonObject obj = SelectFromDatabase(db, sql);
|
QJsonObject obj = SelectFromDatabase(db, sql);
|
||||||
return QJsonDocument(obj).toJson();
|
return QJsonDocument(obj).toJson();
|
||||||
}
|
}
|
||||||
|
|
||||||
void ExecSQL(sqlite3 *db, const QString &sql) {
|
void ExecSQL(sqlite3 *db, const QString &sql) {
|
||||||
sqlite3_exec(db, sql.toUtf8().data(), nullptr, nullptr, nullptr);
|
sqlite3_exec(db, sql.toUtf8().data(), nullptr, nullptr, nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
void CloseDatabase(sqlite3 *db) {
|
void CloseDatabase(sqlite3 *db) {
|
||||||
sqlite3_close(db);
|
sqlite3_close(db);
|
||||||
}
|
}
|
||||||
|
|
86
src/main.cpp
86
src/main.cpp
|
@ -3,59 +3,61 @@
|
||||||
|
|
||||||
int main(int argc, char *argv[])
|
int main(int argc, char *argv[])
|
||||||
{
|
{
|
||||||
QCoreApplication *app;
|
QCoreApplication *app;
|
||||||
QCoreApplication::setApplicationName("FreeKill");
|
QCoreApplication::setApplicationName("FreeKill");
|
||||||
QCoreApplication::setApplicationVersion("Alpha 0.0.1");
|
QCoreApplication::setApplicationVersion("Alpha 0.0.1");
|
||||||
|
|
||||||
QCommandLineParser parser;
|
QCommandLineParser parser;
|
||||||
parser.setApplicationDescription("FreeKill server");
|
parser.setApplicationDescription("FreeKill server");
|
||||||
parser.addHelpOption();
|
parser.addHelpOption();
|
||||||
parser.addVersionOption();
|
parser.addVersionOption();
|
||||||
parser.addOption({{"s", "server"}, "start server at <port>", "port"});
|
parser.addOption({{"s", "server"}, "start server at <port>", "port"});
|
||||||
QStringList cliOptions;
|
QStringList cliOptions;
|
||||||
for (int i = 0; i < argc; i++)
|
for (int i = 0; i < argc; i++)
|
||||||
cliOptions << argv[i];
|
cliOptions << argv[i];
|
||||||
|
|
||||||
parser.parse(cliOptions);
|
parser.parse(cliOptions);
|
||||||
|
|
||||||
bool startServer = parser.isSet("server");
|
bool startServer = parser.isSet("server");
|
||||||
ushort serverPort = 9527;
|
ushort serverPort = 9527;
|
||||||
|
|
||||||
if (startServer) {
|
if (startServer) {
|
||||||
app = new QCoreApplication(argc, argv);
|
app = new QCoreApplication(argc, argv);
|
||||||
bool ok = false;
|
bool ok = false;
|
||||||
if (parser.value("server").toInt(&ok) && ok)
|
if (parser.value("server").toInt(&ok) && ok)
|
||||||
serverPort = parser.value("server").toInt();
|
serverPort = parser.value("server").toInt();
|
||||||
Server *server = new Server;
|
Server *server = new Server;
|
||||||
if (!server->listen(QHostAddress::Any, serverPort)) {
|
if (!server->listen(QHostAddress::Any, serverPort)) {
|
||||||
fprintf(stderr, "cannot listen on port %d!\n", serverPort);
|
fprintf(stderr, "cannot listen on port %d!\n", serverPort);
|
||||||
app->exit(1);
|
app->exit(1);
|
||||||
}
|
|
||||||
return app->exec();
|
|
||||||
}
|
}
|
||||||
|
return app->exec();
|
||||||
|
}
|
||||||
|
|
||||||
app = new QGuiApplication(argc, argv);
|
app = new QGuiApplication(argc, argv);
|
||||||
|
|
||||||
QQmlApplicationEngine *engine = new QQmlApplicationEngine;
|
QQmlApplicationEngine *engine = new QQmlApplicationEngine;
|
||||||
|
|
||||||
QmlBackend backend;
|
QmlBackend backend;
|
||||||
backend.setEngine(engine);
|
backend.setEngine(engine);
|
||||||
|
|
||||||
engine->rootContext()->setContextProperty("Backend", &backend);
|
engine->rootContext()->setContextProperty("Backend", &backend);
|
||||||
engine->rootContext()->setContextProperty("AppPath", QUrl::fromLocalFile(QDir::currentPath()));
|
engine->rootContext()->setContextProperty("AppPath", QUrl::fromLocalFile(QDir::currentPath()));
|
||||||
#ifdef QT_DEBUG
|
#ifdef QT_DEBUG
|
||||||
bool debugging = true;
|
bool debugging = true;
|
||||||
#else
|
#else
|
||||||
bool debugging = false;
|
bool debugging = false;
|
||||||
#endif
|
#endif
|
||||||
engine->rootContext()->setContextProperty("Debugging", debugging);
|
engine->rootContext()->setContextProperty("Debugging", debugging);
|
||||||
engine->load("qml/main.qml");
|
engine->load("qml/main.qml");
|
||||||
|
if (engine->rootObjects().isEmpty())
|
||||||
|
return -1;
|
||||||
|
|
||||||
int ret = app->exec();
|
int ret = app->exec();
|
||||||
|
|
||||||
// delete the engine first
|
// delete the engine first
|
||||||
// to avoid "TypeError: Cannot read property 'xxx' of null"
|
// to avoid "TypeError: Cannot read property 'xxx' of null"
|
||||||
delete engine;
|
delete engine;
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,94 +2,94 @@
|
||||||
|
|
||||||
ClientSocket::ClientSocket() : socket(new QTcpSocket(this))
|
ClientSocket::ClientSocket() : socket(new QTcpSocket(this))
|
||||||
{
|
{
|
||||||
init();
|
init();
|
||||||
}
|
}
|
||||||
|
|
||||||
ClientSocket::ClientSocket(QTcpSocket* socket)
|
ClientSocket::ClientSocket(QTcpSocket* socket)
|
||||||
{
|
{
|
||||||
socket->setParent(this);
|
socket->setParent(this);
|
||||||
this->socket = socket;
|
this->socket = socket;
|
||||||
timerSignup.setSingleShot(true);
|
timerSignup.setSingleShot(true);
|
||||||
connect(&timerSignup, &QTimer::timeout, this, &ClientSocket::disconnectFromHost);
|
connect(&timerSignup, &QTimer::timeout, this, &ClientSocket::disconnectFromHost);
|
||||||
init();
|
init();
|
||||||
}
|
}
|
||||||
|
|
||||||
void ClientSocket::init()
|
void ClientSocket::init()
|
||||||
{
|
{
|
||||||
connect(socket, &QTcpSocket::connected,
|
connect(socket, &QTcpSocket::connected,
|
||||||
this, &ClientSocket::connected);
|
this, &ClientSocket::connected);
|
||||||
connect(socket, &QTcpSocket::disconnected,
|
connect(socket, &QTcpSocket::disconnected,
|
||||||
this, &ClientSocket::disconnected);
|
this, &ClientSocket::disconnected);
|
||||||
connect(socket, &QTcpSocket::readyRead,
|
connect(socket, &QTcpSocket::readyRead,
|
||||||
this, &ClientSocket::getMessage);
|
this, &ClientSocket::getMessage);
|
||||||
connect(socket, &QTcpSocket::errorOccurred,
|
connect(socket, &QTcpSocket::errorOccurred,
|
||||||
this, &ClientSocket::raiseError);
|
this, &ClientSocket::raiseError);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ClientSocket::connectToHost(const QHostAddress &address, ushort port)
|
void ClientSocket::connectToHost(const QHostAddress &address, ushort port)
|
||||||
{
|
{
|
||||||
socket->connectToHost(address, port);
|
socket->connectToHost(address, port);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ClientSocket::getMessage()
|
void ClientSocket::getMessage()
|
||||||
{
|
{
|
||||||
while (socket->canReadLine()) {
|
while (socket->canReadLine()) {
|
||||||
char msg[16000]; // buffer
|
char msg[16000]; // buffer
|
||||||
socket->readLine(msg, sizeof(msg));
|
socket->readLine(msg, sizeof(msg));
|
||||||
emit message_got(msg);
|
emit message_got(msg);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void ClientSocket::disconnectFromHost()
|
void ClientSocket::disconnectFromHost()
|
||||||
{
|
{
|
||||||
socket->disconnectFromHost();
|
socket->disconnectFromHost();
|
||||||
}
|
}
|
||||||
|
|
||||||
void ClientSocket::send(const QByteArray &msg)
|
void ClientSocket::send(const QByteArray &msg)
|
||||||
{
|
{
|
||||||
socket->write(msg);
|
socket->write(msg);
|
||||||
if (!msg.endsWith("\n"))
|
if (!msg.endsWith("\n"))
|
||||||
socket->write("\n");
|
socket->write("\n");
|
||||||
socket->flush();
|
socket->flush();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ClientSocket::isConnected() const
|
bool ClientSocket::isConnected() const
|
||||||
{
|
{
|
||||||
return socket->state() == QTcpSocket::ConnectedState;
|
return socket->state() == QTcpSocket::ConnectedState;
|
||||||
}
|
}
|
||||||
|
|
||||||
QString ClientSocket::peerName() const
|
QString ClientSocket::peerName() const
|
||||||
{
|
{
|
||||||
QString peer_name = socket->peerName();
|
QString peer_name = socket->peerName();
|
||||||
if (peer_name.isEmpty())
|
if (peer_name.isEmpty())
|
||||||
peer_name = QString("%1:%2").arg(socket->peerAddress().toString()).arg(socket->peerPort());
|
peer_name = QString("%1:%2").arg(socket->peerAddress().toString()).arg(socket->peerPort());
|
||||||
|
|
||||||
return peer_name;
|
return peer_name;
|
||||||
}
|
}
|
||||||
|
|
||||||
QString ClientSocket::peerAddress() const
|
QString ClientSocket::peerAddress() const
|
||||||
{
|
{
|
||||||
return socket->peerAddress().toString();
|
return socket->peerAddress().toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
void ClientSocket::raiseError(QAbstractSocket::SocketError socket_error)
|
void ClientSocket::raiseError(QAbstractSocket::SocketError socket_error)
|
||||||
{
|
{
|
||||||
// translate error message
|
// translate error message
|
||||||
QString reason;
|
QString reason;
|
||||||
switch (socket_error) {
|
switch (socket_error) {
|
||||||
case QAbstractSocket::ConnectionRefusedError:
|
case QAbstractSocket::ConnectionRefusedError:
|
||||||
reason = tr("Connection was refused or timeout"); break;
|
reason = tr("Connection was refused or timeout"); break;
|
||||||
case QAbstractSocket::RemoteHostClosedError:
|
case QAbstractSocket::RemoteHostClosedError:
|
||||||
reason = tr("Remote host close this connection"); break;
|
reason = tr("Remote host close this connection"); break;
|
||||||
case QAbstractSocket::HostNotFoundError:
|
case QAbstractSocket::HostNotFoundError:
|
||||||
reason = tr("Host not found"); break;
|
reason = tr("Host not found"); break;
|
||||||
case QAbstractSocket::SocketAccessError:
|
case QAbstractSocket::SocketAccessError:
|
||||||
reason = tr("Socket access error"); break;
|
reason = tr("Socket access error"); break;
|
||||||
case QAbstractSocket::NetworkError:
|
case QAbstractSocket::NetworkError:
|
||||||
return; // this error is ignored ...
|
return; // this error is ignored ...
|
||||||
default: reason = tr("Unknow error"); break;
|
default: reason = tr("Unknow error"); break;
|
||||||
}
|
}
|
||||||
|
|
||||||
emit error_message(tr("Connection failed, error code = %1\n reason: %2")
|
emit error_message(tr("Connection failed, error code = %1\n reason: %2")
|
||||||
.arg(socket_error).arg(reason));
|
.arg(socket_error).arg(reason));
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,34 +2,34 @@
|
||||||
#define _CLIENT_SOCKET_H
|
#define _CLIENT_SOCKET_H
|
||||||
|
|
||||||
class ClientSocket : public QObject {
|
class ClientSocket : public QObject {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
public:
|
public:
|
||||||
ClientSocket();
|
ClientSocket();
|
||||||
// For server use
|
// For server use
|
||||||
ClientSocket(QTcpSocket *socket);
|
ClientSocket(QTcpSocket *socket);
|
||||||
|
|
||||||
void connectToHost(const QHostAddress &address = QHostAddress::LocalHost, ushort port = 9527u);
|
void connectToHost(const QHostAddress &address = QHostAddress::LocalHost, ushort port = 9527u);
|
||||||
void disconnectFromHost();
|
void disconnectFromHost();
|
||||||
void send(const QByteArray& msg);
|
void send(const QByteArray& msg);
|
||||||
bool isConnected() const;
|
bool isConnected() const;
|
||||||
QString peerName() const;
|
QString peerName() const;
|
||||||
QString peerAddress() const;
|
QString peerAddress() const;
|
||||||
QTimer timerSignup;
|
QTimer timerSignup;
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void message_got(const QByteArray& msg);
|
void message_got(const QByteArray& msg);
|
||||||
void error_message(const QString &msg);
|
void error_message(const QString &msg);
|
||||||
void disconnected();
|
void disconnected();
|
||||||
void connected();
|
void connected();
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
void getMessage();
|
void getMessage();
|
||||||
void raiseError(QAbstractSocket::SocketError error);
|
void raiseError(QAbstractSocket::SocketError error);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QTcpSocket *socket;
|
QTcpSocket *socket;
|
||||||
void init();
|
void init();
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // _CLIENT_SOCKET_H
|
#endif // _CLIENT_SOCKET_H
|
||||||
|
|
|
@ -5,200 +5,200 @@
|
||||||
#include "serverplayer.h"
|
#include "serverplayer.h"
|
||||||
|
|
||||||
Router::Router(QObject *parent, ClientSocket *socket, RouterType type)
|
Router::Router(QObject *parent, ClientSocket *socket, RouterType type)
|
||||||
: QObject(parent)
|
: QObject(parent)
|
||||||
{
|
{
|
||||||
this->type = type;
|
this->type = type;
|
||||||
this->socket = nullptr;
|
this->socket = nullptr;
|
||||||
setSocket(socket);
|
setSocket(socket);
|
||||||
expectedReplyId = -1;
|
expectedReplyId = -1;
|
||||||
replyTimeout = 0;
|
replyTimeout = 0;
|
||||||
extraReplyReadySemaphore = nullptr;
|
extraReplyReadySemaphore = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
Router::~Router()
|
Router::~Router()
|
||||||
{
|
{
|
||||||
abortRequest();
|
abortRequest();
|
||||||
}
|
}
|
||||||
|
|
||||||
ClientSocket* Router::getSocket() const
|
ClientSocket* Router::getSocket() const
|
||||||
{
|
{
|
||||||
return socket;
|
return socket;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Router::setSocket(ClientSocket *socket)
|
void Router::setSocket(ClientSocket *socket)
|
||||||
{
|
{
|
||||||
if (this->socket != nullptr) {
|
if (this->socket != nullptr) {
|
||||||
this->socket->disconnect(this);
|
this->socket->disconnect(this);
|
||||||
disconnect(this->socket);
|
disconnect(this->socket);
|
||||||
this->socket->deleteLater();
|
this->socket->deleteLater();
|
||||||
}
|
}
|
||||||
|
|
||||||
this->socket = nullptr;
|
this->socket = nullptr;
|
||||||
if (socket != nullptr) {
|
if (socket != nullptr) {
|
||||||
connect(this, &Router::messageReady, socket, &ClientSocket::send);
|
connect(this, &Router::messageReady, socket, &ClientSocket::send);
|
||||||
connect(socket, &ClientSocket::message_got, this, &Router::handlePacket);
|
connect(socket, &ClientSocket::message_got, this, &Router::handlePacket);
|
||||||
connect(socket, &ClientSocket::disconnected, this, &Router::abortRequest);
|
connect(socket, &ClientSocket::disconnected, this, &Router::abortRequest);
|
||||||
socket->setParent(this);
|
socket->setParent(this);
|
||||||
this->socket = socket;
|
this->socket = socket;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Router::setReplyReadySemaphore(QSemaphore *semaphore)
|
void Router::setReplyReadySemaphore(QSemaphore *semaphore)
|
||||||
{
|
{
|
||||||
extraReplyReadySemaphore = semaphore;
|
extraReplyReadySemaphore = semaphore;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Router::request(int type, const QString& command,
|
void Router::request(int type, const QString& command,
|
||||||
const QString& jsonData, int timeout)
|
const QString& jsonData, int timeout)
|
||||||
{
|
{
|
||||||
// In case a request is called without a following waitForReply call
|
// In case a request is called without a following waitForReply call
|
||||||
if (replyReadySemaphore.available() > 0)
|
if (replyReadySemaphore.available() > 0)
|
||||||
replyReadySemaphore.acquire(replyReadySemaphore.available());
|
replyReadySemaphore.acquire(replyReadySemaphore.available());
|
||||||
|
|
||||||
static int requestId = 0;
|
static int requestId = 0;
|
||||||
requestId++;
|
requestId++;
|
||||||
|
|
||||||
replyMutex.lock();
|
replyMutex.lock();
|
||||||
expectedReplyId = requestId;
|
expectedReplyId = requestId;
|
||||||
replyTimeout = timeout;
|
replyTimeout = timeout;
|
||||||
requestStartTime = QDateTime::currentDateTime();
|
requestStartTime = QDateTime::currentDateTime();
|
||||||
m_reply = QString();
|
m_reply = QString();
|
||||||
replyMutex.unlock();
|
replyMutex.unlock();
|
||||||
|
|
||||||
QJsonArray body;
|
QJsonArray body;
|
||||||
body << requestId;
|
body << requestId;
|
||||||
body << type;
|
body << type;
|
||||||
body << command;
|
body << command;
|
||||||
body << jsonData;
|
body << jsonData;
|
||||||
body << timeout;
|
body << timeout;
|
||||||
|
|
||||||
emit messageReady(QJsonDocument(body).toJson(QJsonDocument::Compact));
|
emit messageReady(QJsonDocument(body).toJson(QJsonDocument::Compact));
|
||||||
}
|
}
|
||||||
|
|
||||||
void Router::reply(int type, const QString& command, const QString& jsonData)
|
void Router::reply(int type, const QString& command, const QString& jsonData)
|
||||||
{
|
{
|
||||||
QJsonArray body;
|
QJsonArray body;
|
||||||
body << this->requestId;
|
body << this->requestId;
|
||||||
body << type;
|
body << type;
|
||||||
body << command;
|
body << command;
|
||||||
body << jsonData;
|
body << jsonData;
|
||||||
|
|
||||||
emit messageReady(QJsonDocument(body).toJson(QJsonDocument::Compact));
|
emit messageReady(QJsonDocument(body).toJson(QJsonDocument::Compact));
|
||||||
}
|
}
|
||||||
|
|
||||||
void Router::notify(int type, const QString& command, const QString& jsonData)
|
void Router::notify(int type, const QString& command, const QString& jsonData)
|
||||||
{
|
{
|
||||||
QJsonArray body;
|
QJsonArray body;
|
||||||
body << -2; // requestId = -2 mean this is for notification
|
body << -2; // requestId = -2 mean this is for notification
|
||||||
body << type;
|
body << type;
|
||||||
body << command;
|
body << command;
|
||||||
body << jsonData;
|
body << jsonData;
|
||||||
|
|
||||||
emit messageReady(QJsonDocument(body).toJson(QJsonDocument::Compact));
|
emit messageReady(QJsonDocument(body).toJson(QJsonDocument::Compact));
|
||||||
}
|
}
|
||||||
|
|
||||||
int Router::getTimeout() const
|
int Router::getTimeout() const
|
||||||
{
|
{
|
||||||
return requestTimeout;
|
return requestTimeout;
|
||||||
}
|
}
|
||||||
|
|
||||||
// cancel last request from the sender
|
// cancel last request from the sender
|
||||||
void Router::cancelRequest()
|
void Router::cancelRequest()
|
||||||
{
|
{
|
||||||
replyMutex.lock();
|
replyMutex.lock();
|
||||||
expectedReplyId = -1;
|
expectedReplyId = -1;
|
||||||
replyTimeout = 0;
|
replyTimeout = 0;
|
||||||
extraReplyReadySemaphore = nullptr;
|
extraReplyReadySemaphore = nullptr;
|
||||||
replyMutex.unlock();
|
replyMutex.unlock();
|
||||||
|
|
||||||
if (replyReadySemaphore.available() > 0)
|
if (replyReadySemaphore.available() > 0)
|
||||||
replyReadySemaphore.acquire(replyReadySemaphore.available());
|
replyReadySemaphore.acquire(replyReadySemaphore.available());
|
||||||
}
|
}
|
||||||
|
|
||||||
QString Router::waitForReply()
|
QString Router::waitForReply()
|
||||||
{
|
{
|
||||||
replyReadySemaphore.acquire();
|
replyReadySemaphore.acquire();
|
||||||
return m_reply;
|
return m_reply;
|
||||||
}
|
}
|
||||||
|
|
||||||
QString Router::waitForReply(int timeout)
|
QString Router::waitForReply(int timeout)
|
||||||
{
|
{
|
||||||
replyReadySemaphore.tryAcquire(1, timeout * 1000);
|
replyReadySemaphore.tryAcquire(1, timeout * 1000);
|
||||||
return m_reply;
|
return m_reply;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Router::abortRequest()
|
void Router::abortRequest()
|
||||||
{
|
{
|
||||||
replyMutex.lock();
|
replyMutex.lock();
|
||||||
if (expectedReplyId != -1) {
|
if (expectedReplyId != -1) {
|
||||||
replyReadySemaphore.release();
|
replyReadySemaphore.release();
|
||||||
if (extraReplyReadySemaphore)
|
if (extraReplyReadySemaphore)
|
||||||
extraReplyReadySemaphore->release();
|
extraReplyReadySemaphore->release();
|
||||||
expectedReplyId = -1;
|
expectedReplyId = -1;
|
||||||
extraReplyReadySemaphore = nullptr;
|
extraReplyReadySemaphore = nullptr;
|
||||||
}
|
}
|
||||||
replyMutex.unlock();
|
replyMutex.unlock();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Router::handlePacket(const QByteArray& rawPacket)
|
void Router::handlePacket(const QByteArray& rawPacket)
|
||||||
{
|
{
|
||||||
QJsonDocument packet = QJsonDocument::fromJson(rawPacket);
|
QJsonDocument packet = QJsonDocument::fromJson(rawPacket);
|
||||||
if (packet.isNull() || !packet.isArray())
|
if (packet.isNull() || !packet.isArray())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
int requestId = packet[0].toInt();
|
int requestId = packet[0].toInt();
|
||||||
int type = packet[1].toInt();
|
int type = packet[1].toInt();
|
||||||
QString command = packet[2].toString();
|
QString command = packet[2].toString();
|
||||||
QString jsonData = packet[3].toString();
|
QString jsonData = packet[3].toString();
|
||||||
|
|
||||||
if (type & TYPE_NOTIFICATION) {
|
if (type & TYPE_NOTIFICATION) {
|
||||||
if (type & DEST_CLIENT) {
|
if (type & DEST_CLIENT) {
|
||||||
ClientInstance->callLua(command, jsonData);
|
ClientInstance->callLua(command, jsonData);
|
||||||
} else {
|
} else {
|
||||||
ServerPlayer *player = qobject_cast<ServerPlayer *>(parent());
|
ServerPlayer *player = qobject_cast<ServerPlayer *>(parent());
|
||||||
// Add the uid of sender to jsonData
|
// Add the uid of sender to jsonData
|
||||||
QJsonArray arr = QJsonDocument::fromJson(jsonData.toUtf8()).array();
|
QJsonArray arr = QJsonDocument::fromJson(jsonData.toUtf8()).array();
|
||||||
arr.prepend(player->getId());
|
arr.prepend(player->getId());
|
||||||
|
|
||||||
Room *room = player->getRoom();
|
Room *room = player->getRoom();
|
||||||
room->lockLua(__FUNCTION__);
|
room->lockLua(__FUNCTION__);
|
||||||
room->callLua(command, QJsonDocument(arr).toJson());
|
room->callLua(command, QJsonDocument(arr).toJson());
|
||||||
room->unlockLua(__FUNCTION__);
|
room->unlockLua(__FUNCTION__);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else if (type & TYPE_REQUEST) {
|
}
|
||||||
this->requestId = requestId;
|
else if (type & TYPE_REQUEST) {
|
||||||
this->requestTimeout = packet[4].toInt();
|
this->requestId = requestId;
|
||||||
|
this->requestTimeout = packet[4].toInt();
|
||||||
|
|
||||||
if (type & DEST_CLIENT) {
|
if (type & DEST_CLIENT) {
|
||||||
qobject_cast<Client *>(parent())->callLua(command, jsonData);
|
qobject_cast<Client *>(parent())->callLua(command, jsonData);
|
||||||
} else {
|
} else {
|
||||||
// requesting server is not allowed
|
// requesting server is not allowed
|
||||||
Q_ASSERT(false);
|
Q_ASSERT(false);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else if (type & TYPE_REPLY) {
|
}
|
||||||
QMutexLocker locker(&replyMutex);
|
else if (type & TYPE_REPLY) {
|
||||||
|
QMutexLocker locker(&replyMutex);
|
||||||
|
|
||||||
if (requestId != this->expectedReplyId)
|
if (requestId != this->expectedReplyId)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
this->expectedReplyId = -1;
|
this->expectedReplyId = -1;
|
||||||
|
|
||||||
if (replyTimeout >= 0 && replyTimeout <
|
if (replyTimeout >= 0 && replyTimeout <
|
||||||
requestStartTime.secsTo(QDateTime::currentDateTime()))
|
requestStartTime.secsTo(QDateTime::currentDateTime()))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
m_reply = jsonData;
|
m_reply = jsonData;
|
||||||
// TODO: callback?
|
// TODO: callback?
|
||||||
|
|
||||||
replyReadySemaphore.release();
|
replyReadySemaphore.release();
|
||||||
if (extraReplyReadySemaphore) {
|
if (extraReplyReadySemaphore) {
|
||||||
extraReplyReadySemaphore->release();
|
extraReplyReadySemaphore->release();
|
||||||
extraReplyReadySemaphore = nullptr;
|
extraReplyReadySemaphore = nullptr;
|
||||||
}
|
|
||||||
locker.unlock();
|
|
||||||
emit replyReady();
|
|
||||||
}
|
}
|
||||||
|
locker.unlock();
|
||||||
|
emit replyReady();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -4,75 +4,75 @@
|
||||||
class ClientSocket;
|
class ClientSocket;
|
||||||
|
|
||||||
class Router : public QObject {
|
class Router : public QObject {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
public:
|
public:
|
||||||
enum PacketType {
|
enum PacketType {
|
||||||
TYPE_REQUEST = 0x100,
|
TYPE_REQUEST = 0x100,
|
||||||
TYPE_REPLY = 0x200,
|
TYPE_REPLY = 0x200,
|
||||||
TYPE_NOTIFICATION = 0x400,
|
TYPE_NOTIFICATION = 0x400,
|
||||||
SRC_CLIENT = 0x010,
|
SRC_CLIENT = 0x010,
|
||||||
SRC_SERVER = 0x020,
|
SRC_SERVER = 0x020,
|
||||||
SRC_LOBBY = 0x040,
|
SRC_LOBBY = 0x040,
|
||||||
DEST_CLIENT = 0x001,
|
DEST_CLIENT = 0x001,
|
||||||
DEST_SERVER = 0x002,
|
DEST_SERVER = 0x002,
|
||||||
DEST_LOBBY = 0x004
|
DEST_LOBBY = 0x004
|
||||||
};
|
};
|
||||||
|
|
||||||
enum RouterType {
|
enum RouterType {
|
||||||
TYPE_SERVER,
|
TYPE_SERVER,
|
||||||
TYPE_CLIENT
|
TYPE_CLIENT
|
||||||
};
|
};
|
||||||
Router(QObject *parent, ClientSocket *socket, RouterType type);
|
Router(QObject *parent, ClientSocket *socket, RouterType type);
|
||||||
~Router();
|
~Router();
|
||||||
|
|
||||||
ClientSocket *getSocket() const;
|
ClientSocket *getSocket() const;
|
||||||
void setSocket(ClientSocket *socket);
|
void setSocket(ClientSocket *socket);
|
||||||
|
|
||||||
void setReplyReadySemaphore(QSemaphore *semaphore);
|
void setReplyReadySemaphore(QSemaphore *semaphore);
|
||||||
|
|
||||||
void request(int type, const QString &command,
|
void request(int type, const QString &command,
|
||||||
const QString &jsonData, int timeout);
|
const QString &jsonData, int timeout);
|
||||||
void reply(int type, const QString &command, const QString &jsonData);
|
void reply(int type, const QString &command, const QString &jsonData);
|
||||||
void notify(int type, const QString &command, const QString &jsonData);
|
void notify(int type, const QString &command, const QString &jsonData);
|
||||||
|
|
||||||
int getTimeout() const;
|
int getTimeout() const;
|
||||||
|
|
||||||
void cancelRequest();
|
void cancelRequest();
|
||||||
void abortRequest();
|
void abortRequest();
|
||||||
|
|
||||||
QString waitForReply();
|
QString waitForReply();
|
||||||
QString waitForReply(int timeout);
|
QString waitForReply(int timeout);
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void messageReady(const QByteArray &message);
|
void messageReady(const QByteArray &message);
|
||||||
void unknownPacket(const QByteArray &packet);
|
void unknownPacket(const QByteArray &packet);
|
||||||
void replyReady();
|
void replyReady();
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void handlePacket(const QByteArray &rawPacket);
|
void handlePacket(const QByteArray &rawPacket);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
ClientSocket *socket;
|
ClientSocket *socket;
|
||||||
RouterType type;
|
RouterType type;
|
||||||
|
|
||||||
// For sender
|
// For sender
|
||||||
int requestId;
|
int requestId;
|
||||||
int requestTimeout;
|
int requestTimeout;
|
||||||
|
|
||||||
// For receiver
|
// For receiver
|
||||||
QDateTime requestStartTime;
|
QDateTime requestStartTime;
|
||||||
QMutex replyMutex;
|
QMutex replyMutex;
|
||||||
int expectedReplyId;
|
int expectedReplyId;
|
||||||
int replyTimeout;
|
int replyTimeout;
|
||||||
QString m_reply; // should be json string
|
QString m_reply; // should be json string
|
||||||
QSemaphore replyReadySemaphore;
|
QSemaphore replyReadySemaphore;
|
||||||
QSemaphore *extraReplyReadySemaphore;
|
QSemaphore *extraReplyReadySemaphore;
|
||||||
|
|
||||||
// Two Lua global table for callbacks and interactions
|
// Two Lua global table for callbacks and interactions
|
||||||
// stored in the lua_State of the sender
|
// stored in the lua_State of the sender
|
||||||
// LuaTable interactions;
|
// LuaTable interactions;
|
||||||
// LuaTable callbacks;
|
// LuaTable callbacks;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // _ROUTER_H
|
#endif // _ROUTER_H
|
||||||
|
|
|
@ -3,23 +3,23 @@
|
||||||
|
|
||||||
ServerSocket::ServerSocket()
|
ServerSocket::ServerSocket()
|
||||||
{
|
{
|
||||||
server = new QTcpServer(this);
|
server = new QTcpServer(this);
|
||||||
connect(server, &QTcpServer::newConnection,
|
connect(server, &QTcpServer::newConnection,
|
||||||
this, &ServerSocket::processNewConnection);
|
this, &ServerSocket::processNewConnection);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ServerSocket::listen(const QHostAddress &address, ushort port)
|
bool ServerSocket::listen(const QHostAddress &address, ushort port)
|
||||||
{
|
{
|
||||||
return server->listen(address, port);
|
return server->listen(address, port);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ServerSocket::processNewConnection()
|
void ServerSocket::processNewConnection()
|
||||||
{
|
{
|
||||||
QTcpSocket *socket = server->nextPendingConnection();
|
QTcpSocket *socket = server->nextPendingConnection();
|
||||||
ClientSocket *connection = new ClientSocket(socket);
|
ClientSocket *connection = new ClientSocket(socket);
|
||||||
connect(connection, &ClientSocket::disconnected, this, [connection](){
|
connect(connection, &ClientSocket::disconnected, this, [connection](){
|
||||||
connection->deleteLater();
|
connection->deleteLater();
|
||||||
});
|
});
|
||||||
emit new_connection(connection);
|
emit new_connection(connection);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -4,21 +4,21 @@
|
||||||
class ClientSocket;
|
class ClientSocket;
|
||||||
|
|
||||||
class ServerSocket : public QObject {
|
class ServerSocket : public QObject {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
public:
|
public:
|
||||||
ServerSocket();
|
ServerSocket();
|
||||||
|
|
||||||
bool listen(const QHostAddress &address = QHostAddress::Any, ushort port = 9527u);
|
bool listen(const QHostAddress &address = QHostAddress::Any, ushort port = 9527u);
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void new_connection(ClientSocket *socket);
|
void new_connection(ClientSocket *socket);
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
void processNewConnection();
|
void processNewConnection();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QTcpServer *server;
|
QTcpServer *server;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // _SERVER_SOCKET_H
|
#endif // _SERVER_SOCKET_H
|
||||||
|
|
|
@ -1,5 +0,0 @@
|
||||||
<!DOCTYPE RCC><RCC version="1.0">
|
|
||||||
<qresource>
|
|
||||||
<file>qml/main.qml</file>
|
|
||||||
</qresource>
|
|
||||||
</RCC>
|
|
|
@ -5,306 +5,306 @@
|
||||||
|
|
||||||
Room::Room(Server* server)
|
Room::Room(Server* server)
|
||||||
{
|
{
|
||||||
id = server->nextRoomId;
|
id = server->nextRoomId;
|
||||||
server->nextRoomId++;
|
server->nextRoomId++;
|
||||||
this->server = server;
|
this->server = server;
|
||||||
setParent(server);
|
setParent(server);
|
||||||
owner = nullptr;
|
owner = nullptr;
|
||||||
gameStarted = false;
|
gameStarted = false;
|
||||||
robot_id = -1;
|
robot_id = -1;
|
||||||
timeout = 15;
|
timeout = 15;
|
||||||
if (!isLobby()) {
|
if (!isLobby()) {
|
||||||
connect(this, &Room::playerAdded, server->lobby(), &Room::removePlayer);
|
connect(this, &Room::playerAdded, server->lobby(), &Room::removePlayer);
|
||||||
connect(this, &Room::playerRemoved, server->lobby(), &Room::addPlayer);
|
connect(this, &Room::playerRemoved, server->lobby(), &Room::addPlayer);
|
||||||
}
|
}
|
||||||
|
|
||||||
L = CreateLuaState();
|
L = CreateLuaState();
|
||||||
DoLuaScript(L, "lua/freekill.lua");
|
DoLuaScript(L, "lua/freekill.lua");
|
||||||
if (isLobby()) {
|
if (isLobby()) {
|
||||||
DoLuaScript(L, "lua/server/lobby.lua");
|
DoLuaScript(L, "lua/server/lobby.lua");
|
||||||
} else {
|
} else {
|
||||||
DoLuaScript(L, "lua/server/room.lua");
|
DoLuaScript(L, "lua/server/room.lua");
|
||||||
}
|
}
|
||||||
initLua();
|
initLua();
|
||||||
}
|
}
|
||||||
|
|
||||||
Room::~Room()
|
Room::~Room()
|
||||||
{
|
{
|
||||||
// TODO
|
// TODO
|
||||||
if (isRunning()) {
|
if (isRunning()) {
|
||||||
callLua("RoomDeleted", "");
|
callLua("RoomDeleted", "");
|
||||||
unlockLua(__FUNCTION__);
|
unlockLua(__FUNCTION__);
|
||||||
wait();
|
wait();
|
||||||
}
|
}
|
||||||
lua_close(L);
|
lua_close(L);
|
||||||
}
|
}
|
||||||
|
|
||||||
Server *Room::getServer() const
|
Server *Room::getServer() const
|
||||||
{
|
{
|
||||||
return server;
|
return server;
|
||||||
}
|
}
|
||||||
|
|
||||||
int Room::getId() const
|
int Room::getId() const
|
||||||
{
|
{
|
||||||
return id;
|
return id;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Room::isLobby() const
|
bool Room::isLobby() const
|
||||||
{
|
{
|
||||||
return id == 0;
|
return id == 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
QString Room::getName() const
|
QString Room::getName() const
|
||||||
{
|
{
|
||||||
return name;
|
return name;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Room::setName(const QString &name)
|
void Room::setName(const QString &name)
|
||||||
{
|
{
|
||||||
this->name = name;
|
this->name = name;
|
||||||
}
|
}
|
||||||
|
|
||||||
int Room::getCapacity() const
|
int Room::getCapacity() const
|
||||||
{
|
{
|
||||||
return capacity;
|
return capacity;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Room::setCapacity(int capacity)
|
void Room::setCapacity(int capacity)
|
||||||
{
|
{
|
||||||
this->capacity = capacity;
|
this->capacity = capacity;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Room::isFull() const
|
bool Room::isFull() const
|
||||||
{
|
{
|
||||||
return players.count() == capacity;
|
return players.count() == capacity;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Room::isAbandoned() const
|
bool Room::isAbandoned() const
|
||||||
{
|
{
|
||||||
if (players.isEmpty())
|
if (players.isEmpty())
|
||||||
return true;
|
|
||||||
|
|
||||||
foreach (ServerPlayer *p, players) {
|
|
||||||
if (p->getState() == Player::Online)
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
|
foreach (ServerPlayer *p, players) {
|
||||||
|
if (p->getState() == Player::Online)
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
ServerPlayer *Room::getOwner() const
|
ServerPlayer *Room::getOwner() const
|
||||||
{
|
{
|
||||||
return owner;
|
return owner;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Room::setOwner(ServerPlayer *owner)
|
void Room::setOwner(ServerPlayer *owner)
|
||||||
{
|
{
|
||||||
this->owner = owner;
|
this->owner = owner;
|
||||||
QJsonArray jsonData;
|
QJsonArray jsonData;
|
||||||
jsonData << owner->getId();
|
jsonData << owner->getId();
|
||||||
doBroadcastNotify(players, "RoomOwner", QJsonDocument(jsonData).toJson());
|
doBroadcastNotify(players, "RoomOwner", QJsonDocument(jsonData).toJson());
|
||||||
}
|
}
|
||||||
|
|
||||||
void Room::addPlayer(ServerPlayer *player)
|
void Room::addPlayer(ServerPlayer *player)
|
||||||
{
|
{
|
||||||
if (!player) return;
|
if (!player) return;
|
||||||
|
|
||||||
if (isFull() || gameStarted) {
|
if (isFull() || gameStarted) {
|
||||||
player->doNotify("ErrorMsg", "Room is full or already started!");
|
player->doNotify("ErrorMsg", "Room is full or already started!");
|
||||||
if (runned_players.contains(player->getId())) {
|
if (runned_players.contains(player->getId())) {
|
||||||
player->doNotify("ErrorMsg", "Running away is shameful.");
|
player->doNotify("ErrorMsg", "Running away is shameful.");
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
QJsonArray jsonData;
|
||||||
|
|
||||||
|
// First, notify other players the new player is entering
|
||||||
|
if (!isLobby()) {
|
||||||
|
jsonData << player->getId();
|
||||||
|
jsonData << player->getScreenName();
|
||||||
|
jsonData << player->getAvatar();
|
||||||
|
doBroadcastNotify(getPlayers(), "AddPlayer", QJsonDocument(jsonData).toJson());
|
||||||
|
}
|
||||||
|
|
||||||
|
players.append(player);
|
||||||
|
player->setRoom(this);
|
||||||
|
if (isLobby()) {
|
||||||
|
player->doNotify("EnterLobby", "[]");
|
||||||
|
} else {
|
||||||
|
// Second, let the player enter room and add other players
|
||||||
|
jsonData = QJsonArray();
|
||||||
|
jsonData << this->capacity;
|
||||||
|
jsonData << this->timeout;
|
||||||
|
player->doNotify("EnterRoom", QJsonDocument(jsonData).toJson());
|
||||||
|
|
||||||
|
foreach (ServerPlayer *p, getOtherPlayers(player)) {
|
||||||
|
jsonData = QJsonArray();
|
||||||
|
jsonData << p->getId();
|
||||||
|
jsonData << p->getScreenName();
|
||||||
|
jsonData << p->getAvatar();
|
||||||
|
player->doNotify("AddPlayer", QJsonDocument(jsonData).toJson());
|
||||||
}
|
}
|
||||||
|
|
||||||
QJsonArray jsonData;
|
if (this->owner != nullptr) {
|
||||||
|
jsonData = QJsonArray();
|
||||||
// First, notify other players the new player is entering
|
jsonData << this->owner->getId();
|
||||||
if (!isLobby()) {
|
player->doNotify("RoomOwner", QJsonDocument(jsonData).toJson());
|
||||||
jsonData << player->getId();
|
|
||||||
jsonData << player->getScreenName();
|
|
||||||
jsonData << player->getAvatar();
|
|
||||||
doBroadcastNotify(getPlayers(), "AddPlayer", QJsonDocument(jsonData).toJson());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
players.append(player);
|
if (isFull() && !gameStarted)
|
||||||
player->setRoom(this);
|
start();
|
||||||
if (isLobby()) {
|
}
|
||||||
player->doNotify("EnterLobby", "[]");
|
emit playerAdded(player);
|
||||||
} else {
|
|
||||||
// Second, let the player enter room and add other players
|
|
||||||
jsonData = QJsonArray();
|
|
||||||
jsonData << this->capacity;
|
|
||||||
jsonData << this->timeout;
|
|
||||||
player->doNotify("EnterRoom", QJsonDocument(jsonData).toJson());
|
|
||||||
|
|
||||||
foreach (ServerPlayer *p, getOtherPlayers(player)) {
|
|
||||||
jsonData = QJsonArray();
|
|
||||||
jsonData << p->getId();
|
|
||||||
jsonData << p->getScreenName();
|
|
||||||
jsonData << p->getAvatar();
|
|
||||||
player->doNotify("AddPlayer", QJsonDocument(jsonData).toJson());
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this->owner != nullptr) {
|
|
||||||
jsonData = QJsonArray();
|
|
||||||
jsonData << this->owner->getId();
|
|
||||||
player->doNotify("RoomOwner", QJsonDocument(jsonData).toJson());
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isFull() && !gameStarted)
|
|
||||||
start();
|
|
||||||
}
|
|
||||||
emit playerAdded(player);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Room::addRobot(ServerPlayer *player)
|
void Room::addRobot(ServerPlayer *player)
|
||||||
{
|
{
|
||||||
if (player != owner || isFull()) return;
|
if (player != owner || isFull()) return;
|
||||||
|
|
||||||
ServerPlayer *robot = new ServerPlayer(this);
|
ServerPlayer *robot = new ServerPlayer(this);
|
||||||
robot->setState(Player::Robot);
|
robot->setState(Player::Robot);
|
||||||
robot->setId(robot_id);
|
robot->setId(robot_id);
|
||||||
robot->setAvatar("guanyu");
|
robot->setAvatar("guanyu");
|
||||||
robot->setScreenName(QString("COMP-%1").arg(robot_id));
|
robot->setScreenName(QString("COMP-%1").arg(robot_id));
|
||||||
robot_id--;
|
robot_id--;
|
||||||
|
|
||||||
addPlayer(robot);
|
addPlayer(robot);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Room::removePlayer(ServerPlayer *player)
|
void Room::removePlayer(ServerPlayer *player)
|
||||||
{
|
{
|
||||||
players.removeOne(player);
|
players.removeOne(player);
|
||||||
emit playerRemoved(player);
|
emit playerRemoved(player);
|
||||||
|
|
||||||
if (isLobby()) return;
|
if (isLobby()) return;
|
||||||
|
|
||||||
if (gameStarted) {
|
if (gameStarted) {
|
||||||
// TODO: if the player is died..
|
// TODO: if the player is died..
|
||||||
|
|
||||||
// create robot first
|
// create robot first
|
||||||
ServerPlayer *robot = new ServerPlayer(this);
|
ServerPlayer *robot = new ServerPlayer(this);
|
||||||
robot->setState(Player::Robot);
|
robot->setState(Player::Robot);
|
||||||
robot->setId(robot_id);
|
robot->setId(robot_id);
|
||||||
robot->setAvatar(player->getAvatar());
|
robot->setAvatar(player->getAvatar());
|
||||||
robot->setScreenName(QString("COMP-%1").arg(robot_id));
|
robot->setScreenName(QString("COMP-%1").arg(robot_id));
|
||||||
robot_id--;
|
robot_id--;
|
||||||
|
|
||||||
players.append(robot);
|
players.append(robot);
|
||||||
|
|
||||||
// tell lua & clients
|
// tell lua & clients
|
||||||
QJsonArray jsonData;
|
QJsonArray jsonData;
|
||||||
jsonData << player->getId();
|
jsonData << player->getId();
|
||||||
jsonData << robot->getId();
|
jsonData << robot->getId();
|
||||||
callLua("PlayerRunned", QJsonDocument(jsonData).toJson());
|
callLua("PlayerRunned", QJsonDocument(jsonData).toJson());
|
||||||
doBroadcastNotify(getPlayers(), "PlayerRunned", QJsonDocument(jsonData).toJson());
|
doBroadcastNotify(getPlayers(), "PlayerRunned", QJsonDocument(jsonData).toJson());
|
||||||
runned_players << player->getId();
|
runned_players << player->getId();
|
||||||
|
|
||||||
// FIXME: abortRequest here will result crash
|
// FIXME: abortRequest here will result crash
|
||||||
// but if dont abort and room is abandoned, the main thread will wait until replyed
|
// but if dont abort and room is abandoned, the main thread will wait until replyed
|
||||||
// player->abortRequest();
|
// player->abortRequest();
|
||||||
} else {
|
} else {
|
||||||
QJsonArray jsonData;
|
QJsonArray jsonData;
|
||||||
jsonData << player->getId();
|
jsonData << player->getId();
|
||||||
doBroadcastNotify(getPlayers(), "RemovePlayer", QJsonDocument(jsonData).toJson());
|
doBroadcastNotify(getPlayers(), "RemovePlayer", QJsonDocument(jsonData).toJson());
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isAbandoned()) {
|
if (isAbandoned()) {
|
||||||
// FIXME: do not delete room here
|
// FIXME: do not delete room here
|
||||||
// create a new thread and delete the room
|
// create a new thread and delete the room
|
||||||
emit abandoned();
|
emit abandoned();
|
||||||
} else if (player == owner) {
|
} else if (player == owner) {
|
||||||
setOwner(players.first());
|
setOwner(players.first());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
QList<ServerPlayer *> Room::getPlayers() const
|
QList<ServerPlayer *> Room::getPlayers() const
|
||||||
{
|
{
|
||||||
return players;
|
return players;
|
||||||
}
|
}
|
||||||
|
|
||||||
QList<ServerPlayer *> Room::getOtherPlayers(ServerPlayer* expect) const
|
QList<ServerPlayer *> Room::getOtherPlayers(ServerPlayer* expect) const
|
||||||
{
|
{
|
||||||
QList<ServerPlayer *> others = getPlayers();
|
QList<ServerPlayer *> others = getPlayers();
|
||||||
others.removeOne(expect);
|
others.removeOne(expect);
|
||||||
return others;
|
return others;
|
||||||
}
|
}
|
||||||
|
|
||||||
ServerPlayer *Room::findPlayer(int id) const
|
ServerPlayer *Room::findPlayer(int id) const
|
||||||
{
|
{
|
||||||
foreach (ServerPlayer *p, players) {
|
foreach (ServerPlayer *p, players) {
|
||||||
if (p->getId() == id)
|
if (p->getId() == id)
|
||||||
return p;
|
return p;
|
||||||
}
|
}
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
int Room::getTimeout() const
|
int Room::getTimeout() const
|
||||||
{
|
{
|
||||||
return timeout;
|
return timeout;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Room::setTimeout(int timeout)
|
void Room::setTimeout(int timeout)
|
||||||
{
|
{
|
||||||
this->timeout = timeout;
|
this->timeout = timeout;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Room::isStarted() const
|
bool Room::isStarted() const
|
||||||
{
|
{
|
||||||
return gameStarted;
|
return gameStarted;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Room::doRequest(const QList<ServerPlayer *> targets, int timeout)
|
void Room::doRequest(const QList<ServerPlayer *> targets, int timeout)
|
||||||
{
|
{
|
||||||
// TODO
|
// TODO
|
||||||
}
|
}
|
||||||
|
|
||||||
void Room::doNotify(const QList<ServerPlayer *> targets, int timeout)
|
void Room::doNotify(const QList<ServerPlayer *> targets, int timeout)
|
||||||
{
|
{
|
||||||
// TODO
|
// TODO
|
||||||
}
|
}
|
||||||
|
|
||||||
void Room::doBroadcastNotify(const QList<ServerPlayer *> targets,
|
void Room::doBroadcastNotify(const QList<ServerPlayer *> targets,
|
||||||
const QString& command, const QString& jsonData)
|
const QString& command, const QString& jsonData)
|
||||||
{
|
{
|
||||||
foreach (ServerPlayer *p, targets) {
|
foreach (ServerPlayer *p, targets) {
|
||||||
p->doNotify(command, jsonData);
|
p->doNotify(command, jsonData);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Room::gameOver()
|
void Room::gameOver()
|
||||||
{
|
{
|
||||||
gameStarted = false;
|
gameStarted = false;
|
||||||
runned_players.clear();
|
runned_players.clear();
|
||||||
// clean not online players
|
// clean not online players
|
||||||
foreach (ServerPlayer *p, players) {
|
foreach (ServerPlayer *p, players) {
|
||||||
if (p->getState() != Player::Online) {
|
if (p->getState() != Player::Online) {
|
||||||
p->deleteLater();
|
p->deleteLater();
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Room::lockLua(const QString &caller)
|
void Room::lockLua(const QString &caller)
|
||||||
{
|
{
|
||||||
if (!gameStarted) return;
|
if (!gameStarted) return;
|
||||||
lua_mutex.lock();
|
lua_mutex.lock();
|
||||||
#ifdef QT_DEBUG
|
#ifdef QT_DEBUG
|
||||||
//qDebug() << caller << "=> room->L is locked.";
|
//qDebug() << caller << "=> room->L is locked.";
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
void Room::unlockLua(const QString &caller)
|
void Room::unlockLua(const QString &caller)
|
||||||
{
|
{
|
||||||
if (!gameStarted) return;
|
if (!gameStarted) return;
|
||||||
lua_mutex.unlock();
|
lua_mutex.unlock();
|
||||||
#ifdef QT_DEBUG
|
#ifdef QT_DEBUG
|
||||||
//qDebug() << caller << "=> room->L is unlocked.";
|
//qDebug() << caller << "=> room->L is unlocked.";
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
void Room::run()
|
void Room::run()
|
||||||
{
|
{
|
||||||
gameStarted = true;
|
gameStarted = true;
|
||||||
lockLua(__FUNCTION__);
|
lockLua(__FUNCTION__);
|
||||||
roomStart();
|
roomStart();
|
||||||
unlockLua(__FUNCTION__);
|
unlockLua(__FUNCTION__);
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,86 +5,86 @@ class Server;
|
||||||
class ServerPlayer;
|
class ServerPlayer;
|
||||||
|
|
||||||
class Room : public QThread {
|
class Room : public QThread {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
public:
|
public:
|
||||||
explicit Room(Server *m_server);
|
explicit Room(Server *m_server);
|
||||||
~Room();
|
~Room();
|
||||||
|
|
||||||
// Property reader & setter
|
// Property reader & setter
|
||||||
// ==================================={
|
// ==================================={
|
||||||
Server *getServer() const;
|
Server *getServer() const;
|
||||||
int getId() const;
|
int getId() const;
|
||||||
bool isLobby() const;
|
bool isLobby() const;
|
||||||
QString getName() const;
|
QString getName() const;
|
||||||
void setName(const QString &name);
|
void setName(const QString &name);
|
||||||
int getCapacity() const;
|
int getCapacity() const;
|
||||||
void setCapacity(int capacity);
|
void setCapacity(int capacity);
|
||||||
bool isFull() const;
|
bool isFull() const;
|
||||||
bool isAbandoned() const;
|
bool isAbandoned() const;
|
||||||
|
|
||||||
ServerPlayer *getOwner() const;
|
ServerPlayer *getOwner() const;
|
||||||
void setOwner(ServerPlayer *owner);
|
void setOwner(ServerPlayer *owner);
|
||||||
|
|
||||||
void addPlayer(ServerPlayer *player);
|
void addPlayer(ServerPlayer *player);
|
||||||
void addRobot(ServerPlayer *player);
|
void addRobot(ServerPlayer *player);
|
||||||
void removePlayer(ServerPlayer *player);
|
void removePlayer(ServerPlayer *player);
|
||||||
QList<ServerPlayer*> getPlayers() const;
|
QList<ServerPlayer*> getPlayers() const;
|
||||||
QList<ServerPlayer *> getOtherPlayers(ServerPlayer *expect) const;
|
QList<ServerPlayer *> getOtherPlayers(ServerPlayer *expect) const;
|
||||||
ServerPlayer *findPlayer(int id) const;
|
ServerPlayer *findPlayer(int id) const;
|
||||||
|
|
||||||
int getTimeout() const;
|
int getTimeout() const;
|
||||||
void setTimeout(int timeout);
|
void setTimeout(int timeout);
|
||||||
|
|
||||||
bool isStarted() const;
|
bool isStarted() const;
|
||||||
// ====================================}
|
// ====================================}
|
||||||
|
|
||||||
void doRequest(const QList<ServerPlayer *> targets, int timeout);
|
void doRequest(const QList<ServerPlayer *> targets, int timeout);
|
||||||
void doNotify(const QList<ServerPlayer *> targets, int timeout);
|
void doNotify(const QList<ServerPlayer *> targets, int timeout);
|
||||||
|
|
||||||
void doBroadcastNotify(
|
void doBroadcastNotify(
|
||||||
const QList<ServerPlayer *> targets,
|
const QList<ServerPlayer *> targets,
|
||||||
const QString &command,
|
const QString &command,
|
||||||
const QString &jsonData
|
const QString &jsonData
|
||||||
);
|
);
|
||||||
|
|
||||||
void gameOver();
|
void gameOver();
|
||||||
|
|
||||||
void initLua();
|
void initLua();
|
||||||
void callLua(const QString &command, const QString &jsonData);
|
void callLua(const QString &command, const QString &jsonData);
|
||||||
LuaFunction callback;
|
LuaFunction callback;
|
||||||
|
|
||||||
void roomStart();
|
void roomStart();
|
||||||
LuaFunction startGame;
|
LuaFunction startGame;
|
||||||
|
|
||||||
void lockLua(const QString &caller);
|
void lockLua(const QString &caller);
|
||||||
void unlockLua(const QString &caller);
|
void unlockLua(const QString &caller);
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void abandoned();
|
void abandoned();
|
||||||
|
|
||||||
void playerAdded(ServerPlayer *player);
|
void playerAdded(ServerPlayer *player);
|
||||||
void playerRemoved(ServerPlayer *player);
|
void playerRemoved(ServerPlayer *player);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
virtual void run();
|
virtual void run();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Server *server;
|
Server *server;
|
||||||
int id; // Lobby's id is 0
|
int id; // Lobby's id is 0
|
||||||
QString name; // “阴间大乱斗”
|
QString name; // “阴间大乱斗”
|
||||||
int capacity; // by default is 5, max is 8
|
int capacity; // by default is 5, max is 8
|
||||||
bool m_abandoned; // If room is empty, delete it
|
bool m_abandoned; // If room is empty, delete it
|
||||||
|
|
||||||
ServerPlayer *owner; // who created this room?
|
ServerPlayer *owner; // who created this room?
|
||||||
QList<ServerPlayer *> players;
|
QList<ServerPlayer *> players;
|
||||||
QList<int> runned_players;
|
QList<int> runned_players;
|
||||||
int robot_id;
|
int robot_id;
|
||||||
bool gameStarted;
|
bool gameStarted;
|
||||||
|
|
||||||
int timeout;
|
int timeout;
|
||||||
|
|
||||||
lua_State *L;
|
lua_State *L;
|
||||||
QMutex lua_mutex;
|
QMutex lua_mutex;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // _ROOM_H
|
#endif // _ROOM_H
|
||||||
|
|
|
@ -9,253 +9,253 @@
|
||||||
Server *ServerInstance;
|
Server *ServerInstance;
|
||||||
|
|
||||||
Server::Server(QObject* parent)
|
Server::Server(QObject* parent)
|
||||||
: QObject(parent)
|
: QObject(parent)
|
||||||
{
|
{
|
||||||
ServerInstance = this;
|
ServerInstance = this;
|
||||||
db = OpenDatabase();
|
db = OpenDatabase();
|
||||||
server = new ServerSocket();
|
server = new ServerSocket();
|
||||||
server->setParent(this);
|
server->setParent(this);
|
||||||
connect(server, &ServerSocket::new_connection,
|
connect(server, &ServerSocket::new_connection,
|
||||||
this, &Server::processNewConnection);
|
this, &Server::processNewConnection);
|
||||||
|
|
||||||
// create lobby
|
// create lobby
|
||||||
nextRoomId = 0;
|
nextRoomId = 0;
|
||||||
createRoom(nullptr, "Lobby", INT32_MAX);
|
createRoom(nullptr, "Lobby", INT32_MAX);
|
||||||
connect(lobby(), &Room::playerAdded, this, &Server::updateRoomList);
|
connect(lobby(), &Room::playerAdded, this, &Server::updateRoomList);
|
||||||
connect(lobby(), &Room::playerRemoved, this, &Server::updateRoomList);
|
connect(lobby(), &Room::playerRemoved, this, &Server::updateRoomList);
|
||||||
}
|
}
|
||||||
|
|
||||||
Server::~Server()
|
Server::~Server()
|
||||||
{
|
{
|
||||||
ServerInstance = nullptr;
|
ServerInstance = nullptr;
|
||||||
m_lobby->deleteLater();
|
m_lobby->deleteLater();
|
||||||
sqlite3_close(db);
|
sqlite3_close(db);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Server::listen(const QHostAddress& address, ushort port)
|
bool Server::listen(const QHostAddress& address, ushort port)
|
||||||
{
|
{
|
||||||
return server->listen(address, port);
|
return server->listen(address, port);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Server::createRoom(ServerPlayer* owner, const QString &name, int capacity)
|
void Server::createRoom(ServerPlayer* owner, const QString &name, int capacity)
|
||||||
{
|
{
|
||||||
Room *room = new Room(this);
|
Room *room = new Room(this);
|
||||||
connect(room, &Room::abandoned, this, &Server::onRoomAbandoned);
|
connect(room, &Room::abandoned, this, &Server::onRoomAbandoned);
|
||||||
if (room->isLobby())
|
if (room->isLobby())
|
||||||
m_lobby = room;
|
m_lobby = room;
|
||||||
else
|
else
|
||||||
rooms.insert(room->getId(), room);
|
rooms.insert(room->getId(), room);
|
||||||
|
|
||||||
room->setName(name);
|
room->setName(name);
|
||||||
room->setCapacity(capacity);
|
room->setCapacity(capacity);
|
||||||
room->addPlayer(owner);
|
room->addPlayer(owner);
|
||||||
if (!room->isLobby()) room->setOwner(owner);
|
if (!room->isLobby()) room->setOwner(owner);
|
||||||
}
|
}
|
||||||
|
|
||||||
Room *Server::findRoom(int id) const
|
Room *Server::findRoom(int id) const
|
||||||
{
|
{
|
||||||
return rooms.value(id);
|
return rooms.value(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
Room *Server::lobby() const
|
Room *Server::lobby() const
|
||||||
{
|
{
|
||||||
return m_lobby;
|
return m_lobby;
|
||||||
}
|
}
|
||||||
|
|
||||||
ServerPlayer *Server::findPlayer(int id) const
|
ServerPlayer *Server::findPlayer(int id) const
|
||||||
{
|
{
|
||||||
return players.value(id);
|
return players.value(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Server::removePlayer(int id) {
|
void Server::removePlayer(int id) {
|
||||||
players.remove(id);
|
players.remove(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Server::updateRoomList()
|
void Server::updateRoomList()
|
||||||
{
|
{
|
||||||
QJsonArray arr;
|
QJsonArray arr;
|
||||||
foreach (Room *room, rooms) {
|
foreach (Room *room, rooms) {
|
||||||
QJsonArray obj;
|
QJsonArray obj;
|
||||||
obj << room->getId(); // roomId
|
obj << room->getId(); // roomId
|
||||||
obj << room->getName(); // roomName
|
obj << room->getName(); // roomName
|
||||||
obj << "Role"; // gameMode
|
obj << "Role"; // gameMode
|
||||||
obj << room->getPlayers().count(); // playerNum
|
obj << room->getPlayers().count(); // playerNum
|
||||||
obj << room->getCapacity(); // capacity
|
obj << room->getCapacity(); // capacity
|
||||||
arr << obj;
|
arr << obj;
|
||||||
}
|
}
|
||||||
lobby()->doBroadcastNotify(
|
lobby()->doBroadcastNotify(
|
||||||
lobby()->getPlayers(),
|
lobby()->getPlayers(),
|
||||||
"UpdateRoomList",
|
"UpdateRoomList",
|
||||||
QJsonDocument(arr).toJson()
|
QJsonDocument(arr).toJson()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
sqlite3 *Server::getDatabase() {
|
sqlite3 *Server::getDatabase() {
|
||||||
return db;
|
return db;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Server::processNewConnection(ClientSocket* client)
|
void Server::processNewConnection(ClientSocket* client)
|
||||||
{
|
{
|
||||||
qDebug() << client->peerAddress() << "connected";
|
qDebug() << client->peerAddress() << "connected";
|
||||||
// version check, file check, ban IP, reconnect, etc
|
// version check, file check, ban IP, reconnect, etc
|
||||||
|
|
||||||
connect(client, &ClientSocket::disconnected, this, [client](){
|
connect(client, &ClientSocket::disconnected, this, [client](){
|
||||||
qDebug() << client->peerAddress() << "disconnected";
|
qDebug() << client->peerAddress() << "disconnected";
|
||||||
});
|
});
|
||||||
|
|
||||||
// network delay test
|
// network delay test
|
||||||
QJsonArray body;
|
QJsonArray body;
|
||||||
body << -2;
|
body << -2;
|
||||||
body << (Router::TYPE_NOTIFICATION | Router::SRC_SERVER | Router::DEST_CLIENT);
|
body << (Router::TYPE_NOTIFICATION | Router::SRC_SERVER | Router::DEST_CLIENT);
|
||||||
body << "NetworkDelayTest";
|
body << "NetworkDelayTest";
|
||||||
body << "[]";
|
body << "[]";
|
||||||
client->send(QJsonDocument(body).toJson(QJsonDocument::Compact));
|
client->send(QJsonDocument(body).toJson(QJsonDocument::Compact));
|
||||||
// Note: the client should send a setup string next
|
// Note: the client should send a setup string next
|
||||||
connect(client, &ClientSocket::message_got, this, &Server::processRequest);
|
connect(client, &ClientSocket::message_got, this, &Server::processRequest);
|
||||||
client->timerSignup.start(30000);
|
client->timerSignup.start(30000);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Server::processRequest(const QByteArray& msg)
|
void Server::processRequest(const QByteArray& msg)
|
||||||
{
|
{
|
||||||
ClientSocket *client = qobject_cast<ClientSocket *>(sender());
|
ClientSocket *client = qobject_cast<ClientSocket *>(sender());
|
||||||
client->disconnect(this, SLOT(processRequest(const QByteArray &)));
|
client->disconnect(this, SLOT(processRequest(const QByteArray &)));
|
||||||
client->timerSignup.stop();
|
client->timerSignup.stop();
|
||||||
|
|
||||||
bool valid = true;
|
bool valid = true;
|
||||||
QJsonDocument doc = QJsonDocument::fromJson(msg);
|
QJsonDocument doc = QJsonDocument::fromJson(msg);
|
||||||
if (doc.isNull() || !doc.isArray()) {
|
if (doc.isNull() || !doc.isArray()) {
|
||||||
valid = false;
|
valid = false;
|
||||||
} else {
|
} else {
|
||||||
if (doc.array().size() != 4
|
if (doc.array().size() != 4
|
||||||
|| doc[0] != -2
|
|| doc[0] != -2
|
||||||
|| doc[1] != (Router::TYPE_NOTIFICATION | Router::SRC_CLIENT | Router::DEST_SERVER)
|
|| doc[1] != (Router::TYPE_NOTIFICATION | Router::SRC_CLIENT | Router::DEST_SERVER)
|
||||||
|| doc[2] != "Setup"
|
|| doc[2] != "Setup"
|
||||||
)
|
)
|
||||||
valid = false;
|
valid = false;
|
||||||
else
|
else
|
||||||
valid = (QJsonDocument::fromJson(doc[3].toString().toUtf8()).array().size() == 2);
|
valid = (QJsonDocument::fromJson(doc[3].toString().toUtf8()).array().size() == 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!valid) {
|
if (!valid) {
|
||||||
qDebug() << "Invalid setup string:" << msg;
|
qDebug() << "Invalid setup string:" << msg;
|
||||||
QJsonArray body;
|
QJsonArray body;
|
||||||
body << -2;
|
body << -2;
|
||||||
body << (Router::TYPE_NOTIFICATION | Router::SRC_SERVER | Router::DEST_CLIENT);
|
body << (Router::TYPE_NOTIFICATION | Router::SRC_SERVER | Router::DEST_CLIENT);
|
||||||
body << "ErrorMsg";
|
body << "ErrorMsg";
|
||||||
body << "INVALID SETUP STRING";
|
body << "INVALID SETUP STRING";
|
||||||
client->send(QJsonDocument(body).toJson(QJsonDocument::Compact));
|
client->send(QJsonDocument(body).toJson(QJsonDocument::Compact));
|
||||||
client->disconnectFromHost();
|
client->disconnectFromHost();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
QJsonArray arr = QJsonDocument::fromJson(doc[3].toString().toUtf8()).array();
|
QJsonArray arr = QJsonDocument::fromJson(doc[3].toString().toUtf8()).array();
|
||||||
handleNameAndPassword(client, arr[0].toString(), arr[1].toString());
|
handleNameAndPassword(client, arr[0].toString(), arr[1].toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
void Server::handleNameAndPassword(ClientSocket *client, const QString& name, const QString& password)
|
void Server::handleNameAndPassword(ClientSocket *client, const QString& name, const QString& password)
|
||||||
{
|
{
|
||||||
// First check the name and password
|
// First check the name and password
|
||||||
// Matches a string that does not contain special characters
|
// Matches a string that does not contain special characters
|
||||||
QRegExp nameExp("[^\\0000-\\0057\\0072-\\0100\\0133-\\0140\\0173-\\0177]+");
|
QRegExp nameExp("[^\\0000-\\0057\\0072-\\0100\\0133-\\0140\\0173-\\0177]+");
|
||||||
QByteArray passwordHash = QCryptographicHash::hash(password.toLatin1(), QCryptographicHash::Sha256).toHex();
|
QByteArray passwordHash = QCryptographicHash::hash(password.toLatin1(), QCryptographicHash::Sha256).toHex();
|
||||||
bool passed = false;
|
bool passed = false;
|
||||||
QString error_msg;
|
QString error_msg;
|
||||||
QJsonObject result;
|
QJsonObject result;
|
||||||
|
|
||||||
if (nameExp.exactMatch(name)) {
|
if (nameExp.exactMatch(name)) {
|
||||||
// Then we check the database,
|
// Then we check the database,
|
||||||
QString sql_find = QString("SELECT * FROM userinfo \
|
QString sql_find = QString("SELECT * FROM userinfo \
|
||||||
WHERE name='%1';").arg(name);
|
WHERE name='%1';").arg(name);
|
||||||
result = SelectFromDatabase(db, sql_find);
|
result = SelectFromDatabase(db, sql_find);
|
||||||
QJsonArray arr = result["password"].toArray();
|
QJsonArray arr = result["password"].toArray();
|
||||||
if (arr.isEmpty()) {
|
if (arr.isEmpty()) {
|
||||||
// not present in database, register
|
// not present in database, register
|
||||||
QString sql_reg = QString("INSERT INTO userinfo (name,password,\
|
QString sql_reg = QString("INSERT INTO userinfo (name,password,\
|
||||||
avatar,lastLoginIp,banned) VALUES ('%1','%2','%3','%4',%5);")
|
avatar,lastLoginIp,banned) VALUES ('%1','%2','%3','%4',%5);")
|
||||||
.arg(name)
|
.arg(name)
|
||||||
.arg(QString(passwordHash))
|
.arg(QString(passwordHash))
|
||||||
.arg("liubei")
|
.arg("liubei")
|
||||||
.arg(client->peerAddress())
|
.arg(client->peerAddress())
|
||||||
.arg("FALSE");
|
.arg("FALSE");
|
||||||
ExecSQL(db, sql_reg);
|
ExecSQL(db, sql_reg);
|
||||||
result = SelectFromDatabase(db, sql_find); // refresh result
|
result = SelectFromDatabase(db, sql_find); // refresh result
|
||||||
passed = true;
|
passed = true;
|
||||||
} else {
|
|
||||||
// check if this username already login
|
|
||||||
int id = result["id"].toArray()[0].toString().toInt();
|
|
||||||
if (!players.value(id)) {
|
|
||||||
// check if password is the same
|
|
||||||
passed = (passwordHash == arr[0].toString());
|
|
||||||
if (!passed) error_msg = "username or password error";
|
|
||||||
} else {
|
|
||||||
// TODO: reconnect here
|
|
||||||
error_msg = "others logged in with this name";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
error_msg = "invalid user name";
|
// check if this username already login
|
||||||
|
int id = result["id"].toArray()[0].toString().toInt();
|
||||||
|
if (!players.value(id)) {
|
||||||
|
// check if password is the same
|
||||||
|
passed = (passwordHash == arr[0].toString());
|
||||||
|
if (!passed) error_msg = "username or password error";
|
||||||
|
} else {
|
||||||
|
// TODO: reconnect here
|
||||||
|
error_msg = "others logged in with this name";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
error_msg = "invalid user name";
|
||||||
|
}
|
||||||
|
|
||||||
if (passed) {
|
if (passed) {
|
||||||
// create new ServerPlayer and setup
|
// create new ServerPlayer and setup
|
||||||
ServerPlayer *player = new ServerPlayer(lobby());
|
ServerPlayer *player = new ServerPlayer(lobby());
|
||||||
player->setSocket(client);
|
player->setSocket(client);
|
||||||
client->disconnect(this);
|
client->disconnect(this);
|
||||||
connect(player, &ServerPlayer::disconnected, this, &Server::onUserDisconnected);
|
connect(player, &ServerPlayer::disconnected, this, &Server::onUserDisconnected);
|
||||||
connect(player, &Player::stateChanged, this, &Server::onUserStateChanged);
|
connect(player, &Player::stateChanged, this, &Server::onUserStateChanged);
|
||||||
player->setScreenName(name);
|
player->setScreenName(name);
|
||||||
player->setAvatar(result["avatar"].toArray()[0].toString());
|
player->setAvatar(result["avatar"].toArray()[0].toString());
|
||||||
player->setId(result["id"].toArray()[0].toString().toInt());
|
player->setId(result["id"].toArray()[0].toString().toInt());
|
||||||
players.insert(player->getId(), player);
|
players.insert(player->getId(), player);
|
||||||
|
|
||||||
// tell the lobby player's basic property
|
// tell the lobby player's basic property
|
||||||
QJsonArray arr;
|
QJsonArray arr;
|
||||||
arr << player->getId();
|
arr << player->getId();
|
||||||
arr << player->getScreenName();
|
arr << player->getScreenName();
|
||||||
arr << player->getAvatar();
|
arr << player->getAvatar();
|
||||||
player->doNotify("Setup", QJsonDocument(arr).toJson());
|
player->doNotify("Setup", QJsonDocument(arr).toJson());
|
||||||
|
|
||||||
lobby()->addPlayer(player);
|
lobby()->addPlayer(player);
|
||||||
} else {
|
} else {
|
||||||
qDebug() << client->peerAddress() << "lost connection:" << error_msg;
|
qDebug() << client->peerAddress() << "lost connection:" << error_msg;
|
||||||
QJsonArray body;
|
QJsonArray body;
|
||||||
body << -2;
|
body << -2;
|
||||||
body << (Router::TYPE_NOTIFICATION | Router::SRC_SERVER | Router::DEST_CLIENT);
|
body << (Router::TYPE_NOTIFICATION | Router::SRC_SERVER | Router::DEST_CLIENT);
|
||||||
body << "ErrorMsg";
|
body << "ErrorMsg";
|
||||||
body << error_msg;
|
body << error_msg;
|
||||||
client->send(QJsonDocument(body).toJson(QJsonDocument::Compact));
|
client->send(QJsonDocument(body).toJson(QJsonDocument::Compact));
|
||||||
client->disconnectFromHost();
|
client->disconnectFromHost();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Server::onRoomAbandoned()
|
void Server::onRoomAbandoned()
|
||||||
{
|
{
|
||||||
Room *room = qobject_cast<Room *>(sender());
|
Room *room = qobject_cast<Room *>(sender());
|
||||||
room->gameOver();
|
room->gameOver();
|
||||||
rooms.remove(room->getId());
|
rooms.remove(room->getId());
|
||||||
updateRoomList();
|
updateRoomList();
|
||||||
room->deleteLater();
|
room->deleteLater();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Server::onUserDisconnected()
|
void Server::onUserDisconnected()
|
||||||
{
|
{
|
||||||
ServerPlayer *player = qobject_cast<ServerPlayer *>(sender());
|
ServerPlayer *player = qobject_cast<ServerPlayer *>(sender());
|
||||||
qDebug() << "Player" << player->getId() << "disconnected";
|
qDebug() << "Player" << player->getId() << "disconnected";
|
||||||
Room *room = player->getRoom();
|
Room *room = player->getRoom();
|
||||||
if (room->isStarted()) {
|
if (room->isStarted()) {
|
||||||
player->setState(Player::Offline);
|
player->setState(Player::Offline);
|
||||||
// TODO: add a robot
|
// TODO: add a robot
|
||||||
} else {
|
} else {
|
||||||
player->deleteLater();
|
player->deleteLater();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Server::onUserStateChanged()
|
void Server::onUserStateChanged()
|
||||||
{
|
{
|
||||||
ServerPlayer *player = qobject_cast<ServerPlayer *>(sender());
|
ServerPlayer *player = qobject_cast<ServerPlayer *>(sender());
|
||||||
QJsonArray arr;
|
QJsonArray arr;
|
||||||
arr << player->getId();
|
arr << player->getId();
|
||||||
arr << player->getStateString();
|
arr << player->getStateString();
|
||||||
player->getRoom()->callLua("PlayerStateChanged", QJsonDocument(arr).toJson());
|
player->getRoom()->callLua("PlayerStateChanged", QJsonDocument(arr).toJson());
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,49 +8,49 @@ class ServerPlayer;
|
||||||
#include "room.h"
|
#include "room.h"
|
||||||
|
|
||||||
class Server : public QObject {
|
class Server : public QObject {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
public:
|
public:
|
||||||
explicit Server(QObject *parent = nullptr);
|
explicit Server(QObject *parent = nullptr);
|
||||||
~Server();
|
~Server();
|
||||||
|
|
||||||
bool listen(const QHostAddress &address = QHostAddress::Any, ushort port = 9527u);
|
bool listen(const QHostAddress &address = QHostAddress::Any, ushort port = 9527u);
|
||||||
|
|
||||||
void createRoom(ServerPlayer *owner, const QString &name, int capacity);
|
void createRoom(ServerPlayer *owner, const QString &name, int capacity);
|
||||||
Room *findRoom(int id) const;
|
Room *findRoom(int id) const;
|
||||||
Room *lobby() const;
|
Room *lobby() const;
|
||||||
|
|
||||||
ServerPlayer *findPlayer(int id) const;
|
ServerPlayer *findPlayer(int id) const;
|
||||||
void removePlayer(int id);
|
void removePlayer(int id);
|
||||||
|
|
||||||
void updateRoomList();
|
void updateRoomList();
|
||||||
|
|
||||||
sqlite3 *getDatabase();
|
sqlite3 *getDatabase();
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void roomCreated(Room *room);
|
void roomCreated(Room *room);
|
||||||
void playerAdded(ServerPlayer *player);
|
void playerAdded(ServerPlayer *player);
|
||||||
void playerRemoved(ServerPlayer *player);
|
void playerRemoved(ServerPlayer *player);
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
void processNewConnection(ClientSocket *client);
|
void processNewConnection(ClientSocket *client);
|
||||||
void processRequest(const QByteArray &msg);
|
void processRequest(const QByteArray &msg);
|
||||||
|
|
||||||
void onRoomAbandoned();
|
void onRoomAbandoned();
|
||||||
void onUserDisconnected();
|
void onUserDisconnected();
|
||||||
void onUserStateChanged();
|
void onUserStateChanged();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
ServerSocket *server;
|
ServerSocket *server;
|
||||||
Room *m_lobby;
|
Room *m_lobby;
|
||||||
QMap<int, Room *> rooms;
|
QMap<int, Room *> rooms;
|
||||||
int nextRoomId;
|
int nextRoomId;
|
||||||
friend Room::Room(Server *server);
|
friend Room::Room(Server *server);
|
||||||
QHash<int, ServerPlayer *> players;
|
QHash<int, ServerPlayer *> players;
|
||||||
|
|
||||||
sqlite3 *db;
|
sqlite3 *db;
|
||||||
|
|
||||||
void handleNameAndPassword(ClientSocket *client, const QString &name, const QString &password);
|
void handleNameAndPassword(ClientSocket *client, const QString &name, const QString &password);
|
||||||
};
|
};
|
||||||
|
|
||||||
extern Server *ServerInstance;
|
extern Server *ServerInstance;
|
||||||
|
|
|
@ -6,111 +6,111 @@
|
||||||
|
|
||||||
ServerPlayer::ServerPlayer(Room *room)
|
ServerPlayer::ServerPlayer(Room *room)
|
||||||
{
|
{
|
||||||
socket = nullptr;
|
socket = nullptr;
|
||||||
router = new Router(this, socket, Router::TYPE_SERVER);
|
router = new Router(this, socket, Router::TYPE_SERVER);
|
||||||
setState(Player::Online);
|
setState(Player::Online);
|
||||||
this->room = room;
|
this->room = room;
|
||||||
server = room->getServer();
|
server = room->getServer();
|
||||||
}
|
}
|
||||||
|
|
||||||
ServerPlayer::~ServerPlayer()
|
ServerPlayer::~ServerPlayer()
|
||||||
{
|
{
|
||||||
// clean up, quit room and server
|
// clean up, quit room and server
|
||||||
|
room->removePlayer(this);
|
||||||
|
if (room != nullptr) {
|
||||||
|
// now we are in lobby, so quit lobby
|
||||||
room->removePlayer(this);
|
room->removePlayer(this);
|
||||||
if (room != nullptr) {
|
}
|
||||||
// now we are in lobby, so quit lobby
|
server->removePlayer(getId());
|
||||||
room->removePlayer(this);
|
router->deleteLater();
|
||||||
}
|
|
||||||
server->removePlayer(getId());
|
|
||||||
router->deleteLater();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void ServerPlayer::setSocket(ClientSocket *socket)
|
void ServerPlayer::setSocket(ClientSocket *socket)
|
||||||
{
|
{
|
||||||
if (this->socket != nullptr) {
|
if (this->socket != nullptr) {
|
||||||
this->socket->disconnect(this);
|
this->socket->disconnect(this);
|
||||||
disconnect(this->socket);
|
disconnect(this->socket);
|
||||||
this->socket->deleteLater();
|
this->socket->deleteLater();
|
||||||
}
|
}
|
||||||
|
|
||||||
this->socket = nullptr;
|
this->socket = nullptr;
|
||||||
if (socket != nullptr) {
|
if (socket != nullptr) {
|
||||||
connect(socket, &ClientSocket::disconnected, this, &ServerPlayer::disconnected);
|
connect(socket, &ClientSocket::disconnected, this, &ServerPlayer::disconnected);
|
||||||
this->socket = socket;
|
this->socket = socket;
|
||||||
}
|
}
|
||||||
|
|
||||||
router->setSocket(socket);
|
router->setSocket(socket);
|
||||||
}
|
}
|
||||||
|
|
||||||
Server *ServerPlayer::getServer() const
|
Server *ServerPlayer::getServer() const
|
||||||
{
|
{
|
||||||
return server;
|
return server;
|
||||||
}
|
}
|
||||||
|
|
||||||
Room *ServerPlayer::getRoom() const
|
Room *ServerPlayer::getRoom() const
|
||||||
{
|
{
|
||||||
return room;
|
return room;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ServerPlayer::setRoom(Room* room)
|
void ServerPlayer::setRoom(Room* room)
|
||||||
{
|
{
|
||||||
this->room = room;
|
this->room = room;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ServerPlayer::speak(const QString& message)
|
void ServerPlayer::speak(const QString& message)
|
||||||
{
|
{
|
||||||
;
|
;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ServerPlayer::doRequest(const QString& command, const QString& jsonData, int timeout)
|
void ServerPlayer::doRequest(const QString& command, const QString& jsonData, int timeout)
|
||||||
{
|
{
|
||||||
if (getState() != Player::Online) return;
|
if (getState() != Player::Online) return;
|
||||||
int type = Router::TYPE_REQUEST | Router::SRC_SERVER | Router::DEST_CLIENT;
|
int type = Router::TYPE_REQUEST | Router::SRC_SERVER | Router::DEST_CLIENT;
|
||||||
router->request(type, command, jsonData, timeout);
|
router->request(type, command, jsonData, timeout);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ServerPlayer::abortRequest()
|
void ServerPlayer::abortRequest()
|
||||||
{
|
{
|
||||||
router->abortRequest();
|
router->abortRequest();
|
||||||
}
|
}
|
||||||
|
|
||||||
QString ServerPlayer::waitForReply()
|
QString ServerPlayer::waitForReply()
|
||||||
{
|
{
|
||||||
room->unlockLua(__FUNCTION__);
|
room->unlockLua(__FUNCTION__);
|
||||||
QString ret;
|
QString ret;
|
||||||
if (getState() != Player::Online) {
|
if (getState() != Player::Online) {
|
||||||
QThread::sleep(1);
|
QThread::sleep(1);
|
||||||
ret = "";
|
ret = "";
|
||||||
} else {
|
} else {
|
||||||
ret = router->waitForReply();
|
ret = router->waitForReply();
|
||||||
}
|
}
|
||||||
room->lockLua(__FUNCTION__);
|
room->lockLua(__FUNCTION__);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
QString ServerPlayer::waitForReply(int timeout)
|
QString ServerPlayer::waitForReply(int timeout)
|
||||||
{
|
{
|
||||||
room->unlockLua(__FUNCTION__);
|
room->unlockLua(__FUNCTION__);
|
||||||
QString ret;
|
QString ret;
|
||||||
if (getState() != Player::Online) {
|
if (getState() != Player::Online) {
|
||||||
QThread::sleep(1);
|
QThread::sleep(1);
|
||||||
ret = "";
|
ret = "";
|
||||||
} else {
|
} else {
|
||||||
ret = router->waitForReply(timeout);
|
ret = router->waitForReply(timeout);
|
||||||
}
|
}
|
||||||
room->lockLua(__FUNCTION__);
|
room->lockLua(__FUNCTION__);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ServerPlayer::doNotify(const QString& command, const QString& jsonData)
|
void ServerPlayer::doNotify(const QString& command, const QString& jsonData)
|
||||||
{
|
{
|
||||||
if (getState() != Player::Online) return;
|
if (getState() != Player::Online) return;
|
||||||
int type = Router::TYPE_NOTIFICATION | Router::SRC_SERVER | Router::DEST_CLIENT;
|
int type = Router::TYPE_NOTIFICATION | Router::SRC_SERVER | Router::DEST_CLIENT;
|
||||||
router->notify(type, command, jsonData);
|
router->notify(type, command, jsonData);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ServerPlayer::prepareForRequest(const QString& command, const QString& data)
|
void ServerPlayer::prepareForRequest(const QString& command, const QString& data)
|
||||||
{
|
{
|
||||||
requestCommand = command;
|
requestCommand = command;
|
||||||
requestData = data;
|
requestData = data;
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,40 +9,40 @@ class Server;
|
||||||
class Room;
|
class Room;
|
||||||
|
|
||||||
class ServerPlayer : public Player {
|
class ServerPlayer : public Player {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
public:
|
public:
|
||||||
explicit ServerPlayer(Room *room);
|
explicit ServerPlayer(Room *room);
|
||||||
~ServerPlayer();
|
~ServerPlayer();
|
||||||
|
|
||||||
void setSocket(ClientSocket *socket);
|
void setSocket(ClientSocket *socket);
|
||||||
|
|
||||||
Server *getServer() const;
|
Server *getServer() const;
|
||||||
Room *getRoom() const;
|
Room *getRoom() const;
|
||||||
void setRoom(Room *room);
|
void setRoom(Room *room);
|
||||||
|
|
||||||
void speak(const QString &message);
|
void speak(const QString &message);
|
||||||
|
|
||||||
void doRequest(const QString &command,
|
void doRequest(const QString &command,
|
||||||
const QString &jsonData, int timeout = -1);
|
const QString &jsonData, int timeout = -1);
|
||||||
void abortRequest();
|
void abortRequest();
|
||||||
QString waitForReply(int timeout);
|
QString waitForReply(int timeout);
|
||||||
QString waitForReply();
|
QString waitForReply();
|
||||||
void doNotify(const QString &command, const QString &jsonData);
|
void doNotify(const QString &command, const QString &jsonData);
|
||||||
|
|
||||||
void prepareForRequest(const QString &command,
|
void prepareForRequest(const QString &command,
|
||||||
const QString &data);
|
const QString &data);
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void disconnected();
|
void disconnected();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
ClientSocket *socket; // socket for communicating with client
|
ClientSocket *socket; // socket for communicating with client
|
||||||
Router *router;
|
Router *router;
|
||||||
Server *server;
|
Server *server;
|
||||||
Room *room; // Room that player is in, maybe lobby
|
Room *room; // Room that player is in, maybe lobby
|
||||||
|
|
||||||
QString requestCommand;
|
QString requestCommand;
|
||||||
QString requestData;
|
QString requestData;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // _SERVERPLAYER_H
|
#endif // _SERVERPLAYER_H
|
||||||
|
|
|
@ -2,13 +2,13 @@
|
||||||
%nodefaultdtor QmlBackend;
|
%nodefaultdtor QmlBackend;
|
||||||
class QmlBackend : public QObject {
|
class QmlBackend : public QObject {
|
||||||
public:
|
public:
|
||||||
void emitNotifyUI(const QString &command, const QString &json_data);
|
void emitNotifyUI(const QString &command, const QString &json_data);
|
||||||
|
|
||||||
static void cd(const QString &path);
|
static void cd(const QString &path);
|
||||||
static QStringList ls(const QString &dir);
|
static QStringList ls(const QString &dir);
|
||||||
static QString pwd();
|
static QString pwd();
|
||||||
static bool exists(const QString &file);
|
static bool exists(const QString &file);
|
||||||
static bool isDir(const QString &file);
|
static bool isDir(const QString &file);
|
||||||
};
|
};
|
||||||
|
|
||||||
extern QmlBackend *Backend;
|
extern QmlBackend *Backend;
|
||||||
|
@ -17,13 +17,13 @@ extern QmlBackend *Backend;
|
||||||
%nodefaultdtor Client;
|
%nodefaultdtor Client;
|
||||||
class Client : public QObject {
|
class Client : public QObject {
|
||||||
public:
|
public:
|
||||||
void replyToServer(const QString &command, const QString &json_data);
|
void replyToServer(const QString &command, const QString &json_data);
|
||||||
void notifyServer(const QString &command, const QString &json_data);
|
void notifyServer(const QString &command, const QString &json_data);
|
||||||
|
|
||||||
LuaFunction callback;
|
LuaFunction callback;
|
||||||
|
|
||||||
ClientPlayer *addPlayer(int id, const QString &name, const QString &avatar);
|
ClientPlayer *addPlayer(int id, const QString &name, const QString &avatar);
|
||||||
void removePlayer(int id);
|
void removePlayer(int id);
|
||||||
};
|
};
|
||||||
|
|
||||||
extern Client *ClientInstance;
|
extern Client *ClientInstance;
|
||||||
|
@ -31,24 +31,24 @@ extern Client *ClientInstance;
|
||||||
%{
|
%{
|
||||||
void Client::callLua(const QString& command, const QString& json_data)
|
void Client::callLua(const QString& command, const QString& json_data)
|
||||||
{
|
{
|
||||||
Q_ASSERT(callback);
|
Q_ASSERT(callback);
|
||||||
|
|
||||||
lua_getglobal(L, "debug");
|
lua_getglobal(L, "debug");
|
||||||
lua_getfield(L, -1, "traceback");
|
lua_getfield(L, -1, "traceback");
|
||||||
lua_replace(L, -2);
|
lua_replace(L, -2);
|
||||||
|
|
||||||
lua_rawgeti(L, LUA_REGISTRYINDEX, callback);
|
lua_rawgeti(L, LUA_REGISTRYINDEX, callback);
|
||||||
SWIG_NewPointerObj(L, this, SWIGTYPE_p_Client, 0);
|
SWIG_NewPointerObj(L, this, SWIGTYPE_p_Client, 0);
|
||||||
lua_pushstring(L, command.toUtf8());
|
lua_pushstring(L, command.toUtf8());
|
||||||
lua_pushstring(L, json_data.toUtf8());
|
lua_pushstring(L, json_data.toUtf8());
|
||||||
|
|
||||||
int error = lua_pcall(L, 3, 0, -5);
|
int error = lua_pcall(L, 3, 0, -5);
|
||||||
|
|
||||||
if (error) {
|
if (error) {
|
||||||
const char *error_msg = lua_tostring(L, -1);
|
const char *error_msg = lua_tostring(L, -1);
|
||||||
qDebug() << error_msg;
|
qDebug() << error_msg;
|
||||||
lua_pop(L, 2);
|
lua_pop(L, 2);
|
||||||
}
|
}
|
||||||
lua_pop(L, 1);
|
lua_pop(L, 1);
|
||||||
}
|
}
|
||||||
%}
|
%}
|
||||||
|
|
|
@ -7,10 +7,10 @@
|
||||||
%typemap(in) LuaFunction
|
%typemap(in) LuaFunction
|
||||||
%{
|
%{
|
||||||
if (lua_isfunction(L, $input)) {
|
if (lua_isfunction(L, $input)) {
|
||||||
lua_pushvalue(L, $input);
|
lua_pushvalue(L, $input);
|
||||||
$1 = luaL_ref(L, LUA_REGISTRYINDEX);
|
$1 = luaL_ref(L, LUA_REGISTRYINDEX);
|
||||||
} else {
|
} else {
|
||||||
$1 = 0;
|
$1 = 0;
|
||||||
}
|
}
|
||||||
%}
|
%}
|
||||||
|
|
||||||
|
@ -35,8 +35,8 @@ SWIG_arg ++;
|
||||||
|
|
||||||
%typemap(in, checkfn = "lua_isstring") QString const &
|
%typemap(in, checkfn = "lua_isstring") QString const &
|
||||||
%{
|
%{
|
||||||
$1_str = QString::fromUtf8(lua_tostring(L, $input));
|
$1_str = QString::fromUtf8(lua_tostring(L, $input));
|
||||||
$1 = &$1_str;
|
$1 = &$1_str;
|
||||||
%}
|
%}
|
||||||
|
|
||||||
%typemap(out) QString const &
|
%typemap(out) QString const &
|
||||||
|
@ -48,10 +48,10 @@ SWIG_arg ++;
|
||||||
%typemap(in, checkfn = "lua_istable") QStringList
|
%typemap(in, checkfn = "lua_istable") QStringList
|
||||||
%{
|
%{
|
||||||
for (size_t i = 0; i < lua_rawlen(L, $input); ++i) {
|
for (size_t i = 0; i < lua_rawlen(L, $input); ++i) {
|
||||||
lua_rawgeti(L, $input, i + 1);
|
lua_rawgeti(L, $input, i + 1);
|
||||||
const char *elem = luaL_checkstring(L, -1);
|
const char *elem = luaL_checkstring(L, -1);
|
||||||
$1 << QString::fromUtf8(QByteArray(elem));
|
$1 << QString::fromUtf8(QByteArray(elem));
|
||||||
lua_pop(L, 1);
|
lua_pop(L, 1);
|
||||||
}
|
}
|
||||||
%}
|
%}
|
||||||
|
|
||||||
|
@ -60,9 +60,9 @@ for (size_t i = 0; i < lua_rawlen(L, $input); ++i) {
|
||||||
lua_createtable(L, $1.length(), 0);
|
lua_createtable(L, $1.length(), 0);
|
||||||
|
|
||||||
for (int i = 0; i < $1.length(); i++) {
|
for (int i = 0; i < $1.length(); i++) {
|
||||||
QString str = $1.at(i);
|
QString str = $1.at(i);
|
||||||
lua_pushstring(L, str.toUtf8().constData());
|
lua_pushstring(L, str.toUtf8().constData());
|
||||||
lua_rawseti(L, -2, i + 1);
|
lua_rawseti(L, -2, i + 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
SWIG_arg++;
|
SWIG_arg++;
|
||||||
|
@ -70,7 +70,7 @@ SWIG_arg++;
|
||||||
|
|
||||||
%typemap(typecheck) QStringList
|
%typemap(typecheck) QStringList
|
||||||
%{
|
%{
|
||||||
$1 = lua_istable(L, $input) ? 1 : 0;
|
$1 = lua_istable(L, $input) ? 1 : 0;
|
||||||
%}
|
%}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -2,29 +2,29 @@
|
||||||
%nodefaultdtor Player;
|
%nodefaultdtor Player;
|
||||||
class Player : public QObject {
|
class Player : public QObject {
|
||||||
public:
|
public:
|
||||||
enum State{
|
enum State{
|
||||||
Invalid,
|
Invalid,
|
||||||
Online,
|
Online,
|
||||||
Trust,
|
Trust,
|
||||||
Offline
|
Offline
|
||||||
};
|
};
|
||||||
|
|
||||||
int getId() const;
|
int getId() const;
|
||||||
void setId(int id);
|
void setId(int id);
|
||||||
|
|
||||||
QString getScreenName() const;
|
QString getScreenName() const;
|
||||||
void setScreenName(const QString &name);
|
void setScreenName(const QString &name);
|
||||||
|
|
||||||
QString getAvatar() const;
|
QString getAvatar() const;
|
||||||
void setAvatar(const QString &avatar);
|
void setAvatar(const QString &avatar);
|
||||||
|
|
||||||
State getState() const;
|
State getState() const;
|
||||||
QString getStateString() const;
|
QString getStateString() const;
|
||||||
void setState(State state);
|
void setState(State state);
|
||||||
void setStateString(const QString &state);
|
void setStateString(const QString &state);
|
||||||
|
|
||||||
bool isReady() const;
|
bool isReady() const;
|
||||||
void setReady(bool ready);
|
void setReady(bool ready);
|
||||||
};
|
};
|
||||||
|
|
||||||
%nodefaultctor ClientPlayer;
|
%nodefaultctor ClientPlayer;
|
||||||
|
@ -39,17 +39,17 @@ extern ClientPlayer *Self;
|
||||||
%nodefaultdtor ServerPlayer;
|
%nodefaultdtor ServerPlayer;
|
||||||
class ServerPlayer : public Player {
|
class ServerPlayer : public Player {
|
||||||
public:
|
public:
|
||||||
Server *getServer() const;
|
Server *getServer() const;
|
||||||
Room *getRoom() const;
|
Room *getRoom() const;
|
||||||
void setRoom(Room *room);
|
void setRoom(Room *room);
|
||||||
|
|
||||||
void speak(const QString &message);
|
void speak(const QString &message);
|
||||||
|
|
||||||
void doRequest(const QString &command,
|
void doRequest(const QString &command,
|
||||||
const QString &json_data, int timeout);
|
const QString &json_data, int timeout);
|
||||||
QString waitForReply();
|
QString waitForReply();
|
||||||
QString waitForReply(int timeout);
|
QString waitForReply(int timeout);
|
||||||
void doNotify(const QString &command, const QString &json_data);
|
void doNotify(const QString &command, const QString &json_data);
|
||||||
|
|
||||||
void prepareForRequest(const QString &command, const QString &data);
|
void prepareForRequest(const QString &command, const QString &data);
|
||||||
};
|
};
|
||||||
|
|
|
@ -5,29 +5,29 @@ class QThread {};
|
||||||
template <class T>
|
template <class T>
|
||||||
class QList {
|
class QList {
|
||||||
public:
|
public:
|
||||||
QList();
|
QList();
|
||||||
~QList();
|
~QList();
|
||||||
int length() const;
|
int length() const;
|
||||||
void append(const T &elem);
|
void append(const T &elem);
|
||||||
void prepend(const T &elem);
|
void prepend(const T &elem);
|
||||||
bool isEmpty() const;
|
bool isEmpty() const;
|
||||||
bool contains(const T &value) const;
|
bool contains(const T &value) const;
|
||||||
T first() const;
|
T first() const;
|
||||||
T last() const;
|
T last() const;
|
||||||
void removeAt(int i);
|
void removeAt(int i);
|
||||||
int removeAll(const T &value);
|
int removeAll(const T &value);
|
||||||
bool removeOne(const T &value);
|
bool removeOne(const T &value);
|
||||||
QList<T> mid(int pos, int length = -1) const;
|
QList<T> mid(int pos, int length = -1) const;
|
||||||
int indexOf(const T &value, int from = 0);
|
int indexOf(const T &value, int from = 0);
|
||||||
void replace(int i, const T &value);
|
void replace(int i, const T &value);
|
||||||
void swapItemsAt(int i, int j);
|
void swapItemsAt(int i, int j);
|
||||||
};
|
};
|
||||||
|
|
||||||
%extend QList {
|
%extend QList {
|
||||||
T at(int i) const
|
T at(int i) const
|
||||||
{
|
{
|
||||||
return $self->value(i);
|
return $self->value(i);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
%template(SPlayerList) QList<ServerPlayer *>;
|
%template(SPlayerList) QList<ServerPlayer *>;
|
||||||
|
@ -39,10 +39,10 @@ public:
|
||||||
%{
|
%{
|
||||||
#include <sys/time.h>
|
#include <sys/time.h>
|
||||||
static int GetMicroSecond(lua_State *L) {
|
static int GetMicroSecond(lua_State *L) {
|
||||||
struct timeval tv;
|
struct timeval tv;
|
||||||
gettimeofday(&tv, nullptr);
|
gettimeofday(&tv, nullptr);
|
||||||
long microsecond = tv.tv_sec * 1000000 + tv.tv_usec;
|
long microsecond = tv.tv_sec * 1000000 + tv.tv_usec;
|
||||||
lua_pushnumber(L, microsecond);
|
lua_pushnumber(L, microsecond);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
%}
|
%}
|
||||||
|
|
|
@ -2,12 +2,12 @@
|
||||||
%nodefaultdtor Server;
|
%nodefaultdtor Server;
|
||||||
class Server : public QObject {
|
class Server : public QObject {
|
||||||
public:
|
public:
|
||||||
Room *lobby() const;
|
Room *lobby() const;
|
||||||
void createRoom(ServerPlayer *owner, const QString &name, int capacity);
|
void createRoom(ServerPlayer *owner, const QString &name, int capacity);
|
||||||
Room *findRoom(int id) const;
|
Room *findRoom(int id) const;
|
||||||
ServerPlayer *findPlayer(int id) const;
|
ServerPlayer *findPlayer(int id) const;
|
||||||
|
|
||||||
sqlite3 *getDatabase();
|
sqlite3 *getDatabase();
|
||||||
};
|
};
|
||||||
|
|
||||||
extern Server *ServerInstance;
|
extern Server *ServerInstance;
|
||||||
|
@ -16,104 +16,104 @@ extern Server *ServerInstance;
|
||||||
%nodefaultdtor Room;
|
%nodefaultdtor Room;
|
||||||
class Room : public QThread {
|
class Room : public QThread {
|
||||||
public:
|
public:
|
||||||
// Property reader & setter
|
// Property reader & setter
|
||||||
// ==================================={
|
// ==================================={
|
||||||
Server *getServer() const;
|
Server *getServer() const;
|
||||||
int getId() const;
|
int getId() const;
|
||||||
bool isLobby() const;
|
bool isLobby() const;
|
||||||
QString getName() const;
|
QString getName() const;
|
||||||
void setName(const QString &name);
|
void setName(const QString &name);
|
||||||
int getCapacity() const;
|
int getCapacity() const;
|
||||||
void setCapacity(int capacity);
|
void setCapacity(int capacity);
|
||||||
bool isFull() const;
|
bool isFull() const;
|
||||||
bool isAbandoned() const;
|
bool isAbandoned() const;
|
||||||
|
|
||||||
ServerPlayer *getOwner() const;
|
ServerPlayer *getOwner() const;
|
||||||
void setOwner(ServerPlayer *owner);
|
void setOwner(ServerPlayer *owner);
|
||||||
|
|
||||||
void addPlayer(ServerPlayer *player);
|
void addPlayer(ServerPlayer *player);
|
||||||
void addRobot(ServerPlayer *player);
|
void addRobot(ServerPlayer *player);
|
||||||
void removePlayer(ServerPlayer *player);
|
void removePlayer(ServerPlayer *player);
|
||||||
QList<ServerPlayer *> getPlayers() const;
|
QList<ServerPlayer *> getPlayers() const;
|
||||||
ServerPlayer *findPlayer(int id) const;
|
ServerPlayer *findPlayer(int id) const;
|
||||||
|
|
||||||
int getTimeout() const;
|
int getTimeout() const;
|
||||||
|
|
||||||
bool isStarted() const;
|
bool isStarted() const;
|
||||||
// ====================================}
|
// ====================================}
|
||||||
|
|
||||||
void doRequest(const QList<ServerPlayer *> targets, int timeout);
|
void doRequest(const QList<ServerPlayer *> targets, int timeout);
|
||||||
void doNotify(const QList<ServerPlayer *> targets, int timeout);
|
void doNotify(const QList<ServerPlayer *> targets, int timeout);
|
||||||
void doBroadcastNotify(
|
void doBroadcastNotify(
|
||||||
const QList<ServerPlayer *> targets,
|
const QList<ServerPlayer *> targets,
|
||||||
const QString &command,
|
const QString &command,
|
||||||
const QString &jsonData
|
const QString &jsonData
|
||||||
);
|
);
|
||||||
|
|
||||||
void gameOver();
|
void gameOver();
|
||||||
|
|
||||||
LuaFunction callback;
|
LuaFunction callback;
|
||||||
LuaFunction startGame;
|
LuaFunction startGame;
|
||||||
};
|
};
|
||||||
|
|
||||||
%{
|
%{
|
||||||
void Room::initLua()
|
void Room::initLua()
|
||||||
{
|
{
|
||||||
lua_getglobal(L, "debug");
|
lua_getglobal(L, "debug");
|
||||||
lua_getfield(L, -1, "traceback");
|
lua_getfield(L, -1, "traceback");
|
||||||
lua_replace(L, -2);
|
lua_replace(L, -2);
|
||||||
lua_getglobal(L, "CreateRoom");
|
lua_getglobal(L, "CreateRoom");
|
||||||
SWIG_NewPointerObj(L, this, SWIGTYPE_p_Room, 0);
|
SWIG_NewPointerObj(L, this, SWIGTYPE_p_Room, 0);
|
||||||
int error = lua_pcall(L, 1, 0, -2);
|
int error = lua_pcall(L, 1, 0, -2);
|
||||||
lua_pop(L, 1);
|
lua_pop(L, 1);
|
||||||
if (error) {
|
if (error) {
|
||||||
const char *error_msg = lua_tostring(L, -1);
|
const char *error_msg = lua_tostring(L, -1);
|
||||||
qDebug() << error_msg;
|
qDebug() << error_msg;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Room::callLua(const QString& command, const QString& json_data)
|
void Room::callLua(const QString& command, const QString& json_data)
|
||||||
{
|
{
|
||||||
Q_ASSERT(callback);
|
Q_ASSERT(callback);
|
||||||
|
|
||||||
lua_getglobal(L, "debug");
|
lua_getglobal(L, "debug");
|
||||||
lua_getfield(L, -1, "traceback");
|
lua_getfield(L, -1, "traceback");
|
||||||
lua_replace(L, -2);
|
lua_replace(L, -2);
|
||||||
|
|
||||||
lua_rawgeti(L, LUA_REGISTRYINDEX, callback);
|
lua_rawgeti(L, LUA_REGISTRYINDEX, callback);
|
||||||
SWIG_NewPointerObj(L, this, SWIGTYPE_p_Room, 0);
|
SWIG_NewPointerObj(L, this, SWIGTYPE_p_Room, 0);
|
||||||
lua_pushstring(L, command.toUtf8());
|
lua_pushstring(L, command.toUtf8());
|
||||||
lua_pushstring(L, json_data.toUtf8());
|
lua_pushstring(L, json_data.toUtf8());
|
||||||
|
|
||||||
int error = lua_pcall(L, 3, 0, -5);
|
int error = lua_pcall(L, 3, 0, -5);
|
||||||
|
|
||||||
if (error) {
|
if (error) {
|
||||||
const char *error_msg = lua_tostring(L, -1);
|
const char *error_msg = lua_tostring(L, -1);
|
||||||
qDebug() << error_msg;
|
qDebug() << error_msg;
|
||||||
lua_pop(L, 2);
|
lua_pop(L, 2);
|
||||||
}
|
}
|
||||||
lua_pop(L, 1);
|
lua_pop(L, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Room::roomStart() {
|
void Room::roomStart() {
|
||||||
Q_ASSERT(startGame);
|
Q_ASSERT(startGame);
|
||||||
|
|
||||||
lua_getglobal(L, "debug");
|
lua_getglobal(L, "debug");
|
||||||
lua_getfield(L, -1, "traceback");
|
lua_getfield(L, -1, "traceback");
|
||||||
lua_replace(L, -2);
|
lua_replace(L, -2);
|
||||||
|
|
||||||
lua_rawgeti(L, LUA_REGISTRYINDEX, startGame);
|
lua_rawgeti(L, LUA_REGISTRYINDEX, startGame);
|
||||||
SWIG_NewPointerObj(L, this, SWIGTYPE_p_Room, 0);
|
SWIG_NewPointerObj(L, this, SWIGTYPE_p_Room, 0);
|
||||||
|
|
||||||
int error = lua_pcall(L, 1, 0, -3);
|
int error = lua_pcall(L, 1, 0, -3);
|
||||||
|
|
||||||
if (error) {
|
if (error) {
|
||||||
const char *error_msg = lua_tostring(L, -1);
|
const char *error_msg = lua_tostring(L, -1);
|
||||||
qDebug() << error_msg;
|
qDebug() << error_msg;
|
||||||
lua_pop(L, 2);
|
lua_pop(L, 2);
|
||||||
}
|
}
|
||||||
lua_pop(L, 1);
|
lua_pop(L, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
%}
|
%}
|
||||||
|
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user