mirror of
https://github.com/Qsgs-Fans/FreeKill.git
synced 2024-11-16 03:32:34 +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)
|
||||
|
||||
find_package(Qt5 REQUIRED COMPONENTS
|
||||
Gui
|
||||
Qml
|
||||
Network
|
||||
Multimedia
|
||||
Gui
|
||||
Qml
|
||||
Network
|
||||
Multimedia
|
||||
)
|
||||
|
||||
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
|
||||
ClientPlayer = require "client.clientplayer"
|
||||
dofile "lua/client/client_util.lua"
|
||||
|
||||
fk.client_callback = {}
|
||||
|
||||
function Client:initialize()
|
||||
self.client = fk.ClientInstance
|
||||
self.notifyUI = function(self, command, jsonData)
|
||||
fk.Backend:emitNotifyUI(command, jsonData)
|
||||
end
|
||||
self.client.callback = function(_self, command, jsonData)
|
||||
local cb = fk.client_callback[command]
|
||||
if (type(cb) == "function") then
|
||||
cb(jsonData)
|
||||
else
|
||||
self:notifyUI(command, jsonData);
|
||||
end
|
||||
self.client = fk.ClientInstance
|
||||
self.notifyUI = function(self, command, jsonData)
|
||||
fk.Backend:emitNotifyUI(command, jsonData)
|
||||
end
|
||||
self.client.callback = function(_self, command, jsonData)
|
||||
local cb = fk.client_callback[command]
|
||||
if (type(cb) == "function") then
|
||||
cb(jsonData)
|
||||
else
|
||||
self:notifyUI(command, jsonData);
|
||||
end
|
||||
end
|
||||
|
||||
self.players = {} -- ClientPlayer[]
|
||||
self.players = {} -- ClientPlayer[]
|
||||
end
|
||||
|
||||
---@param id integer
|
||||
---@return ClientPlayer
|
||||
function Client:findPlayer(id)
|
||||
for _, p in ipairs(self.players) do
|
||||
if p.player:getId() == id then return p end
|
||||
end
|
||||
return nil
|
||||
for _, p in ipairs(self.players) do
|
||||
if p.player:getId() == id then return p end
|
||||
end
|
||||
return nil
|
||||
end
|
||||
|
||||
fk.client_callback["Setup"] = function(jsonData)
|
||||
-- jsonData: [ int id, string screenName, string avatar ]
|
||||
local data = json.decode(jsonData)
|
||||
local id, name, avatar = data[1], data[2], data[3]
|
||||
local self = fk.Self
|
||||
self:setId(id)
|
||||
self:setScreenName(name)
|
||||
self:setAvatar(avatar)
|
||||
Self = ClientPlayer:new(fk.Self)
|
||||
table.insert(ClientInstance.players, Self)
|
||||
-- jsonData: [ int id, string screenName, string avatar ]
|
||||
local data = json.decode(jsonData)
|
||||
local id, name, avatar = data[1], data[2], data[3]
|
||||
local self = fk.Self
|
||||
self:setId(id)
|
||||
self:setScreenName(name)
|
||||
self:setAvatar(avatar)
|
||||
Self = ClientPlayer:new(fk.Self)
|
||||
table.insert(ClientInstance.players, Self)
|
||||
end
|
||||
|
||||
fk.client_callback["AddPlayer"] = function(jsonData)
|
||||
-- jsonData: [ int id, string screenName, string avatar ]
|
||||
-- when other player enter the room, we create clientplayer(C and lua) for them
|
||||
local data = json.decode(jsonData)
|
||||
local id, name, avatar = data[1], data[2], data[3]
|
||||
local player = fk.ClientInstance:addPlayer(id, name, avatar)
|
||||
table.insert(ClientInstance.players, ClientPlayer:new(player))
|
||||
ClientInstance:notifyUI("AddPlayer", jsonData)
|
||||
-- jsonData: [ int id, string screenName, string avatar ]
|
||||
-- when other player enter the room, we create clientplayer(C and lua) for them
|
||||
local data = json.decode(jsonData)
|
||||
local id, name, avatar = data[1], data[2], data[3]
|
||||
local player = fk.ClientInstance:addPlayer(id, name, avatar)
|
||||
table.insert(ClientInstance.players, ClientPlayer:new(player))
|
||||
ClientInstance:notifyUI("AddPlayer", jsonData)
|
||||
end
|
||||
|
||||
fk.client_callback["RemovePlayer"] = function(jsonData)
|
||||
-- jsonData: [ int id ]
|
||||
local data = json.decode(jsonData)
|
||||
local id = data[1]
|
||||
for _, p in ipairs(ClientInstance.players) do
|
||||
if p.player:getId() == id then
|
||||
table.removeOne(ClientInstance.players, p)
|
||||
break
|
||||
end
|
||||
-- jsonData: [ int id ]
|
||||
local data = json.decode(jsonData)
|
||||
local id = data[1]
|
||||
for _, p in ipairs(ClientInstance.players) do
|
||||
if p.player:getId() == id then
|
||||
table.removeOne(ClientInstance.players, p)
|
||||
break
|
||||
end
|
||||
fk.ClientInstance:removePlayer(id)
|
||||
ClientInstance:notifyUI("RemovePlayer", jsonData)
|
||||
end
|
||||
fk.ClientInstance:removePlayer(id)
|
||||
ClientInstance:notifyUI("RemovePlayer", jsonData)
|
||||
end
|
||||
|
||||
fk.client_callback["ArrangeSeats"] = function(jsonData)
|
||||
local data = json.decode(jsonData)
|
||||
local n = #ClientInstance.players
|
||||
local players = {}
|
||||
local data = json.decode(jsonData)
|
||||
local n = #ClientInstance.players
|
||||
local players = {}
|
||||
|
||||
for i = 1, n do
|
||||
table.insert(players, ClientInstance:findPlayer(data[i]))
|
||||
end
|
||||
ClientInstance.players = players
|
||||
for i = 1, n do
|
||||
table.insert(players, ClientInstance:findPlayer(data[i]))
|
||||
end
|
||||
ClientInstance.players = players
|
||||
|
||||
ClientInstance:notifyUI("ArrangeSeats", jsonData)
|
||||
ClientInstance:notifyUI("ArrangeSeats", jsonData)
|
||||
end
|
||||
|
||||
fk.client_callback["PropertyUpdate"] = function(jsonData)
|
||||
-- jsonData: [ int id, string property_name, value ]
|
||||
local data = json.decode(jsonData)
|
||||
local id, name, value = data[1], data[2], data[3]
|
||||
ClientInstance:findPlayer(id)[name] = value
|
||||
ClientInstance:notifyUI("PropertyUpdate", jsonData)
|
||||
-- jsonData: [ int id, string property_name, value ]
|
||||
local data = json.decode(jsonData)
|
||||
local id, name, value = data[1], data[2], data[3]
|
||||
ClientInstance:findPlayer(id)[name] = value
|
||||
ClientInstance:notifyUI("PropertyUpdate", jsonData)
|
||||
end
|
||||
|
||||
--- separated moves to many moves(one card per move)
|
||||
---@param moves CardsMoveStruct[]
|
||||
local function separateMoves(moves)
|
||||
local ret = {} ---@type CardsMoveInfo[]
|
||||
for _, move in ipairs(moves) do
|
||||
for _, info in ipairs(move.moveInfo) do
|
||||
table.insert(ret, {
|
||||
ids = {info.cardId},
|
||||
from = move.from,
|
||||
to = move.to,
|
||||
toArea = move.toArea,
|
||||
fromArea = info.fromArea,
|
||||
})
|
||||
end
|
||||
local ret = {} ---@type CardsMoveInfo[]
|
||||
for _, move in ipairs(moves) do
|
||||
for _, info in ipairs(move.moveInfo) do
|
||||
table.insert(ret, {
|
||||
ids = {info.cardId},
|
||||
from = move.from,
|
||||
to = move.to,
|
||||
toArea = move.toArea,
|
||||
fromArea = info.fromArea,
|
||||
})
|
||||
end
|
||||
return ret
|
||||
end
|
||||
return ret
|
||||
end
|
||||
|
||||
--- merge separated moves (one fromArea per move)
|
||||
local function mergeMoves(moves)
|
||||
local ret = {}
|
||||
local temp = {}
|
||||
for _, move in ipairs(moves) do
|
||||
if temp[move.fromArea] == nil then
|
||||
temp[move.fromArea] = {
|
||||
ids = {},
|
||||
from = move.from,
|
||||
to = move.to,
|
||||
fromArea = move.fromArea,
|
||||
toArea = move.toArea
|
||||
}
|
||||
end
|
||||
table.insert(temp[move.fromArea].ids, move.ids[1])
|
||||
local ret = {}
|
||||
local temp = {}
|
||||
for _, move in ipairs(moves) do
|
||||
if temp[move.fromArea] == nil then
|
||||
temp[move.fromArea] = {
|
||||
ids = {},
|
||||
from = move.from,
|
||||
to = move.to,
|
||||
fromArea = move.fromArea,
|
||||
toArea = move.toArea
|
||||
}
|
||||
end
|
||||
for _, v in pairs(temp) do
|
||||
table.insert(ret, v)
|
||||
end
|
||||
return ret
|
||||
table.insert(temp[move.fromArea].ids, move.ids[1])
|
||||
end
|
||||
for _, v in pairs(temp) do
|
||||
table.insert(ret, v)
|
||||
end
|
||||
return ret
|
||||
end
|
||||
|
||||
fk.client_callback["MoveCards"] = function(jsonData)
|
||||
-- jsonData: CardsMoveStruct[]
|
||||
local raw_moves = json.decode(jsonData)
|
||||
local separated = separateMoves(raw_moves)
|
||||
local merged = mergeMoves(separated)
|
||||
ClientInstance:notifyUI("MoveCards", json.encode(merged))
|
||||
-- jsonData: CardsMoveStruct[]
|
||||
local raw_moves = json.decode(jsonData)
|
||||
local separated = separateMoves(raw_moves)
|
||||
local merged = mergeMoves(separated)
|
||||
ClientInstance:notifyUI("MoveCards", json.encode(merged))
|
||||
end
|
||||
|
||||
-- Create ClientInstance (used by Lua)
|
||||
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)
|
||||
return Fk.translations[src]
|
||||
return Fk.translations[src]
|
||||
end
|
||||
|
||||
function GetGeneralData(name)
|
||||
local general = Fk.generals[name]
|
||||
if general == nil then general = Fk.generals["diaochan"] end
|
||||
return json.encode {
|
||||
kingdom = general.kingdom,
|
||||
hp = general.hp,
|
||||
maxHp = general.maxHp
|
||||
}
|
||||
local general = Fk.generals[name]
|
||||
if general == nil then general = Fk.generals["diaochan"] end
|
||||
return json.encode {
|
||||
kingdom = general.kingdom,
|
||||
hp = general.hp,
|
||||
maxHp = general.maxHp
|
||||
}
|
||||
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)
|
||||
local card = Fk.cards[id]
|
||||
if card == nil then return json.encode{
|
||||
cid = id,
|
||||
known = false
|
||||
} end
|
||||
return json.encode{
|
||||
cid = id,
|
||||
name = card.name,
|
||||
number = card.number,
|
||||
suit = card:getSuitString(),
|
||||
color = card.color,
|
||||
}
|
||||
local card = Fk.cards[id]
|
||||
if card == nil then return json.encode{
|
||||
cid = id,
|
||||
known = false
|
||||
} end
|
||||
local ret = {
|
||||
cid = id,
|
||||
name = card.name,
|
||||
number = card.number,
|
||||
suit = card:getSuitString(),
|
||||
color = card.color,
|
||||
subtype = cardSubtypeStrings[card.sub_type]
|
||||
}
|
||||
return json.encode(ret)
|
||||
end
|
||||
|
||||
function GetAllGeneralPack()
|
||||
local ret = {}
|
||||
for _, name in ipairs(Fk.package_names) do
|
||||
if Fk.packages[name].type == Package.GeneralPack then
|
||||
table.insert(ret, name)
|
||||
end
|
||||
local ret = {}
|
||||
for _, name in ipairs(Fk.package_names) do
|
||||
if Fk.packages[name].type == Package.GeneralPack then
|
||||
table.insert(ret, name)
|
||||
end
|
||||
return json.encode(ret)
|
||||
end
|
||||
return json.encode(ret)
|
||||
end
|
||||
|
||||
function GetGenerals(pack_name)
|
||||
local ret = {}
|
||||
for _, g in ipairs(Fk.packages[pack_name].generals) do
|
||||
table.insert(ret, g.name)
|
||||
end
|
||||
return json.encode(ret)
|
||||
local ret = {}
|
||||
for _, g in ipairs(Fk.packages[pack_name].generals) do
|
||||
table.insert(ret, g.name)
|
||||
end
|
||||
return json.encode(ret)
|
||||
end
|
||||
|
||||
function GetAllCardPack()
|
||||
local ret = {}
|
||||
for _, name in ipairs(Fk.package_names) do
|
||||
if Fk.packages[name].type == Package.CardPack then
|
||||
table.insert(ret, name)
|
||||
end
|
||||
local ret = {}
|
||||
for _, name in ipairs(Fk.package_names) do
|
||||
if Fk.packages[name].type == Package.CardPack then
|
||||
table.insert(ret, name)
|
||||
end
|
||||
return json.encode(ret)
|
||||
end
|
||||
return json.encode(ret)
|
||||
end
|
||||
|
||||
function GetCards(pack_name)
|
||||
local ret = {}
|
||||
for _, c in ipairs(Fk.packages[pack_name].cards) do
|
||||
table.insert(ret, c.id)
|
||||
end
|
||||
return json.encode(ret)
|
||||
local ret = {}
|
||||
for _, c in ipairs(Fk.packages[pack_name].cards) do
|
||||
table.insert(ret, c.id)
|
||||
end
|
||||
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
|
||||
|
|
|
@ -5,9 +5,9 @@
|
|||
local ClientPlayer = Player:subclass("ClientPlayer")
|
||||
|
||||
function ClientPlayer:initialize(cp)
|
||||
self.player = cp
|
||||
self.handcardNum = 0
|
||||
self.known_cards = {}
|
||||
self.player = cp
|
||||
self.handcardNum = 0
|
||||
self.known_cards = {}
|
||||
end
|
||||
|
||||
return ClientPlayer
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
---@field name string
|
||||
---@field suit Suit
|
||||
---@field number integer
|
||||
---@field trueName string
|
||||
---@field color Color
|
||||
---@field id integer
|
||||
---@field type CardType
|
||||
|
@ -26,10 +27,9 @@ Card.NoColor = 3
|
|||
|
||||
---@alias CardType integer
|
||||
|
||||
Card.TypeSkill = 1
|
||||
Card.TypeBasic = 2
|
||||
Card.TypeTrick = 3
|
||||
Card.TypeEquip = 4
|
||||
Card.TypeBasic = 1
|
||||
Card.TypeTrick = 2
|
||||
Card.TypeEquip = 3
|
||||
|
||||
---@alias CardSubtype integer
|
||||
|
||||
|
@ -54,39 +54,41 @@ Card.DiscardPile = 7
|
|||
Card.Void = 8
|
||||
|
||||
function Card:initialize(name, suit, number, color)
|
||||
self.name = name
|
||||
self.suit = suit or Card.NoSuit
|
||||
self.number = number or 0
|
||||
self.name = name
|
||||
self.suit = suit or Card.NoSuit
|
||||
self.number = number or 0
|
||||
self.trueName = name
|
||||
|
||||
if suit == Card.Spade or suit == Card.Club then
|
||||
self.color = Card.Black
|
||||
elseif suit == Card.Heart or suit == Card.Diamond then
|
||||
self.color = Card.Red
|
||||
elseif color ~= nil then
|
||||
self.color = color
|
||||
else
|
||||
self.color = Card.NoColor
|
||||
end
|
||||
if suit == Card.Spade or suit == Card.Club then
|
||||
self.color = Card.Black
|
||||
elseif suit == Card.Heart or suit == Card.Diamond then
|
||||
self.color = Card.Red
|
||||
elseif color ~= nil then
|
||||
self.color = color
|
||||
else
|
||||
self.color = Card.NoColor
|
||||
end
|
||||
|
||||
self.package = nil
|
||||
self.id = 0
|
||||
self.type = 0
|
||||
self.sub_type = Card.SubTypeNone
|
||||
self.package = nil
|
||||
self.id = 0
|
||||
self.type = 0
|
||||
self.sub_type = Card.SubTypeNone
|
||||
self.skill = nil
|
||||
end
|
||||
|
||||
function Card:getSuitString()
|
||||
local suit = self.suit
|
||||
if suit == Card.Spade then
|
||||
return "spade"
|
||||
elseif suit == Card.Heart then
|
||||
return "heart"
|
||||
elseif suit == Card.Club then
|
||||
return "club"
|
||||
elseif suit == Card.Diamond then
|
||||
return "diamond"
|
||||
else
|
||||
return "unknown"
|
||||
end
|
||||
local suit = self.suit
|
||||
if suit == Card.Spade then
|
||||
return "spade"
|
||||
elseif suit == Card.Heart then
|
||||
return "heart"
|
||||
elseif suit == Card.Club then
|
||||
return "club"
|
||||
elseif suit == Card.Diamond then
|
||||
return "diamond"
|
||||
else
|
||||
return "unknown"
|
||||
end
|
||||
end
|
||||
|
||||
return Card
|
||||
|
|
|
@ -2,16 +2,17 @@
|
|||
local BasicCard = Card:subclass("BasicCard")
|
||||
|
||||
function BasicCard:initialize(name, suit, number)
|
||||
Card.initialize(self, name, suit, number)
|
||||
self.type = Card.TypeBasic
|
||||
Card.initialize(self, name, suit, number)
|
||||
self.type = Card.TypeBasic
|
||||
end
|
||||
|
||||
---@param suit Suit
|
||||
---@param number integer
|
||||
---@return BasicCard
|
||||
function BasicCard:clone(suit, number)
|
||||
local newCard = BasicCard:new(self.name, suit, number)
|
||||
return newCard
|
||||
local newCard = BasicCard:new(self.name, suit, number)
|
||||
newCard.skill = self.skill
|
||||
return newCard
|
||||
end
|
||||
|
||||
return BasicCard
|
||||
|
|
|
@ -1,90 +1,97 @@
|
|||
---@class EquipCard : Card
|
||||
---@field equipSkill Skill
|
||||
local EquipCard = Card:subclass("EquipCard")
|
||||
|
||||
function EquipCard:initialize(name, suit, number)
|
||||
Card.initialize(self, name, suit, number)
|
||||
self.type = Card.TypeEquip
|
||||
Card.initialize(self, name, suit, number)
|
||||
self.type = Card.TypeEquip
|
||||
self.equipSkill = nil
|
||||
end
|
||||
|
||||
---@class Weapon : EquipCard
|
||||
local Weapon = EquipCard:subclass("Weapon")
|
||||
|
||||
function Weapon:initialize(name, suit, number, attackRange)
|
||||
EquipCard.initialize(self, name, suit, number)
|
||||
self.sub_type = Card.SubtypeWeapon
|
||||
self.attack_range = attackRange or 1
|
||||
EquipCard.initialize(self, name, suit, number)
|
||||
self.sub_type = Card.SubtypeWeapon
|
||||
self.attack_range = attackRange or 1
|
||||
end
|
||||
|
||||
---@param suit Suit
|
||||
---@param number integer
|
||||
---@return Weapon
|
||||
function Weapon:clone(suit, number)
|
||||
local newCard = Weapon:new(self.name, suit, number, self.attack_range)
|
||||
return newCard
|
||||
local newCard = Weapon:new(self.name, suit, number, self.attack_range)
|
||||
newCard.skill = self.skill
|
||||
return newCard
|
||||
end
|
||||
|
||||
---@class Armor : EquipCard
|
||||
local Armor = EquipCard:subclass("armor")
|
||||
|
||||
function Armor:initialize(name, suit, number)
|
||||
EquipCard.initialize(self, name, suit, number)
|
||||
self.sub_type = Card.SubtypeArmor
|
||||
EquipCard.initialize(self, name, suit, number)
|
||||
self.sub_type = Card.SubtypeArmor
|
||||
end
|
||||
|
||||
---@param suit Suit
|
||||
---@param number integer
|
||||
---@return Armor
|
||||
function Armor:clone(suit, number)
|
||||
local newCard = Armor:new(self.name, suit, number)
|
||||
return newCard
|
||||
local newCard = Armor:new(self.name, suit, number)
|
||||
newCard.skill = self.skill
|
||||
return newCard
|
||||
end
|
||||
|
||||
---@class DefensiveRide : EquipCard
|
||||
local DefensiveRide = EquipCard:subclass("DefensiveRide")
|
||||
|
||||
function DefensiveRide:initialize(name, suit, number)
|
||||
EquipCard.initialize(self, name, suit, number)
|
||||
self.sub_type = Card.SubtypeDefensiveRide
|
||||
EquipCard.initialize(self, name, suit, number)
|
||||
self.sub_type = Card.SubtypeDefensiveRide
|
||||
end
|
||||
|
||||
---@param suit Suit
|
||||
---@param number integer
|
||||
---@return DefensiveRide
|
||||
function DefensiveRide:clone(suit, number)
|
||||
local newCard = DefensiveRide:new(self.name, suit, number)
|
||||
return newCard
|
||||
local newCard = DefensiveRide:new(self.name, suit, number)
|
||||
newCard.skill = self.skill
|
||||
return newCard
|
||||
end
|
||||
|
||||
---@class OffensiveRide : EquipCard
|
||||
local OffensiveRide = EquipCard:subclass("OffensiveRide")
|
||||
|
||||
function OffensiveRide:initialize(name, suit, number)
|
||||
EquipCard.initialize(self, name, suit, number)
|
||||
self.sub_type = Card.SubtypeOffensiveRide
|
||||
EquipCard.initialize(self, name, suit, number)
|
||||
self.sub_type = Card.SubtypeOffensiveRide
|
||||
end
|
||||
|
||||
---@param suit Suit
|
||||
---@param number integer
|
||||
---@return OffensiveRide
|
||||
function OffensiveRide:clone(suit, number)
|
||||
local newCard = OffensiveRide:new(self.name, suit, number)
|
||||
return newCard
|
||||
local newCard = OffensiveRide:new(self.name, suit, number)
|
||||
newCard.skill = self.skill
|
||||
return newCard
|
||||
end
|
||||
|
||||
---@class Treasure : EquipCard
|
||||
local Treasure = EquipCard:subclass("Treasure")
|
||||
|
||||
function Treasure:initialize(name, suit, number)
|
||||
EquipCard.initialize(self, name, suit, number)
|
||||
self.sub_type = Card.SubtypeTreasure
|
||||
EquipCard.initialize(self, name, suit, number)
|
||||
self.sub_type = Card.SubtypeTreasure
|
||||
end
|
||||
|
||||
---@param suit Suit
|
||||
---@param number integer
|
||||
---@return Treasure
|
||||
function Treasure:clone(suit, number)
|
||||
local newCard = Treasure:new(self.name, suit, number)
|
||||
return newCard
|
||||
local newCard = Treasure:new(self.name, suit, number)
|
||||
newCard.skill = self.skill
|
||||
return newCard
|
||||
end
|
||||
|
||||
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")
|
||||
|
||||
function TrickCard:initialize(name, suit, number)
|
||||
Card.initialize(self, name, suit, number)
|
||||
self.type = Card.TypeTrick
|
||||
Card.initialize(self, name, suit, number)
|
||||
self.type = Card.TypeTrick
|
||||
end
|
||||
|
||||
---@param suit Suit
|
||||
---@param number integer
|
||||
---@return TrickCard
|
||||
function TrickCard:clone(suit, number)
|
||||
local newCard = TrickCard:new(self.name, suit, number)
|
||||
return newCard
|
||||
local newCard = TrickCard:new(self.name, suit, number)
|
||||
|
||||
newCard.skill = self.skill
|
||||
|
||||
return newCard
|
||||
end
|
||||
|
||||
---@class DelayedTrickCard : TrickCard
|
||||
local DelayedTrickCard = TrickCard:subclass("DelayedTrickCard")
|
||||
|
||||
function DelayedTrickCard:initialize(name, suit, number)
|
||||
TrickCard.initialize(self, name, suit, number)
|
||||
self.sub_type = Card.SubtypeDelayedTrick
|
||||
TrickCard.initialize(self, name, suit, number)
|
||||
self.sub_type = Card.SubtypeDelayedTrick
|
||||
end
|
||||
|
||||
---@param suit Suit
|
||||
---@param number integer
|
||||
---@return DelayedTrickCard
|
||||
function DelayedTrickCard:clone(suit, number)
|
||||
local newCard = DelayedTrickCard:new(self.name, suit, number)
|
||||
return newCard
|
||||
local newCard = DelayedTrickCard:new(self.name, suit, number)
|
||||
newCard.skill = self.skill
|
||||
return newCard
|
||||
end
|
||||
|
||||
return { TrickCard, DelayedTrickCard }
|
||||
|
|
|
@ -3,16 +3,16 @@ inspect = require "inspect"
|
|||
|
||||
DebugMode = true
|
||||
function PrintWhenMethodCall()
|
||||
local info = debug.getinfo(2)
|
||||
local name = info.name
|
||||
local line = info.currentline
|
||||
local namewhat = info.namewhat
|
||||
local shortsrc = info.short_src
|
||||
if (namewhat == "method") and
|
||||
(shortsrc ~= "[C]") and
|
||||
(not string.find(shortsrc, "/lib")) then
|
||||
print(shortsrc .. ":" .. line .. ": " .. name)
|
||||
end
|
||||
local info = debug.getinfo(2)
|
||||
local name = info.name
|
||||
local line = info.currentline
|
||||
local namewhat = info.namewhat
|
||||
local shortsrc = info.short_src
|
||||
if (namewhat == "method") and
|
||||
(shortsrc ~= "[C]") and
|
||||
(not string.find(shortsrc, "/lib")) then
|
||||
print(shortsrc .. ":" .. line .. ": " .. name)
|
||||
end
|
||||
end
|
||||
--debug.sethook(PrintWhenMethodCall, "c")
|
||||
|
||||
|
|
|
@ -11,126 +11,126 @@
|
|||
local Engine = class("Engine")
|
||||
|
||||
function Engine:initialize()
|
||||
-- Engine should be singleton
|
||||
if Fk ~= nil then
|
||||
error("Engine has been initialized")
|
||||
return
|
||||
end
|
||||
-- Engine should be singleton
|
||||
if Fk ~= nil then
|
||||
error("Engine has been initialized")
|
||||
return
|
||||
end
|
||||
|
||||
Fk = self
|
||||
Fk = self
|
||||
|
||||
self.packages = {} -- name --> Package
|
||||
self.package_names = {}
|
||||
self.skills = {} -- name --> Skill
|
||||
self.related_skills = {} -- skillName --> relatedSkill[]
|
||||
self.global_trigger = {}
|
||||
self.generals = {} -- name --> General
|
||||
self.lords = {} -- lordName[]
|
||||
self.cards = {} -- Card[]
|
||||
self.translations = {} -- srcText --> translated
|
||||
self.packages = {} -- name --> Package
|
||||
self.package_names = {}
|
||||
self.skills = {} -- name --> Skill
|
||||
self.related_skills = {} -- skillName --> relatedSkill[]
|
||||
self.global_trigger = {}
|
||||
self.generals = {} -- name --> General
|
||||
self.lords = {} -- lordName[]
|
||||
self.cards = {} -- Card[]
|
||||
self.translations = {} -- srcText --> translated
|
||||
|
||||
self:loadPackages()
|
||||
self:loadPackages()
|
||||
end
|
||||
|
||||
---@param pack Package
|
||||
function Engine:loadPackage(pack)
|
||||
assert(pack:isInstanceOf(Package))
|
||||
if self.packages[pack.name] ~= nil then
|
||||
error(string.format("Duplicate package %s detected", pack.name))
|
||||
end
|
||||
self.packages[pack.name] = pack
|
||||
table.insert(self.package_names, pack.name)
|
||||
assert(pack:isInstanceOf(Package))
|
||||
if self.packages[pack.name] ~= nil then
|
||||
error(string.format("Duplicate package %s detected", pack.name))
|
||||
end
|
||||
self.packages[pack.name] = pack
|
||||
table.insert(self.package_names, pack.name)
|
||||
|
||||
-- add cards, generals and skills to Engine
|
||||
if pack.type == Package.CardPack then
|
||||
self:addCards(pack.cards)
|
||||
elseif pack.type == Package.GeneralPack then
|
||||
self:addGenerals(pack.generals)
|
||||
end
|
||||
self:addSkills(pack:getSkills())
|
||||
-- add cards, generals and skills to Engine
|
||||
if pack.type == Package.CardPack then
|
||||
self:addCards(pack.cards)
|
||||
elseif pack.type == Package.GeneralPack then
|
||||
self:addGenerals(pack.generals)
|
||||
end
|
||||
self:addSkills(pack:getSkills())
|
||||
end
|
||||
|
||||
function Engine:loadPackages()
|
||||
local directories = FileIO.ls("packages")
|
||||
local directories = FileIO.ls("packages")
|
||||
|
||||
-- load standard & standard_cards first
|
||||
self:loadPackage(require("packages.standard"))
|
||||
self:loadPackage(require("packages.standard_cards"))
|
||||
table.removeOne(directories, "standard")
|
||||
table.removeOne(directories, "standard_cards")
|
||||
-- load standard & standard_cards first
|
||||
self:loadPackage(require("packages.standard"))
|
||||
self:loadPackage(require("packages.standard_cards"))
|
||||
table.removeOne(directories, "standard")
|
||||
table.removeOne(directories, "standard_cards")
|
||||
|
||||
for _, dir in ipairs(directories) do
|
||||
if FileIO.isDir("packages/" .. dir) then
|
||||
local pack = require(string.format("packages.%s", dir))
|
||||
-- Note that instance of Package is a table too
|
||||
-- so dont use type(pack) == "table" here
|
||||
if pack[1] ~= nil then
|
||||
for _, p in ipairs(pack) do
|
||||
self:loadPackage(p)
|
||||
end
|
||||
else
|
||||
self:loadPackage(pack)
|
||||
end
|
||||
for _, dir in ipairs(directories) do
|
||||
if FileIO.isDir("packages/" .. dir) then
|
||||
local pack = require(string.format("packages.%s", dir))
|
||||
-- Note that instance of Package is a table too
|
||||
-- so dont use type(pack) == "table" here
|
||||
if pack[1] ~= nil then
|
||||
for _, p in ipairs(pack) do
|
||||
self:loadPackage(p)
|
||||
end
|
||||
else
|
||||
self:loadPackage(pack)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
---@param t table
|
||||
function Engine:loadTranslationTable(t)
|
||||
assert(type(t) == "table")
|
||||
for k, v in pairs(t) do
|
||||
self.translations[k] = v
|
||||
end
|
||||
assert(type(t) == "table")
|
||||
for k, v in pairs(t) do
|
||||
self.translations[k] = v
|
||||
end
|
||||
end
|
||||
|
||||
---@param skill Skill
|
||||
function Engine:addSkill(skill)
|
||||
assert(skill.class:isSubclassOf(Skill))
|
||||
if self.skills[skill.name] ~= nil then
|
||||
error(string.format("Duplicate skill %s detected", skill.name))
|
||||
end
|
||||
self.skills[skill.name] = skill
|
||||
assert(skill.class:isSubclassOf(Skill))
|
||||
if self.skills[skill.name] ~= nil then
|
||||
error(string.format("Duplicate skill %s detected", skill.name))
|
||||
end
|
||||
self.skills[skill.name] = skill
|
||||
end
|
||||
|
||||
---@param skills Skill[]
|
||||
function Engine:addSkills(skills)
|
||||
assert(type(skills) == "table")
|
||||
for _, skill in ipairs(skills) do
|
||||
self:addSkill(skill)
|
||||
end
|
||||
assert(type(skills) == "table")
|
||||
for _, skill in ipairs(skills) do
|
||||
self:addSkill(skill)
|
||||
end
|
||||
end
|
||||
|
||||
---@param general General
|
||||
function Engine:addGeneral(general)
|
||||
assert(general:isInstanceOf(General))
|
||||
if self.generals[general.name] ~= nil then
|
||||
error(string.format("Duplicate general %s detected", general.name))
|
||||
end
|
||||
self.generals[general.name] = general
|
||||
assert(general:isInstanceOf(General))
|
||||
if self.generals[general.name] ~= nil then
|
||||
error(string.format("Duplicate general %s detected", general.name))
|
||||
end
|
||||
self.generals[general.name] = general
|
||||
end
|
||||
|
||||
---@param generals General[]
|
||||
function Engine:addGenerals(generals)
|
||||
assert(type(generals) == "table")
|
||||
for _, general in ipairs(generals) do
|
||||
self:addGeneral(general)
|
||||
end
|
||||
assert(type(generals) == "table")
|
||||
for _, general in ipairs(generals) do
|
||||
self:addGeneral(general)
|
||||
end
|
||||
end
|
||||
|
||||
local cardId = 1
|
||||
---@param card Card
|
||||
function Engine:addCard(card)
|
||||
assert(card.class:isSubclassOf(Card))
|
||||
card.id = cardId
|
||||
cardId = cardId + 1
|
||||
table.insert(self.cards, card)
|
||||
assert(card.class:isSubclassOf(Card))
|
||||
card.id = cardId
|
||||
cardId = cardId + 1
|
||||
table.insert(self.cards, card)
|
||||
end
|
||||
|
||||
---@param cards Card[]
|
||||
function Engine:addCards(cards)
|
||||
for _, card in ipairs(cards) do
|
||||
self:addCard(card)
|
||||
end
|
||||
for _, card in ipairs(cards) do
|
||||
self:addCard(card)
|
||||
end
|
||||
end
|
||||
|
||||
---@param num integer
|
||||
|
@ -139,68 +139,68 @@ end
|
|||
---@param filter function
|
||||
---@return General[]
|
||||
function Engine:getGeneralsRandomly(num, generalPool, except, filter)
|
||||
if filter then
|
||||
assert(type(filter) == "function")
|
||||
end
|
||||
if filter then
|
||||
assert(type(filter) == "function")
|
||||
end
|
||||
|
||||
generalPool = generalPool or self.generals
|
||||
except = except or {}
|
||||
generalPool = generalPool or self.generals
|
||||
except = except or {}
|
||||
|
||||
local availableGenerals = {}
|
||||
for _, general in pairs(generalPool) do
|
||||
if not table.contains(except, general.name) and not (filter and filter(general)) then
|
||||
table.insert(availableGenerals, general)
|
||||
end
|
||||
local availableGenerals = {}
|
||||
for _, general in pairs(generalPool) do
|
||||
if not table.contains(except, general.name) and not (filter and filter(general)) then
|
||||
table.insert(availableGenerals, general)
|
||||
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
|
||||
return {}
|
||||
break
|
||||
end
|
||||
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
|
||||
break
|
||||
end
|
||||
end
|
||||
|
||||
return result
|
||||
return result
|
||||
end
|
||||
|
||||
---@param except General[]
|
||||
---@return General[]
|
||||
function Engine:getAllGenerals(except)
|
||||
local result = {}
|
||||
for _, general in ipairs(self.generals) do
|
||||
if not (except and table.contains(except, general)) then
|
||||
table.insert(result, general)
|
||||
end
|
||||
local result = {}
|
||||
for _, general in ipairs(self.generals) do
|
||||
if not (except and table.contains(except, general)) then
|
||||
table.insert(result, general)
|
||||
end
|
||||
end
|
||||
|
||||
return result
|
||||
return result
|
||||
end
|
||||
|
||||
---@param except integer[]
|
||||
---@return integer[]
|
||||
function Engine:getAllCardIds(except)
|
||||
local result = {}
|
||||
for _, card in ipairs(self.cards) do
|
||||
if not (except and table.contains(except, card.id)) then
|
||||
table.insert(result, card.id)
|
||||
end
|
||||
local result = {}
|
||||
for _, card in ipairs(self.cards) do
|
||||
if not (except and table.contains(except, card.id)) then
|
||||
table.insert(result, card.id)
|
||||
end
|
||||
end
|
||||
|
||||
return result
|
||||
return result
|
||||
end
|
||||
|
||||
---@param id integer
|
||||
---@return Card
|
||||
function Engine:getCardById(id)
|
||||
return self.cards[id]
|
||||
return self.cards[id]
|
||||
end
|
||||
|
||||
return Engine
|
||||
|
|
|
@ -15,24 +15,24 @@ General.Male = 1
|
|||
General.Female = 2
|
||||
|
||||
function General:initialize(package, name, kingdom, hp, maxHp, gender)
|
||||
self.package = package
|
||||
self.name = name
|
||||
self.kingdom = kingdom
|
||||
self.hp = hp
|
||||
self.maxHp = maxHp or hp
|
||||
self.gender = gender or General.Male
|
||||
self.package = package
|
||||
self.name = name
|
||||
self.kingdom = kingdom
|
||||
self.hp = hp
|
||||
self.maxHp = maxHp or hp
|
||||
self.gender = gender or General.Male
|
||||
|
||||
self.skills = {} -- skills first added to this general
|
||||
self.other_skills = {} -- skill belongs other general, e.g. "mashu" of pangde
|
||||
self.skills = {} -- skills first added to this general
|
||||
self.other_skills = {} -- skill belongs other general, e.g. "mashu" of pangde
|
||||
end
|
||||
|
||||
---@param skill Skill
|
||||
function General:addSkill(skill)
|
||||
if (type(skill) == "string") then
|
||||
table.insert(self.other_skills, skill)
|
||||
elseif (skill.class and skill.class:isSubclassOf(Skill)) then
|
||||
table.insert(self.skills, skill)
|
||||
end
|
||||
if (type(skill) == "string") then
|
||||
table.insert(self.other_skills, skill)
|
||||
elseif (skill.class and skill.class:isSubclassOf(Skill)) then
|
||||
table.insert(self.skills, skill)
|
||||
end
|
||||
end
|
||||
|
||||
return General
|
||||
|
|
|
@ -14,48 +14,48 @@ Package.CardPack = 2
|
|||
Package.SpecialPack = 3
|
||||
|
||||
function Package:initialize(name, _type)
|
||||
assert(type(name) == "string")
|
||||
assert(type(_type) == "nil" or type(_type) == "number")
|
||||
self.name = name
|
||||
self.type = _type or Package.GeneralPack
|
||||
assert(type(name) == "string")
|
||||
assert(type(_type) == "nil" or type(_type) == "number")
|
||||
self.name = name
|
||||
self.type = _type or Package.GeneralPack
|
||||
|
||||
self.generals = {}
|
||||
self.extra_skills = {} -- skill not belongs to any generals, like "jixi"
|
||||
self.related_skills = {}
|
||||
self.cards = {}
|
||||
self.generals = {}
|
||||
self.extra_skills = {} -- skill not belongs to any generals, like "jixi"
|
||||
self.related_skills = {}
|
||||
self.cards = {}
|
||||
end
|
||||
|
||||
---@return Skill[]
|
||||
function Package:getSkills()
|
||||
local ret = {table.unpack(self.related_skills)}
|
||||
if self.type == Package.GeneralPack then
|
||||
for _, g in ipairs(self.generals) do
|
||||
for _, s in ipairs(g.skills) do
|
||||
table.insert(ret, s)
|
||||
end
|
||||
end
|
||||
local ret = {table.unpack(self.related_skills)}
|
||||
if self.type == Package.GeneralPack then
|
||||
for _, g in ipairs(self.generals) do
|
||||
for _, s in ipairs(g.skills) do
|
||||
table.insert(ret, s)
|
||||
end
|
||||
end
|
||||
return ret
|
||||
end
|
||||
return ret
|
||||
end
|
||||
|
||||
---@param general General
|
||||
function Package:addGeneral(general)
|
||||
assert(general.class and general:isInstanceOf(General))
|
||||
table.insert(self.generals, general)
|
||||
assert(general.class and general:isInstanceOf(General))
|
||||
table.insert(self.generals, general)
|
||||
end
|
||||
|
||||
---@param card Card
|
||||
function Package:addCard(card)
|
||||
assert(card.class and card:isInstanceOf(Card))
|
||||
card.package = self
|
||||
table.insert(self.cards, card)
|
||||
assert(card.class and card:isInstanceOf(Card))
|
||||
card.package = self
|
||||
table.insert(self.cards, card)
|
||||
end
|
||||
|
||||
---@param cards Card[]
|
||||
function Package:addCards(cards)
|
||||
for _, card in ipairs(cards) do
|
||||
self:addCard(card)
|
||||
end
|
||||
for _, card in ipairs(cards) do
|
||||
self:addCard(card)
|
||||
end
|
||||
end
|
||||
|
||||
return Package
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
---@field mark table<string, integer>
|
||||
---@field player_cards table<integer, integer[]>
|
||||
---@field special_cards table<string, integer[]>
|
||||
---@field cardUsedHistory table<string, integer>
|
||||
local Player = class("Player")
|
||||
|
||||
---@alias Phase integer
|
||||
|
@ -41,184 +42,211 @@ Player.Judge = 3
|
|||
Player.Special = 4
|
||||
|
||||
function Player:initialize()
|
||||
self.id = 114514
|
||||
self.hp = 0
|
||||
self.maxHp = 0
|
||||
self.kingdom = "qun"
|
||||
self.role = ""
|
||||
self.general = ""
|
||||
self.seat = 0
|
||||
self.phase = Player.PhaseNone
|
||||
self.faceup = true
|
||||
self.chained = false
|
||||
self.dying = false
|
||||
self.dead = false
|
||||
self.state = ""
|
||||
self.id = 114514
|
||||
self.hp = 0
|
||||
self.maxHp = 0
|
||||
self.kingdom = "qun"
|
||||
self.role = ""
|
||||
self.general = ""
|
||||
self.seat = 0
|
||||
self.phase = Player.PhaseNone
|
||||
self.faceup = true
|
||||
self.chained = false
|
||||
self.dying = false
|
||||
self.dead = false
|
||||
self.state = ""
|
||||
|
||||
self.player_skills = {}
|
||||
self.flag = {}
|
||||
self.tag = {}
|
||||
self.mark = {}
|
||||
self.player_cards = {
|
||||
[Player.Hand] = {},
|
||||
[Player.Equip] = {},
|
||||
[Player.Judge] = {},
|
||||
}
|
||||
self.special_cards = {}
|
||||
self.player_skills = {}
|
||||
self.flag = {}
|
||||
self.tag = {}
|
||||
self.mark = {}
|
||||
self.player_cards = {
|
||||
[Player.Hand] = {},
|
||||
[Player.Equip] = {},
|
||||
[Player.Judge] = {},
|
||||
}
|
||||
self.special_cards = {}
|
||||
|
||||
self.cardUsedHistory = {}
|
||||
end
|
||||
|
||||
---@param general General
|
||||
---@param setHp boolean
|
||||
---@param addSkills boolean
|
||||
function Player:setGeneral(general, setHp, addSkills)
|
||||
self.general = general
|
||||
if setHp then
|
||||
self.maxHp = general.maxHp
|
||||
self.hp = general.hp
|
||||
end
|
||||
self.general = general
|
||||
if setHp then
|
||||
self.maxHp = general.maxHp
|
||||
self.hp = general.hp
|
||||
end
|
||||
|
||||
if addSkills then
|
||||
table.insertTable(self.player_skills, general.skills)
|
||||
end
|
||||
if addSkills then
|
||||
table.insertTable(self.player_skills, general.skills)
|
||||
end
|
||||
end
|
||||
|
||||
---@param flag string
|
||||
function Player:hasFlag(flag)
|
||||
return table.contains(self.flag, flag)
|
||||
return table.contains(self.flag, flag)
|
||||
end
|
||||
|
||||
---@param flag string
|
||||
function Player:setFlag(flag)
|
||||
if flag == "." then
|
||||
self:clearFlags()
|
||||
return
|
||||
end
|
||||
if flag:sub(1, 1) == "-" then
|
||||
flag = flag:sub(2, #flag)
|
||||
table.removeOne(self.flag, flag)
|
||||
return
|
||||
end
|
||||
if not self:hasFlag(flag) then
|
||||
table.insert(self.flag, flag)
|
||||
end
|
||||
if flag == "." then
|
||||
self:clearFlags()
|
||||
return
|
||||
end
|
||||
if flag:sub(1, 1) == "-" then
|
||||
flag = flag:sub(2, #flag)
|
||||
table.removeOne(self.flag, flag)
|
||||
return
|
||||
end
|
||||
if not self:hasFlag(flag) then
|
||||
table.insert(self.flag, flag)
|
||||
end
|
||||
end
|
||||
|
||||
function Player:clearFlags()
|
||||
self.flag = {}
|
||||
self.flag = {}
|
||||
end
|
||||
|
||||
function Player:addMark(mark, count)
|
||||
count = count or 1
|
||||
local num = self.mark[mark]
|
||||
num = num or 0
|
||||
self:setMark(mark, math.max(num + count, 0))
|
||||
count = count or 1
|
||||
local num = self.mark[mark]
|
||||
num = num or 0
|
||||
self:setMark(mark, math.max(num + count, 0))
|
||||
end
|
||||
|
||||
function Player:removeMark(mark, count)
|
||||
count = count or 1
|
||||
local num = self.mark[mark]
|
||||
num = num or 0
|
||||
self:setMark(mark, math.max(num - count, 0))
|
||||
count = count or 1
|
||||
local num = self.mark[mark]
|
||||
num = num or 0
|
||||
self:setMark(mark, math.max(num - count, 0))
|
||||
end
|
||||
|
||||
function Player:setMark(mark, count)
|
||||
if self.mark[mark] ~= count then
|
||||
self.mark[mark] = count
|
||||
end
|
||||
if self.mark[mark] ~= count then
|
||||
self.mark[mark] = count
|
||||
end
|
||||
end
|
||||
|
||||
function Player:getMark(mark)
|
||||
return (self.mark[mark] or 0)
|
||||
return (self.mark[mark] or 0)
|
||||
end
|
||||
|
||||
function Player:getMarkNames()
|
||||
local ret = {}
|
||||
for k, _ in pairs(self.mark) do
|
||||
table.insert(ret, k)
|
||||
end
|
||||
return ret
|
||||
local ret = {}
|
||||
for k, _ in pairs(self.mark) do
|
||||
table.insert(ret, k)
|
||||
end
|
||||
return ret
|
||||
end
|
||||
|
||||
---@param playerArea PlayerCardArea
|
||||
---@param cardIds integer[]
|
||||
---@param specialName string
|
||||
function Player:addCards(playerArea, cardIds, specialName)
|
||||
assert(table.contains({ Player.Hand, Player.Equip, Player.Judge, Player.Special }, playerArea))
|
||||
assert(playerArea ~= Player.Special or type(specialName) == "string")
|
||||
assert(table.contains({ Player.Hand, Player.Equip, Player.Judge, Player.Special }, playerArea))
|
||||
assert(playerArea ~= Player.Special or type(specialName) == "string")
|
||||
|
||||
if playerArea == Player.Special then
|
||||
self.special_cards[specialName] = self.special_cards[specialName] or {}
|
||||
table.insertTable(self.special_cards[specialName], cardIds)
|
||||
else
|
||||
table.insertTable(self.player_cards[playerArea], cardIds)
|
||||
end
|
||||
if playerArea == Player.Special then
|
||||
self.special_cards[specialName] = self.special_cards[specialName] or {}
|
||||
table.insertTable(self.special_cards[specialName], cardIds)
|
||||
else
|
||||
table.insertTable(self.player_cards[playerArea], cardIds)
|
||||
end
|
||||
end
|
||||
|
||||
---@param playerArea PlayerCardArea
|
||||
---@param cardIds integer[]
|
||||
---@param specialName string
|
||||
function Player:removeCards(playerArea, cardIds, specialName)
|
||||
assert(table.contains({ Player.Hand, Player.Equip, Player.Judge, Player.Special }, playerArea))
|
||||
assert(playerArea ~= Player.Special or type(specialName) == "string")
|
||||
assert(table.contains({ Player.Hand, Player.Equip, Player.Judge, Player.Special }, playerArea))
|
||||
assert(playerArea ~= Player.Special or type(specialName) == "string")
|
||||
|
||||
local fromAreaIds = playerArea == Player.Special and self.special_cards[specialName] or self.player_cards[playerArea]
|
||||
if fromAreaIds then
|
||||
for _, id in ipairs(cardIds) do
|
||||
if #fromAreaIds == 0 then
|
||||
break
|
||||
end
|
||||
local fromAreaIds = playerArea == Player.Special and self.special_cards[specialName] or self.player_cards[playerArea]
|
||||
if fromAreaIds then
|
||||
for _, id in ipairs(cardIds) do
|
||||
if #fromAreaIds == 0 then
|
||||
break
|
||||
end
|
||||
|
||||
table.removeOne(fromAreaIds, id)
|
||||
end
|
||||
table.removeOne(fromAreaIds, id)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
---@param playerAreas PlayerCardArea
|
||||
---@param specialName string
|
||||
---@return integer[]
|
||||
function Player:getCardIds(playerAreas, specialName)
|
||||
local rightAreas = { Player.Hand, Player.Equip, Player.Judge }
|
||||
playerAreas = playerAreas or rightAreas
|
||||
assert(type(playerAreas) == "number" or type(playerAreas) == "table")
|
||||
local areas = type(playerAreas) == "table" and playerAreas or { playerAreas }
|
||||
local rightAreas = { Player.Hand, Player.Equip, Player.Judge }
|
||||
playerAreas = playerAreas or rightAreas
|
||||
assert(type(playerAreas) == "number" or type(playerAreas) == "table")
|
||||
local areas = type(playerAreas) == "table" and playerAreas or { playerAreas }
|
||||
|
||||
local rightAreas = { Player.Hand, Player.Equip, Player.Judge, Player.Special }
|
||||
local cardIds = {}
|
||||
for _, area in ipairs(areas) do
|
||||
assert(table.contains(rightAreas, area))
|
||||
assert(area ~= Player.Special or type(specialName) == "string")
|
||||
local currentCardIds = area == Player.Special and self.special_cards[specialName] or self.player_cards[area]
|
||||
table.insertTable(cardIds, currentCardIds)
|
||||
local rightAreas = { Player.Hand, Player.Equip, Player.Judge, Player.Special }
|
||||
local cardIds = {}
|
||||
for _, area in ipairs(areas) do
|
||||
assert(table.contains(rightAreas, area))
|
||||
assert(area ~= Player.Special or type(specialName) == "string")
|
||||
local currentCardIds = area == Player.Special and self.special_cards[specialName] or self.player_cards[area]
|
||||
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
|
||||
|
||||
return cardIds
|
||||
return nil
|
||||
end
|
||||
|
||||
function Player:getMaxCards()
|
||||
local baseValue = math.max(self.hp, 0)
|
||||
local baseValue = math.max(self.hp, 0)
|
||||
|
||||
return baseValue
|
||||
return baseValue
|
||||
end
|
||||
|
||||
---@param subtype CardSubtype
|
||||
---@return integer|null
|
||||
function Player:getEquipBySubtype(subtype)
|
||||
local equipId = nil
|
||||
for _, id in ipairs(self.player_cards[Player.Equip]) do
|
||||
if Fk.getCardById(id).sub_type == subtype then
|
||||
equipId = id
|
||||
break
|
||||
end
|
||||
local equipId = nil
|
||||
for _, id in ipairs(self.player_cards[Player.Equip]) do
|
||||
if Fk:getCardById(id).sub_type == subtype then
|
||||
equipId = id
|
||||
break
|
||||
end
|
||||
end
|
||||
|
||||
return equipId
|
||||
return equipId
|
||||
end
|
||||
|
||||
function Player:getAttackRange()
|
||||
local weapon = Fk.getCardById(self:getEquipBySubtype(Card.SubtypeWeapon))
|
||||
local baseAttackRange = math.max(weapon and weapon.attack_range or 1, 0)
|
||||
local weapon = Fk:getCardById(self:getEquipBySubtype(Card.SubtypeWeapon))
|
||||
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
|
||||
|
||||
return Player
|
||||
|
|
|
@ -13,10 +13,10 @@ Skill.Limited = 4
|
|||
Skill.Wake = 5
|
||||
|
||||
function Skill:initialize(name, frequency)
|
||||
-- TODO: visible, lord, etc
|
||||
self.name = name
|
||||
self.frequency = frequency
|
||||
self.visible = true
|
||||
-- TODO: visible, lord, etc
|
||||
self.name = name
|
||||
self.frequency = frequency
|
||||
self.visible = true
|
||||
end
|
||||
|
||||
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")
|
||||
|
||||
function TriggerSkill:initialize(name, frequency)
|
||||
Skill.initialize(self, name, frequency)
|
||||
Skill.initialize(self, name, frequency)
|
||||
|
||||
self.global = false
|
||||
self.events = {}
|
||||
self.refresh_events = {}
|
||||
self.priority_table = {} -- GameEvent --> priority
|
||||
self.global = false
|
||||
self.events = {}
|
||||
self.refresh_events = {}
|
||||
self.priority_table = {} -- GameEvent --> priority
|
||||
end
|
||||
|
||||
-- Default functions
|
||||
|
@ -37,8 +37,8 @@ function TriggerSkill:refresh(event, target, player, data) end
|
|||
---@param data any @ useful data of the event
|
||||
---@return boolean
|
||||
function TriggerSkill:triggerable(event, target, player, data)
|
||||
return target and (target == player)
|
||||
and (self.global or (target:isAlive() and target:hasSkill(self)))
|
||||
return target and (target == player)
|
||||
and (self.global or (target:isAlive() and target:hasSkill(self)))
|
||||
end
|
||||
|
||||
---Trigger this skill
|
||||
|
@ -48,10 +48,10 @@ end
|
|||
---@param data any @ useful data of the event
|
||||
---@return boolean @ returns true if trigger is broken
|
||||
function TriggerSkill:trigger(event, target, player, data)
|
||||
if player.room:askForSkillInvoke(player, self.name) then
|
||||
return self:use(event, target, player, data)
|
||||
end
|
||||
return false
|
||||
if player.room:askForSkillInvoke(player, self.name) then
|
||||
return self:use(event, target, player, data)
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
---Use this skill
|
||||
|
|
|
@ -1,52 +1,52 @@
|
|||
-- the iterator of QList object
|
||||
local qlist_iterator = function(list, n)
|
||||
if n < list:length() - 1 then
|
||||
return n + 1, list:at(n + 1) -- the next element of list
|
||||
end
|
||||
if n < list:length() - 1 then
|
||||
return n + 1, list:at(n + 1) -- the next element of list
|
||||
end
|
||||
end
|
||||
|
||||
function fk.qlist(list)
|
||||
return qlist_iterator, list, -1
|
||||
return qlist_iterator, list, -1
|
||||
end
|
||||
|
||||
function table:contains(element)
|
||||
if #self == 0 or type(self[1]) ~= type(element) then return false end
|
||||
for _, e in ipairs(self) do
|
||||
if e == element then return true end
|
||||
end
|
||||
if #self == 0 or type(self[1]) ~= type(element) then return false end
|
||||
for _, e in ipairs(self) do
|
||||
if e == element then return true end
|
||||
end
|
||||
end
|
||||
|
||||
function table:shuffle()
|
||||
for i = #self, 2, -1 do
|
||||
local j = math.random(i)
|
||||
self[i], self[j] = self[j], self[i]
|
||||
end
|
||||
for i = #self, 2, -1 do
|
||||
local j = math.random(i)
|
||||
self[i], self[j] = self[j], self[i]
|
||||
end
|
||||
end
|
||||
|
||||
function table:insertTable(list)
|
||||
for _, e in ipairs(list) do
|
||||
table.insert(self, e)
|
||||
end
|
||||
for _, e in ipairs(list) do
|
||||
table.insert(self, e)
|
||||
end
|
||||
end
|
||||
|
||||
function table:indexOf(value, from)
|
||||
from = from or 1
|
||||
for i = from, #self do
|
||||
if self[i] == value then return i end
|
||||
end
|
||||
return -1
|
||||
from = from or 1
|
||||
for i = from, #self do
|
||||
if self[i] == value then return i end
|
||||
end
|
||||
return -1
|
||||
end
|
||||
|
||||
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
|
||||
if self[i] == element then
|
||||
table.remove(self, i)
|
||||
return true
|
||||
end
|
||||
end
|
||||
return false
|
||||
for i = 1, #self do
|
||||
if self[i] == element then
|
||||
table.remove(self, i)
|
||||
return true
|
||||
end
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
-- Note: only clone key and value, no metatable
|
||||
|
@ -55,57 +55,57 @@ end
|
|||
---@param self T
|
||||
---@return T
|
||||
function table.clone(self)
|
||||
local ret = {}
|
||||
for k, v in pairs(self) do
|
||||
if type(v) == "table" then
|
||||
ret[k] = table.clone(v)
|
||||
else
|
||||
ret[k] = v
|
||||
end
|
||||
end
|
||||
return ret
|
||||
local ret = {}
|
||||
for k, v in pairs(self) do
|
||||
if type(v) == "table" then
|
||||
ret[k] = table.clone(v)
|
||||
else
|
||||
ret[k] = v
|
||||
end
|
||||
end
|
||||
return ret
|
||||
end
|
||||
|
||||
---@class Sql
|
||||
Sql = {
|
||||
---@param filename string
|
||||
open = function(filename)
|
||||
return fk.OpenDatabase(filename)
|
||||
end,
|
||||
---@param filename string
|
||||
open = function(filename)
|
||||
return fk.OpenDatabase(filename)
|
||||
end,
|
||||
|
||||
---@param db fk.SQLite3
|
||||
close = function(db)
|
||||
fk.CloseDatabase(db)
|
||||
end,
|
||||
---@param db fk.SQLite3
|
||||
close = function(db)
|
||||
fk.CloseDatabase(db)
|
||||
end,
|
||||
|
||||
--- Execute an SQL statement.
|
||||
---@param db fk.SQLite3
|
||||
---@param sql string
|
||||
exec = function(db, sql)
|
||||
fk.ExecSQL(db, sql)
|
||||
end,
|
||||
--- Execute an SQL statement.
|
||||
---@param db fk.SQLite3
|
||||
---@param sql string
|
||||
exec = function(db, sql)
|
||||
fk.ExecSQL(db, sql)
|
||||
end,
|
||||
|
||||
--- Execute a `SELECT` SQL statement.
|
||||
---@param db fk.SQLite3
|
||||
---@param sql string
|
||||
---@return table @ { [columnName] --> result : string[] }
|
||||
exec_select = function(db, sql)
|
||||
return json.decode(fk.SelectFromDb(db, sql))
|
||||
end,
|
||||
--- Execute a `SELECT` SQL statement.
|
||||
---@param db fk.SQLite3
|
||||
---@param sql string
|
||||
---@return table @ { [columnName] --> result : string[] }
|
||||
exec_select = function(db, sql)
|
||||
return json.decode(fk.SelectFromDb(db, sql))
|
||||
end,
|
||||
}
|
||||
|
||||
FileIO = {
|
||||
pwd = fk.QmlBackend_pwd,
|
||||
ls = function(filename)
|
||||
if filename == nil then
|
||||
return fk.QmlBackend_ls(".")
|
||||
else
|
||||
return fk.QmlBackend_ls(filename)
|
||||
end
|
||||
end,
|
||||
cd = fk.QmlBackend_cd,
|
||||
exists = fk.QmlBackend_exists,
|
||||
isDir = fk.QmlBackend_isDir
|
||||
pwd = fk.QmlBackend_pwd,
|
||||
ls = function(filename)
|
||||
if filename == nil then
|
||||
return fk.QmlBackend_ls(".")
|
||||
else
|
||||
return fk.QmlBackend_ls(filename)
|
||||
end
|
||||
end,
|
||||
cd = fk.QmlBackend_cd,
|
||||
exists = fk.QmlBackend_exists,
|
||||
isDir = fk.QmlBackend_isDir
|
||||
}
|
||||
|
||||
os.getms = fk.GetMicroSecond
|
||||
|
@ -113,23 +113,23 @@ os.getms = fk.GetMicroSecond
|
|||
---@class Stack : Object
|
||||
Stack = class("Stack")
|
||||
function Stack:initialize()
|
||||
self.t = {}
|
||||
self.p = 0
|
||||
self.t = {}
|
||||
self.p = 0
|
||||
end
|
||||
|
||||
function Stack:push(e)
|
||||
self.p = self.p + 1
|
||||
self.t[self.p] = e
|
||||
self.p = self.p + 1
|
||||
self.t[self.p] = e
|
||||
end
|
||||
|
||||
function Stack:isEmpty()
|
||||
return self.p == 0
|
||||
return self.p == 0
|
||||
end
|
||||
|
||||
function Stack:pop()
|
||||
if self.p == 0 then return nil end
|
||||
self.p = self.p - 1
|
||||
return self.t[self.p + 1]
|
||||
if self.p == 0 then return nil end
|
||||
self.p = self.p - 1
|
||||
return self.t[self.p + 1]
|
||||
end
|
||||
|
||||
|
||||
|
@ -139,15 +139,156 @@ end
|
|||
---@param table string
|
||||
---@param enum string[]
|
||||
function CreateEnum(table, enum)
|
||||
local enum_format = "%s.%s = %d"
|
||||
for i, v in ipairs(enum) do
|
||||
print(string.format(enum_format, table, v, i))
|
||||
end
|
||||
local enum_format = "%s.%s = %d"
|
||||
for i, v in ipairs(enum) do
|
||||
print(string.format(enum_format, table, v, i))
|
||||
end
|
||||
end
|
||||
|
||||
function switch(param, case_table)
|
||||
local case = case_table[param]
|
||||
if case then return case() end
|
||||
local def = case_table["default"]
|
||||
return def and def() or nil
|
||||
local case = case_table[param]
|
||||
if case then return case() end
|
||||
local def = case_table["default"]
|
||||
return def and def() or nil
|
||||
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
|
||||
|
||||
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"
|
||||
local Trick = require "core.card_type.trick"
|
||||
TrickCard, DelayedTrickCard = table.unpack(Trick)
|
||||
local Equip = require "core.card_type.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
|
||||
|
||||
---@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 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
|
||||
---@return BasicCard
|
||||
function fk.CreateBasicCard(spec)
|
||||
assert(type(spec.name) == "string" or type(spec.class_name) == "string")
|
||||
if not spec.name then spec.name = spec.class_name
|
||||
elseif not spec.class_name then spec.class_name = spec.name end
|
||||
if spec.suit then assert(type(spec.suit) == "number") end
|
||||
if spec.number then assert(type(spec.number) == "number") end
|
||||
assert(type(spec.name) == "string" or type(spec.class_name) == "string")
|
||||
if not spec.name then spec.name = spec.class_name
|
||||
elseif not spec.class_name then spec.class_name = spec.name end
|
||||
if spec.suit then assert(type(spec.suit) == "number") end
|
||||
if spec.number then assert(type(spec.number) == "number") end
|
||||
|
||||
local card = BasicCard:new(spec.name, spec.suit, spec.number)
|
||||
return card
|
||||
local card = BasicCard:new(spec.name, spec.suit, spec.number)
|
||||
card.skill = spec.skill or defaultCardSkill
|
||||
return card
|
||||
end
|
||||
|
||||
---@param spec CardSpec
|
||||
---@return TrickCard
|
||||
function fk.CreateTrickCard(spec)
|
||||
assert(type(spec.name) == "string" or type(spec.class_name) == "string")
|
||||
if not spec.name then spec.name = spec.class_name
|
||||
elseif not spec.class_name then spec.class_name = spec.name end
|
||||
if spec.suit then assert(type(spec.suit) == "number") end
|
||||
if spec.number then assert(type(spec.number) == "number") end
|
||||
assert(type(spec.name) == "string" or type(spec.class_name) == "string")
|
||||
if not spec.name then spec.name = spec.class_name
|
||||
elseif not spec.class_name then spec.class_name = spec.name end
|
||||
if spec.suit then assert(type(spec.suit) == "number") end
|
||||
if spec.number then assert(type(spec.number) == "number") end
|
||||
|
||||
local card = TrickCard:new(spec.name, spec.suit, spec.number)
|
||||
return card
|
||||
local card = TrickCard:new(spec.name, spec.suit, spec.number)
|
||||
card.skill = spec.skill or defaultCardSkill
|
||||
return card
|
||||
end
|
||||
|
||||
---@param spec CardSpec
|
||||
---@return DelayedTrickCard
|
||||
function fk.CreateDelayedTrickCard(spec)
|
||||
assert(type(spec.name) == "string" or type(spec.class_name) == "string")
|
||||
if not spec.name then spec.name = spec.class_name
|
||||
elseif not spec.class_name then spec.class_name = spec.name end
|
||||
if spec.suit then assert(type(spec.suit) == "number") end
|
||||
if spec.number then assert(type(spec.number) == "number") end
|
||||
assert(type(spec.name) == "string" or type(spec.class_name) == "string")
|
||||
if not spec.name then spec.name = spec.class_name
|
||||
elseif not spec.class_name then spec.class_name = spec.name end
|
||||
if spec.suit then assert(type(spec.suit) == "number") end
|
||||
if spec.number then assert(type(spec.number) == "number") end
|
||||
|
||||
local card = DelayedTrickCard:new(spec.name, spec.suit, spec.number)
|
||||
return card
|
||||
local card = DelayedTrickCard:new(spec.name, spec.suit, spec.number)
|
||||
card.skill = spec.skill or defaultCardSkill
|
||||
return card
|
||||
end
|
||||
|
||||
---@param spec CardSpec
|
||||
---@return Weapon
|
||||
function fk.CreateWeapon(spec)
|
||||
assert(type(spec.name) == "string" or type(spec.class_name) == "string")
|
||||
if not spec.name then spec.name = spec.class_name
|
||||
elseif not spec.class_name then spec.class_name = spec.name end
|
||||
if spec.suit then assert(type(spec.suit) == "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
|
||||
assert(type(spec.name) == "string" or type(spec.class_name) == "string")
|
||||
if not spec.name then spec.name = spec.class_name
|
||||
elseif not spec.class_name then spec.class_name = spec.name end
|
||||
if spec.suit then assert(type(spec.suit) == "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
|
||||
|
||||
local card = Weapon:new(spec.name, spec.suit, spec.number, spec.attack_range)
|
||||
return card
|
||||
local card = Weapon:new(spec.name, spec.suit, spec.number, spec.attack_range)
|
||||
card.skill = spec.skill or defaultCardSkill
|
||||
return card
|
||||
end
|
||||
|
||||
---@param spec CardSpec
|
||||
---@return Armor
|
||||
function fk.CreateArmor(spec)
|
||||
assert(type(spec.name) == "string" or type(spec.class_name) == "string")
|
||||
if not spec.name then spec.name = spec.class_name
|
||||
elseif not spec.class_name then spec.class_name = spec.name end
|
||||
if spec.suit then assert(type(spec.suit) == "number") end
|
||||
if spec.number then assert(type(spec.number) == "number") end
|
||||
assert(type(spec.name) == "string" or type(spec.class_name) == "string")
|
||||
if not spec.name then spec.name = spec.class_name
|
||||
elseif not spec.class_name then spec.class_name = spec.name end
|
||||
if spec.suit then assert(type(spec.suit) == "number") end
|
||||
if spec.number then assert(type(spec.number) == "number") end
|
||||
|
||||
local card = Armor:new(spec.name, spec.suit, spec.number)
|
||||
return card
|
||||
local card = Armor:new(spec.name, spec.suit, spec.number)
|
||||
card.skill = spec.skill or defaultCardSkill
|
||||
return card
|
||||
end
|
||||
|
||||
---@param spec CardSpec
|
||||
---@return DefensiveRide
|
||||
function fk.CreateDefensiveRide(spec)
|
||||
assert(type(spec.name) == "string" or type(spec.class_name) == "string")
|
||||
if not spec.name then spec.name = spec.class_name
|
||||
elseif not spec.class_name then spec.class_name = spec.name end
|
||||
if spec.suit then assert(type(spec.suit) == "number") end
|
||||
if spec.number then assert(type(spec.number) == "number") end
|
||||
assert(type(spec.name) == "string" or type(spec.class_name) == "string")
|
||||
if not spec.name then spec.name = spec.class_name
|
||||
elseif not spec.class_name then spec.class_name = spec.name end
|
||||
if spec.suit then assert(type(spec.suit) == "number") end
|
||||
if spec.number then assert(type(spec.number) == "number") end
|
||||
|
||||
local card = DefensiveRide:new(spec.name, spec.suit, spec.number)
|
||||
return card
|
||||
local card = DefensiveRide:new(spec.name, spec.suit, spec.number)
|
||||
card.skill = spec.skill or defaultCardSkill
|
||||
return card
|
||||
end
|
||||
|
||||
---@param spec CardSpec
|
||||
---@return OffensiveRide
|
||||
function fk.CreateOffensiveRide(spec)
|
||||
assert(type(spec.name) == "string" or type(spec.class_name) == "string")
|
||||
if not spec.name then spec.name = spec.class_name
|
||||
elseif not spec.class_name then spec.class_name = spec.name end
|
||||
if spec.suit then assert(type(spec.suit) == "number") end
|
||||
if spec.number then assert(type(spec.number) == "number") end
|
||||
assert(type(spec.name) == "string" or type(spec.class_name) == "string")
|
||||
if not spec.name then spec.name = spec.class_name
|
||||
elseif not spec.class_name then spec.class_name = spec.name end
|
||||
if spec.suit then assert(type(spec.suit) == "number") end
|
||||
if spec.number then assert(type(spec.number) == "number") end
|
||||
|
||||
local card = OffensiveRide:new(spec.name, spec.suit, spec.number)
|
||||
return card
|
||||
local card = OffensiveRide:new(spec.name, spec.suit, spec.number)
|
||||
card.skill = spec.skill or defaultCardSkill
|
||||
return card
|
||||
end
|
||||
|
||||
---@param spec CardSpec
|
||||
---@return Treasure
|
||||
function fk.CreateTreasure(spec)
|
||||
assert(type(spec.name) == "string" or type(spec.class_name) == "string")
|
||||
if not spec.name then spec.name = spec.class_name
|
||||
elseif not spec.class_name then spec.class_name = spec.name end
|
||||
if spec.suit then assert(type(spec.suit) == "number") end
|
||||
if spec.number then assert(type(spec.number) == "number") end
|
||||
assert(type(spec.name) == "string" or type(spec.class_name) == "string")
|
||||
if not spec.name then spec.name = spec.class_name
|
||||
elseif not spec.class_name then spec.class_name = spec.name end
|
||||
if spec.suit then assert(type(spec.suit) == "number") end
|
||||
if spec.number then assert(type(spec.number) == "number") end
|
||||
|
||||
local card = Treasure:new(spec.name, spec.suit, spec.number)
|
||||
return card
|
||||
end
|
||||
|
||||
---@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
|
||||
local card = Treasure:new(spec.name, spec.suit, spec.number)
|
||||
card.skill = spec.skill or defaultCardSkill
|
||||
return card
|
||||
end
|
||||
|
|
|
@ -10,7 +10,8 @@ class = require "middleclass"
|
|||
json = require "json"
|
||||
|
||||
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"
|
||||
|
||||
math.randomseed(os.time())
|
||||
|
|
|
@ -50,4 +50,24 @@ fk.EnterDying = 38
|
|||
fk.Dying = 39
|
||||
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")
|
||||
|
||||
function GameLogic:initialize(room)
|
||||
self.room = room
|
||||
self.skill_table = {} -- TriggerEvent --> TriggerSkill[]
|
||||
self.refresh_skill_table = {}
|
||||
self.skills = {} -- skillName[]
|
||||
self.event_stack = Stack:new()
|
||||
self.room = room
|
||||
self.skill_table = {} -- TriggerEvent --> TriggerSkill[]
|
||||
self.refresh_skill_table = {}
|
||||
self.skills = {} -- skillName[]
|
||||
self.event_stack = Stack:new()
|
||||
|
||||
self.role_table = {
|
||||
{ "lord" },
|
||||
{ "lord", "rebel" },
|
||||
{ "lord", "rebel", "renegade" },
|
||||
{ "lord", "loyalist", "rebel", "renegade" },
|
||||
{ "lord", "loyalist", "rebel", "rebel", "renegade" },
|
||||
{ "lord", "loyalist", "rebel", "rebel", "rebel", "renegade" },
|
||||
{ "lord", "loyalist", "loyalist", "rebel", "rebel", "rebel", "renegade" },
|
||||
{ "lord", "loyalist", "loyalist", "rebel", "rebel", "rebel", "rebel", "renegade" },
|
||||
}
|
||||
self.role_table = {
|
||||
{ "lord" },
|
||||
{ "lord", "rebel" },
|
||||
{ "lord", "rebel", "renegade" },
|
||||
{ "lord", "loyalist", "rebel", "renegade" },
|
||||
{ "lord", "loyalist", "rebel", "rebel", "renegade" },
|
||||
{ "lord", "loyalist", "rebel", "rebel", "rebel", "renegade" },
|
||||
{ "lord", "loyalist", "loyalist", "rebel", "rebel", "rebel", "renegade" },
|
||||
{ "lord", "loyalist", "loyalist", "rebel", "rebel", "rebel", "rebel", "renegade" },
|
||||
}
|
||||
end
|
||||
|
||||
function GameLogic:run()
|
||||
-- default logic
|
||||
table.shuffle(self.room.players)
|
||||
self:assignRoles()
|
||||
self.room:adjustSeats()
|
||||
-- default logic
|
||||
table.shuffle(self.room.players)
|
||||
self:assignRoles()
|
||||
self.room:adjustSeats()
|
||||
|
||||
self:chooseGenerals()
|
||||
self:prepareForStart()
|
||||
self:action()
|
||||
self:chooseGenerals()
|
||||
self:prepareForStart()
|
||||
self:action()
|
||||
end
|
||||
|
||||
function GameLogic:assignRoles()
|
||||
local room = self.room
|
||||
local n = #room.players
|
||||
local roles = self.role_table[n]
|
||||
table.shuffle(roles)
|
||||
local room = self.room
|
||||
local n = #room.players
|
||||
local roles = self.role_table[n]
|
||||
table.shuffle(roles)
|
||||
|
||||
for i = 1, n do
|
||||
local p = room.players[i]
|
||||
p.role = roles[i]
|
||||
if p.role == "lord" then
|
||||
room:broadcastProperty(p, "role")
|
||||
else
|
||||
room:notifyProperty(p, p, "role")
|
||||
end
|
||||
for i = 1, n do
|
||||
local p = room.players[i]
|
||||
p.role = roles[i]
|
||||
if p.role == "lord" then
|
||||
room:broadcastProperty(p, "role")
|
||||
else
|
||||
room:notifyProperty(p, p, "role")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function GameLogic:chooseGenerals()
|
||||
local room = self.room
|
||||
local function setPlayerGeneral(player, general)
|
||||
if Fk.generals[general] == nil then return end
|
||||
player.general = general
|
||||
self.room:notifyProperty(player, player, "general")
|
||||
end
|
||||
local lord = room:getLord()
|
||||
local lord_general = nil
|
||||
if lord ~= nil then
|
||||
room.current = lord
|
||||
local generals = Fk:getGeneralsRandomly(3)
|
||||
for i = 1, #generals do
|
||||
generals[i] = generals[i].name
|
||||
end
|
||||
lord_general = room:askForGeneral(lord, generals)
|
||||
setPlayerGeneral(lord, lord_general)
|
||||
room:broadcastProperty(lord, "general")
|
||||
local room = self.room
|
||||
local function setPlayerGeneral(player, general)
|
||||
if Fk.generals[general] == nil then return end
|
||||
player.general = general
|
||||
self.room:notifyProperty(player, player, "general")
|
||||
end
|
||||
local lord = room:getLord()
|
||||
local lord_general = nil
|
||||
if lord ~= nil then
|
||||
room.current = lord
|
||||
local generals = Fk:getGeneralsRandomly(3)
|
||||
for i = 1, #generals do
|
||||
generals[i] = generals[i].name
|
||||
end
|
||||
lord_general = room:askForGeneral(lord, generals)
|
||||
setPlayerGeneral(lord, lord_general)
|
||||
room:broadcastProperty(lord, "general")
|
||||
end
|
||||
|
||||
local nonlord = room:getOtherPlayers(lord)
|
||||
local generals = Fk:getGeneralsRandomly(#nonlord * 3, Fk.generals, {lord_general})
|
||||
table.shuffle(generals)
|
||||
for _, p in ipairs(nonlord) do
|
||||
local arg = {
|
||||
(table.remove(generals, 1)).name,
|
||||
(table.remove(generals, 1)).name,
|
||||
(table.remove(generals, 1)).name,
|
||||
}
|
||||
p.request_data = json.encode(arg)
|
||||
p.default_reply = arg[1]
|
||||
end
|
||||
local nonlord = room:getOtherPlayers(lord)
|
||||
local generals = Fk:getGeneralsRandomly(#nonlord * 3, Fk.generals, {lord_general})
|
||||
table.shuffle(generals)
|
||||
for _, p in ipairs(nonlord) do
|
||||
local arg = {
|
||||
(table.remove(generals, 1)).name,
|
||||
(table.remove(generals, 1)).name,
|
||||
(table.remove(generals, 1)).name,
|
||||
}
|
||||
p.request_data = json.encode(arg)
|
||||
p.default_reply = arg[1]
|
||||
end
|
||||
|
||||
room:doBroadcastRequest("AskForGeneral", nonlord)
|
||||
for _, p in ipairs(nonlord) do
|
||||
if p.general == "" and p.reply_ready then
|
||||
local general = json.decode(p.client_reply)[1]
|
||||
setPlayerGeneral(p, general)
|
||||
else
|
||||
setPlayerGeneral(p, p.default_reply)
|
||||
end
|
||||
p.default_reply = ""
|
||||
room:doBroadcastRequest("AskForGeneral", nonlord)
|
||||
for _, p in ipairs(nonlord) do
|
||||
if p.general == "" and p.reply_ready then
|
||||
local general = json.decode(p.client_reply)[1]
|
||||
setPlayerGeneral(p, general)
|
||||
else
|
||||
setPlayerGeneral(p, p.default_reply)
|
||||
end
|
||||
p.default_reply = ""
|
||||
end
|
||||
end
|
||||
|
||||
function GameLogic:prepareForStart()
|
||||
local room = self.room
|
||||
local players = room.players
|
||||
room.alive_players = {table.unpack(players)}
|
||||
for i = 1, #players - 1 do
|
||||
players[i].next = players[i + 1]
|
||||
local room = self.room
|
||||
local players = room.players
|
||||
room.alive_players = {table.unpack(players)}
|
||||
for i = 1, #players - 1 do
|
||||
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
|
||||
players[#players].next = players[1]
|
||||
room:broadcastProperty(p, "maxHp")
|
||||
room:broadcastProperty(p, "hp")
|
||||
|
||||
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
|
||||
-- TODO: add skills to player
|
||||
end
|
||||
|
||||
if p.role ~= "lord" then
|
||||
room:broadcastProperty(p, "general")
|
||||
elseif #players >= 5 then
|
||||
p.maxHp = p.maxHp + 1
|
||||
p.hp = p.hp + 1
|
||||
end
|
||||
room:broadcastProperty(p, "maxHp")
|
||||
room:broadcastProperty(p, "hp")
|
||||
-- TODO: prepare drawPile
|
||||
-- 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
|
||||
|
||||
-- TODO: add skills to player
|
||||
end
|
||||
|
||||
-- TODO: prepare drawPile
|
||||
-- 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
|
||||
self:addTriggerSkill(GameRule)
|
||||
for _, trig in ipairs(Fk.global_trigger) do
|
||||
self:addTriggerSkill(trig)
|
||||
end
|
||||
end
|
||||
|
||||
function GameLogic:action()
|
||||
self:trigger(fk.GameStart)
|
||||
local room = self.room
|
||||
self:trigger(fk.GameStart)
|
||||
local room = self.room
|
||||
|
||||
for _, p in ipairs(room.players) do
|
||||
self:trigger(fk.DrawInitialCards, p, { num = 4 })
|
||||
end
|
||||
for _, p in ipairs(room.players) do
|
||||
self:trigger(fk.DrawInitialCards, p, { num = 4 })
|
||||
end
|
||||
|
||||
while true do
|
||||
self:trigger(fk.TurnStart, room.current)
|
||||
if room.game_finished then break end
|
||||
room.current = room.current:getNextAlive()
|
||||
end
|
||||
while true do
|
||||
self:trigger(fk.TurnStart, room.current)
|
||||
if room.game_finished then break end
|
||||
room.current = room.current:getNextAlive()
|
||||
end
|
||||
end
|
||||
|
||||
---@param skill TriggerSkill
|
||||
function GameLogic:addTriggerSkill(skill)
|
||||
if skill == nil or table.contains(self.skills, skill.name) then
|
||||
return
|
||||
end
|
||||
if skill == nil or table.contains(self.skills, skill.name) then
|
||||
return
|
||||
end
|
||||
|
||||
table.insert(self.skills, skill.name)
|
||||
table.insert(self.skills, skill.name)
|
||||
|
||||
for _, event in ipairs(skill.refresh_events) do
|
||||
if self.refresh_skill_table[event] == nil then
|
||||
self.refresh_skill_table[event] = {}
|
||||
end
|
||||
table.insert(self.refresh_skill_table[event], skill)
|
||||
for _, event in ipairs(skill.refresh_events) do
|
||||
if self.refresh_skill_table[event] == nil then
|
||||
self.refresh_skill_table[event] = {}
|
||||
end
|
||||
table.insert(self.refresh_skill_table[event], skill)
|
||||
end
|
||||
|
||||
for _, event in ipairs(skill.events) do
|
||||
if self.skill_table[event] == nil then
|
||||
self.skill_table[event] = {}
|
||||
end
|
||||
table.insert(self.skill_table[event], skill)
|
||||
for _, event in ipairs(skill.events) do
|
||||
if self.skill_table[event] == nil then
|
||||
self.skill_table[event] = {}
|
||||
end
|
||||
table.insert(self.skill_table[event], skill)
|
||||
end
|
||||
|
||||
if skill.visible then
|
||||
if (Fk.related_skills[skill.name] == nil) then return end
|
||||
for _, s in ipairs(Fk.related_skills[skill.name]) do
|
||||
if (s.class == TriggerSkill) then
|
||||
self:addTriggerSkill(s)
|
||||
end
|
||||
end
|
||||
if skill.visible then
|
||||
if (Fk.related_skills[skill.name] == nil) then return end
|
||||
for _, s in ipairs(Fk.related_skills[skill.name]) do
|
||||
if (s.class == TriggerSkill) then
|
||||
self:addTriggerSkill(s)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
---@param event Event
|
||||
---@param target ServerPlayer
|
||||
---@param data any
|
||||
function GameLogic:trigger(event, target, data)
|
||||
local room = self.room
|
||||
local broken = false
|
||||
local skills = self.skill_table[event] or {}
|
||||
local skills_to_refresh = self.refresh_skill_table[event] or {}
|
||||
local player = target
|
||||
local room = self.room
|
||||
local broken = false
|
||||
local skills = self.skill_table[event] or {}
|
||||
local skills_to_refresh = self.refresh_skill_table[event] or {}
|
||||
local player = target
|
||||
|
||||
self.event_stack:push({event, target, data})
|
||||
self.event_stack:push({event, target, data})
|
||||
|
||||
if target == nil then
|
||||
for _, skill in ipairs(skills_to_refresh) do
|
||||
if skill:canRefresh(event, target, player, data) then
|
||||
skill:refresh(event, target, player, data)
|
||||
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
|
||||
if target == nil then
|
||||
for _, skill in ipairs(skills_to_refresh) do
|
||||
if skill:canRefresh(event, target, player, data) then
|
||||
skill:refresh(event, target, player, data)
|
||||
end
|
||||
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
|
||||
|
||||
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
|
||||
|
||||
player = player.next
|
||||
end until player == target
|
||||
end
|
||||
end
|
||||
|
||||
self.event_stack:pop()
|
||||
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
|
||||
|
||||
return GameLogic
|
||||
|
|
|
@ -6,63 +6,63 @@ fk.lobby_callback = {}
|
|||
local db = fk.ServerInstance:getDatabase()
|
||||
|
||||
function Lobby:initialize(_lobby)
|
||||
self.lobby = _lobby
|
||||
self.lobby.callback = function(_self, command, jsonData)
|
||||
local cb = fk.lobby_callback[command]
|
||||
if (type(cb) == "function") then
|
||||
cb(jsonData)
|
||||
else
|
||||
print("Lobby error: Unknown command " .. command);
|
||||
end
|
||||
self.lobby = _lobby
|
||||
self.lobby.callback = function(_self, command, jsonData)
|
||||
local cb = fk.lobby_callback[command]
|
||||
if (type(cb) == "function") then
|
||||
cb(jsonData)
|
||||
else
|
||||
print("Lobby error: Unknown command " .. command);
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
fk.lobby_callback["UpdateAvatar"] = function(jsonData)
|
||||
-- jsonData: [ int uid, string newavatar ]
|
||||
local data = json.decode(jsonData)
|
||||
local id, avatar = data[1], data[2]
|
||||
local sql = "UPDATE userinfo SET avatar='%s' WHERE id=%d;"
|
||||
Sql.exec(db, string.format(sql, avatar, id))
|
||||
local player = fk.ServerInstance:findPlayer(id)
|
||||
player:setAvatar(avatar)
|
||||
player:doNotify("UpdateAvatar", avatar)
|
||||
-- jsonData: [ int uid, string newavatar ]
|
||||
local data = json.decode(jsonData)
|
||||
local id, avatar = data[1], data[2]
|
||||
local sql = "UPDATE userinfo SET avatar='%s' WHERE id=%d;"
|
||||
Sql.exec(db, string.format(sql, avatar, id))
|
||||
local player = fk.ServerInstance:findPlayer(id)
|
||||
player:setAvatar(avatar)
|
||||
player:doNotify("UpdateAvatar", avatar)
|
||||
end
|
||||
|
||||
fk.lobby_callback["UpdatePassword"] = function(jsonData)
|
||||
-- jsonData: [ int uid, string oldpassword, int newpassword ]
|
||||
local data = json.decode(jsonData)
|
||||
local id, old, new = data[1], data[2], data[3]
|
||||
local sql_find = "SELECT password FROM userinfo WHERE id=%d;"
|
||||
local sql_update = "UPDATE userinfo SET password='%s' WHERE id=%d;"
|
||||
-- jsonData: [ int uid, string oldpassword, int newpassword ]
|
||||
local data = json.decode(jsonData)
|
||||
local id, old, new = data[1], data[2], data[3]
|
||||
local sql_find = "SELECT password FROM userinfo WHERE id=%d;"
|
||||
local sql_update = "UPDATE userinfo SET password='%s' WHERE id=%d;"
|
||||
|
||||
local passed = false
|
||||
local result = Sql.exec_select(db, string.format(sql_find, id))
|
||||
passed = (result["password"][1] == sha256(old))
|
||||
if passed then
|
||||
Sql.exec(db, string.format(sql_update, sha256(new), id))
|
||||
end
|
||||
local passed = false
|
||||
local result = Sql.exec_select(db, string.format(sql_find, id))
|
||||
passed = (result["password"][1] == sha256(old))
|
||||
if passed then
|
||||
Sql.exec(db, string.format(sql_update, sha256(new), id))
|
||||
end
|
||||
|
||||
local player = fk.ServerInstance:findPlayer(tonumber(id))
|
||||
player:doNotify("UpdatePassword", passed and "1" or "0")
|
||||
local player = fk.ServerInstance:findPlayer(tonumber(id))
|
||||
player:doNotify("UpdatePassword", passed and "1" or "0")
|
||||
end
|
||||
|
||||
fk.lobby_callback["CreateRoom"] = function(jsonData)
|
||||
-- jsonData: [ int uid, string name, int capacity ]
|
||||
local data = json.decode(jsonData)
|
||||
local owner = fk.ServerInstance:findPlayer(tonumber(data[1]))
|
||||
local roomName = data[2]
|
||||
local capacity = data[3]
|
||||
fk.ServerInstance:createRoom(owner, roomName, capacity)
|
||||
-- jsonData: [ int uid, string name, int capacity ]
|
||||
local data = json.decode(jsonData)
|
||||
local owner = fk.ServerInstance:findPlayer(tonumber(data[1]))
|
||||
local roomName = data[2]
|
||||
local capacity = data[3]
|
||||
fk.ServerInstance:createRoom(owner, roomName, capacity)
|
||||
end
|
||||
|
||||
fk.lobby_callback["EnterRoom"] = function(jsonData)
|
||||
-- jsonData: [ int uid, int roomId ]
|
||||
local data = json.decode(jsonData)
|
||||
local player = fk.ServerInstance:findPlayer(tonumber(data[1]))
|
||||
local room = fk.ServerInstance:findRoom(tonumber(data[2]))
|
||||
room:addPlayer(player)
|
||||
-- jsonData: [ int uid, int roomId ]
|
||||
local data = json.decode(jsonData)
|
||||
local player = fk.ServerInstance:findPlayer(tonumber(data[1]))
|
||||
local room = fk.ServerInstance:findRoom(tonumber(data[2]))
|
||||
room:addPlayer(player)
|
||||
end
|
||||
|
||||
function CreateRoom(_room)
|
||||
LobbyInstance = Lobby:new(_room)
|
||||
LobbyInstance = Lobby:new(_room)
|
||||
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")
|
||||
|
||||
function ServerPlayer:initialize(_self)
|
||||
Player.initialize(self)
|
||||
self.serverplayer = _self
|
||||
self.id = _self:getId()
|
||||
self.room = nil
|
||||
Player.initialize(self)
|
||||
self.serverplayer = _self
|
||||
self.id = _self:getId()
|
||||
self.room = nil
|
||||
|
||||
self.next = nil
|
||||
self.next = nil
|
||||
|
||||
-- Below are for doBroadcastRequest
|
||||
self.request_data = ""
|
||||
self.client_reply = ""
|
||||
self.default_reply = ""
|
||||
self.reply_ready = false
|
||||
self.phases = {}
|
||||
-- Below are for doBroadcastRequest
|
||||
self.request_data = ""
|
||||
self.client_reply = ""
|
||||
self.default_reply = ""
|
||||
self.reply_ready = false
|
||||
self.phases = {}
|
||||
end
|
||||
|
||||
---@return integer
|
||||
function ServerPlayer:getId()
|
||||
return self.id
|
||||
return self.id
|
||||
end
|
||||
|
||||
---@param command string
|
||||
---@param jsonData string
|
||||
function ServerPlayer:doNotify(command, jsonData)
|
||||
self.serverplayer:doNotify(command, jsonData)
|
||||
self.serverplayer:doNotify(command, jsonData)
|
||||
end
|
||||
|
||||
--- Send a request to client, and allow client to reply within *timeout* seconds.
|
||||
|
@ -45,10 +45,10 @@ end
|
|||
---@param jsonData string
|
||||
---@param timeout integer
|
||||
function ServerPlayer:doRequest(command, jsonData, timeout)
|
||||
timeout = timeout or self.room.timeout
|
||||
self.client_reply = ""
|
||||
self.reply_ready = false
|
||||
self.serverplayer:doRequest(command, jsonData, timeout)
|
||||
timeout = timeout or self.room.timeout
|
||||
self.client_reply = ""
|
||||
self.reply_ready = false
|
||||
self.serverplayer:doRequest(command, jsonData, timeout)
|
||||
end
|
||||
|
||||
--- Wait for at most *timeout* seconds for reply from client.
|
||||
|
@ -57,153 +57,153 @@ end
|
|||
---@param timeout integer @ seconds to wait
|
||||
---@return string @ JSON data
|
||||
function ServerPlayer:waitForReply(timeout)
|
||||
local result = ""
|
||||
if timeout == nil then
|
||||
result = self.serverplayer:waitForReply()
|
||||
else
|
||||
result = self.serverplayer:waitForReply(timeout)
|
||||
end
|
||||
self.request_data = ""
|
||||
self.client_reply = result
|
||||
if result ~= "" then self.reply_ready = true end
|
||||
return result
|
||||
local result = ""
|
||||
if timeout == nil then
|
||||
result = self.serverplayer:waitForReply()
|
||||
else
|
||||
result = self.serverplayer:waitForReply(timeout)
|
||||
end
|
||||
self.request_data = ""
|
||||
self.client_reply = result
|
||||
if result ~= "" then self.reply_ready = true end
|
||||
return result
|
||||
end
|
||||
|
||||
---@param skill Skill
|
||||
function ServerPlayer:hasSkill(skill)
|
||||
return table.contains(self.player_skills, skill)
|
||||
return table.contains(self.player_skills, skill)
|
||||
end
|
||||
|
||||
function ServerPlayer:isAlive()
|
||||
return self.dead == false
|
||||
return self.dead == false
|
||||
end
|
||||
|
||||
function ServerPlayer:getNextAlive()
|
||||
if #self.room.alive_players == 0 then
|
||||
return self
|
||||
end
|
||||
if #self.room.alive_players == 0 then
|
||||
return self
|
||||
end
|
||||
|
||||
local ret = self.next
|
||||
while ret.dead do
|
||||
ret = ret.next
|
||||
end
|
||||
return ret
|
||||
local ret = self.next
|
||||
while ret.dead do
|
||||
ret = ret.next
|
||||
end
|
||||
return ret
|
||||
end
|
||||
|
||||
function ServerPlayer:turnOver()
|
||||
self.faceup = not self.faceup
|
||||
self.room:broadcastProperty(self, "faceup")
|
||||
self.faceup = not self.faceup
|
||||
self.room:broadcastProperty(self, "faceup")
|
||||
|
||||
-- TODO: log
|
||||
self.room.logic:trigger(fk.TurnedOver, self)
|
||||
-- TODO: log
|
||||
self.room.logic:trigger(fk.TurnedOver, self)
|
||||
end
|
||||
|
||||
---@param from_phase Phase
|
||||
---@param to_phase Phase
|
||||
function ServerPlayer:changePhase(from_phase, to_phase)
|
||||
local room = self.room
|
||||
local logic = room.logic
|
||||
self.phase = Player.PhaseNone
|
||||
local room = self.room
|
||||
local logic = room.logic
|
||||
self.phase = Player.PhaseNone
|
||||
|
||||
local phase_change = {
|
||||
from = from_phase,
|
||||
to = to_phase
|
||||
}
|
||||
local phase_change = {
|
||||
from = from_phase,
|
||||
to = to_phase
|
||||
}
|
||||
|
||||
local skip = logic:trigger(fk.EventPhaseChanging, self, phase_change)
|
||||
if skip and to_phase ~= Player.NotActive then
|
||||
self.phase = from_phase
|
||||
return true
|
||||
end
|
||||
local skip = logic:trigger(fk.EventPhaseChanging, self, phase_change)
|
||||
if skip and to_phase ~= Player.NotActive then
|
||||
self.phase = from_phase
|
||||
return true
|
||||
end
|
||||
|
||||
self.phase = to_phase
|
||||
room:notifyProperty(self, self, "phase")
|
||||
self.phase = to_phase
|
||||
room:notifyProperty(self, self, "phase")
|
||||
|
||||
if #self.phases > 0 then
|
||||
table.remove(self.phases, 1)
|
||||
end
|
||||
|
||||
if not logic:trigger(fk.EventPhaseStart, self) then
|
||||
if self.phase ~= Player.NotActive then
|
||||
logic:trigger(fk.EventPhaseProceeding, self)
|
||||
end
|
||||
end
|
||||
if #self.phases > 0 then
|
||||
table.remove(self.phases, 1)
|
||||
end
|
||||
|
||||
if not logic:trigger(fk.EventPhaseStart, self) then
|
||||
if self.phase ~= Player.NotActive then
|
||||
logic:trigger(fk.EventPhaseEnd, self)
|
||||
logic:trigger(fk.EventPhaseProceeding, self)
|
||||
end
|
||||
end
|
||||
|
||||
return false
|
||||
if self.phase ~= Player.NotActive then
|
||||
logic:trigger(fk.EventPhaseEnd, self)
|
||||
end
|
||||
|
||||
return false
|
||||
end
|
||||
|
||||
---@param phase_table Phase[]
|
||||
function ServerPlayer:play(phase_table)
|
||||
phase_table = phase_table or {}
|
||||
if #phase_table > 0 then
|
||||
if not table.contains(phase_table, Player.NotActive) then
|
||||
table.insert(phase_table, Player.NotActive)
|
||||
end
|
||||
else
|
||||
phase_table = {
|
||||
Player.RoundStart, Player.Start,
|
||||
Player.Judge, Player.Draw, Player.Play, Player.Discard,
|
||||
Player.Finish, Player.NotActive,
|
||||
}
|
||||
phase_table = phase_table or {}
|
||||
if #phase_table > 0 then
|
||||
if not table.contains(phase_table, Player.NotActive) then
|
||||
table.insert(phase_table, Player.NotActive)
|
||||
end
|
||||
else
|
||||
phase_table = {
|
||||
Player.RoundStart, Player.Start,
|
||||
Player.Judge, Player.Draw, Player.Play, Player.Discard,
|
||||
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
|
||||
|
||||
self.phases = phase_table
|
||||
self.phase_state = {}
|
||||
self.phase_index = i
|
||||
local phase_change = {
|
||||
from = self.phase,
|
||||
to = phases[i]
|
||||
}
|
||||
|
||||
local phases = self.phases
|
||||
local phase_state = self.phase_state
|
||||
local room = self.room
|
||||
local logic = self.room.logic
|
||||
self.phase = Player.PhaseNone
|
||||
|
||||
for i = 1, #phases do
|
||||
phase_state[i] = {
|
||||
phase = phases[i],
|
||||
skipped = false
|
||||
}
|
||||
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
|
||||
|
||||
for i = 1, #phases do
|
||||
if self.dead then
|
||||
self:changePhase(self.phase, Player.NotActive)
|
||||
break
|
||||
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
|
||||
|
||||
self.phase_index = i
|
||||
local phase_change = {
|
||||
from = self.phase,
|
||||
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
|
||||
if self.phase ~= Player.NotActive then
|
||||
logic:trigger(fk.EventPhaseEnd, self)
|
||||
else break end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return ServerPlayer
|
||||
|
|
|
@ -10,6 +10,9 @@
|
|||
---@alias DyingStruct { 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
|
||||
|
||||
|
@ -21,6 +24,8 @@ fk.ReasonPut = 5
|
|||
fk.ReasonPutIntoDiscardPile = 6
|
||||
fk.ReasonPrey = 7
|
||||
fk.ReasonExchange = 8
|
||||
fk.ReasonUse = 9
|
||||
fk.ReasonResonpse = 10
|
||||
|
||||
---@alias DamageType integer
|
||||
|
||||
|
|
|
@ -1,118 +1,133 @@
|
|||
GameRule = fk.CreateTriggerSkill{
|
||||
name = "game_rule",
|
||||
events = {
|
||||
fk.GameStart, fk.DrawInitialCards, fk.TurnStart,
|
||||
fk.EventPhaseProceeding, fk.EventPhaseEnd, fk.EventPhaseChanging,
|
||||
},
|
||||
priority = 0,
|
||||
name = "game_rule",
|
||||
events = {
|
||||
fk.GameStart, fk.DrawInitialCards, fk.TurnStart,
|
||||
fk.EventPhaseProceeding, fk.EventPhaseEnd, fk.EventPhaseChanging,
|
||||
},
|
||||
priority = 0,
|
||||
|
||||
can_trigger = function(self, event, target, player, data)
|
||||
return (target == player) or (target == nil)
|
||||
end,
|
||||
can_trigger = function(self, event, target, player, data)
|
||||
return (target == player) or (target == nil)
|
||||
end,
|
||||
|
||||
on_trigger = function(self, event, target, player, data)
|
||||
if RoomInstance.tag["SkipGameRule"] then
|
||||
RoomInstance.tag["SkipGameRule"] = false
|
||||
return false
|
||||
on_trigger = function(self, event, target, player, data)
|
||||
if RoomInstance.tag["SkipGameRule"] then
|
||||
RoomInstance.tag["SkipGameRule"] = 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
|
||||
|
||||
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
|
||||
|
||||
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
|
||||
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()
|
||||
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"
|
||||
|
||||
Fk:loadTranslationTable{
|
||||
["standard"] = "标准包",
|
||||
["wei"] = "魏",
|
||||
["shu"] = "蜀",
|
||||
["wu"] = "吴",
|
||||
["qun"] = "群",
|
||||
["standard"] = "标准包",
|
||||
["wei"] = "魏",
|
||||
["shu"] = "蜀",
|
||||
["wu"] = "吴",
|
||||
["qun"] = "群",
|
||||
}
|
||||
|
||||
local caocao = General:new(extension, "caocao", "wei", 4)
|
||||
extension:addGeneral(caocao)
|
||||
Fk:loadTranslationTable{
|
||||
["caocao"] = "曹操",
|
||||
["caocao"] = "曹操",
|
||||
}
|
||||
|
||||
local simayi = General:new(extension, "simayi", "wei", 3)
|
||||
extension:addGeneral(simayi)
|
||||
Fk:loadTranslationTable{
|
||||
["simayi"] = "司马懿",
|
||||
["simayi"] = "司马懿",
|
||||
}
|
||||
|
||||
local xiahoudun = General:new(extension, "xiahoudun", "wei", 4)
|
||||
extension:addGeneral(xiahoudun)
|
||||
Fk:loadTranslationTable{
|
||||
["xiahoudun"] = "夏侯惇",
|
||||
["xiahoudun"] = "夏侯惇",
|
||||
}
|
||||
|
||||
local zhangliao = General:new(extension, "zhangliao", "wei", 4)
|
||||
extension:addGeneral(zhangliao)
|
||||
Fk:loadTranslationTable{
|
||||
["zhangliao"] = "张辽",
|
||||
["zhangliao"] = "张辽",
|
||||
}
|
||||
|
||||
local xuchu = General:new(extension, "xuchu", "wei", 4)
|
||||
extension:addGeneral(xuchu)
|
||||
Fk:loadTranslationTable{
|
||||
["xuchu"] = "许褚",
|
||||
["xuchu"] = "许褚",
|
||||
}
|
||||
|
||||
local guojia = General:new(extension, "guojia", "wei", 4)
|
||||
extension:addGeneral(guojia)
|
||||
Fk:loadTranslationTable{
|
||||
["guojia"] = "郭嘉",
|
||||
["guojia"] = "郭嘉",
|
||||
}
|
||||
|
||||
local zhenji = General:new(extension, "zhenji", "wei", 3)
|
||||
extension:addGeneral(zhenji)
|
||||
Fk:loadTranslationTable{
|
||||
["zhenji"] = "甄姬",
|
||||
["zhenji"] = "甄姬",
|
||||
}
|
||||
|
||||
local liubei = General:new(extension, "liubei", "shu", 4)
|
||||
extension:addGeneral(liubei)
|
||||
Fk:loadTranslationTable{
|
||||
["liubei"] = "刘备",
|
||||
["liubei"] = "刘备",
|
||||
}
|
||||
|
||||
local guanyu = General:new(extension, "guanyu", "shu", 4)
|
||||
extension:addGeneral(guanyu)
|
||||
Fk:loadTranslationTable{
|
||||
["guanyu"] = "关羽",
|
||||
["guanyu"] = "关羽",
|
||||
}
|
||||
|
||||
local zhangfei = General:new(extension, "zhangfei", "shu", 4)
|
||||
extension:addGeneral(zhangfei)
|
||||
Fk:loadTranslationTable{
|
||||
["zhangfei"] = "张飞",
|
||||
["zhangfei"] = "张飞",
|
||||
}
|
||||
|
||||
local zhugeliang = General:new(extension, "zhugeliang", "shu", 3)
|
||||
extension:addGeneral(zhugeliang)
|
||||
Fk:loadTranslationTable{
|
||||
["zhugeliang"] = "诸葛亮",
|
||||
["zhugeliang"] = "诸葛亮",
|
||||
}
|
||||
|
||||
local zhaoyun = General:new(extension, "zhaoyun", "shu", 4)
|
||||
extension:addGeneral(zhaoyun)
|
||||
Fk:loadTranslationTable{
|
||||
["zhaoyun"] = "赵云",
|
||||
["zhaoyun"] = "赵云",
|
||||
}
|
||||
|
||||
local machao = General:new(extension, "machao", "shu", 4)
|
||||
extension:addGeneral(machao)
|
||||
Fk:loadTranslationTable{
|
||||
["machao"] = "马超",
|
||||
["machao"] = "马超",
|
||||
}
|
||||
|
||||
local huangyueying = General:new(extension, "huangyueying", "shu", 3)
|
||||
extension:addGeneral(huangyueying)
|
||||
Fk:loadTranslationTable{
|
||||
["huangyueying"] = "黄月英",
|
||||
["huangyueying"] = "黄月英",
|
||||
}
|
||||
|
||||
local sunquan = General:new(extension, "sunquan", "wu", 4)
|
||||
extension:addGeneral(sunquan)
|
||||
Fk:loadTranslationTable{
|
||||
["sunquan"] = "孙权",
|
||||
["sunquan"] = "孙权",
|
||||
}
|
||||
|
||||
local ganning = General:new(extension, "ganning", "wu", 4)
|
||||
extension:addGeneral(ganning)
|
||||
Fk:loadTranslationTable{
|
||||
["ganning"] = "甘宁",
|
||||
["ganning"] = "甘宁",
|
||||
}
|
||||
|
||||
local lvmeng = General:new(extension, "lvmeng", "wu", 4)
|
||||
extension:addGeneral(lvmeng)
|
||||
Fk:loadTranslationTable{
|
||||
["lvmeng"] = "吕蒙",
|
||||
["lvmeng"] = "吕蒙",
|
||||
}
|
||||
|
||||
local huanggai = General:new(extension, "huanggai", "wu", 4)
|
||||
extension:addGeneral(huanggai)
|
||||
Fk:loadTranslationTable{
|
||||
["huanggai"] = "黄盖",
|
||||
["huanggai"] = "黄盖",
|
||||
}
|
||||
|
||||
local zhouyu = General:new(extension, "zhouyu", "wu", 3)
|
||||
extension:addGeneral(zhouyu)
|
||||
Fk:loadTranslationTable{
|
||||
["zhouyu"] = "周瑜",
|
||||
["zhouyu"] = "周瑜",
|
||||
}
|
||||
|
||||
local daqiao = General:new(extension, "daqiao", "wu", 3)
|
||||
extension:addGeneral(daqiao)
|
||||
Fk:loadTranslationTable{
|
||||
["daqiao"] = "大乔",
|
||||
["daqiao"] = "大乔",
|
||||
}
|
||||
|
||||
local luxun = General:new(extension, "luxun", "wu", 3)
|
||||
extension:addGeneral(luxun)
|
||||
Fk:loadTranslationTable{
|
||||
["luxun"] = "陆逊",
|
||||
["luxun"] = "陆逊",
|
||||
}
|
||||
|
||||
local sunshangxiang = General:new(extension, "sunshangxiang", "wu", 3)
|
||||
extension:addGeneral(sunshangxiang)
|
||||
Fk:loadTranslationTable{
|
||||
["sunshangxiang"] = "孙尚香",
|
||||
["sunshangxiang"] = "孙尚香",
|
||||
}
|
||||
|
||||
local huatuo = General:new(extension, "huatuo", "qun", 3)
|
||||
extension:addGeneral(huatuo)
|
||||
Fk:loadTranslationTable{
|
||||
["huatuo"] = "华佗",
|
||||
["huatuo"] = "华佗",
|
||||
}
|
||||
|
||||
local lvbu = General:new(extension, "lvbu", "qun", 4)
|
||||
extension:addGeneral(lvbu)
|
||||
Fk:loadTranslationTable{
|
||||
["lvbu"] = "吕布",
|
||||
["lvbu"] = "吕布",
|
||||
}
|
||||
|
||||
local diaochan = General:new(extension, "diaochan", "qun", 3)
|
||||
extension:addGeneral(diaochan)
|
||||
Fk:loadTranslationTable{
|
||||
["diaochan"] = "貂蝉",
|
||||
["diaochan"] = "貂蝉",
|
||||
}
|
||||
|
||||
return extension
|
||||
|
|
|
@ -1,14 +1,14 @@
|
|||
return {
|
||||
name = "standard",
|
||||
author = "official",
|
||||
description = "",
|
||||
collaborators = {
|
||||
program = {},
|
||||
designer = {},
|
||||
cv = {},
|
||||
illustrator = {},
|
||||
},
|
||||
name = "standard",
|
||||
author = "official",
|
||||
description = "",
|
||||
collaborators = {
|
||||
program = {},
|
||||
designer = {},
|
||||
cv = {},
|
||||
illustrator = {},
|
||||
},
|
||||
|
||||
dependencies = {},
|
||||
extra_files = {},
|
||||
dependencies = {},
|
||||
extra_files = {},
|
||||
}
|
||||
|
|
|
@ -2,510 +2,523 @@ local extension = Package:new("standard_cards", Package.CardPack)
|
|||
extension.metadata = require "packages.standard_cards.metadata"
|
||||
|
||||
Fk:loadTranslationTable{
|
||||
["standard_cards"] = "标+EX"
|
||||
["standard_cards"] = "标+EX"
|
||||
}
|
||||
|
||||
local slash = fk.CreateBasicCard{
|
||||
name = "slash",
|
||||
number = 7,
|
||||
suit = Card.Spade,
|
||||
name = "slash",
|
||||
number = 7,
|
||||
suit = Card.Spade,
|
||||
}
|
||||
Fk:loadTranslationTable{
|
||||
["slash"] = "杀",
|
||||
["slash"] = "杀",
|
||||
}
|
||||
|
||||
extension:addCards({
|
||||
slash,
|
||||
slash:clone(Card.Spade, 8),
|
||||
slash:clone(Card.Spade, 8),
|
||||
slash:clone(Card.Spade, 9),
|
||||
slash:clone(Card.Spade, 9),
|
||||
slash:clone(Card.Spade, 10),
|
||||
slash:clone(Card.Spade, 10),
|
||||
slash,
|
||||
slash:clone(Card.Spade, 8),
|
||||
slash:clone(Card.Spade, 8),
|
||||
slash:clone(Card.Spade, 9),
|
||||
slash:clone(Card.Spade, 9),
|
||||
slash:clone(Card.Spade, 10),
|
||||
slash:clone(Card.Spade, 10),
|
||||
|
||||
slash:clone(Card.Club, 2),
|
||||
slash:clone(Card.Club, 3),
|
||||
slash:clone(Card.Club, 4),
|
||||
slash:clone(Card.Club, 5),
|
||||
slash:clone(Card.Club, 6),
|
||||
slash:clone(Card.Club, 7),
|
||||
slash:clone(Card.Club, 8),
|
||||
slash:clone(Card.Club, 8),
|
||||
slash:clone(Card.Club, 9),
|
||||
slash:clone(Card.Club, 9),
|
||||
slash:clone(Card.Club, 10),
|
||||
slash:clone(Card.Club, 10),
|
||||
slash:clone(Card.Club, 11),
|
||||
slash:clone(Card.Club, 11),
|
||||
slash:clone(Card.Club, 2),
|
||||
slash:clone(Card.Club, 3),
|
||||
slash:clone(Card.Club, 4),
|
||||
slash:clone(Card.Club, 5),
|
||||
slash:clone(Card.Club, 6),
|
||||
slash:clone(Card.Club, 7),
|
||||
slash:clone(Card.Club, 8),
|
||||
slash:clone(Card.Club, 8),
|
||||
slash:clone(Card.Club, 9),
|
||||
slash:clone(Card.Club, 9),
|
||||
slash:clone(Card.Club, 10),
|
||||
slash:clone(Card.Club, 10),
|
||||
slash:clone(Card.Club, 11),
|
||||
slash:clone(Card.Club, 11),
|
||||
|
||||
slash:clone(Card.Heart, 10),
|
||||
slash:clone(Card.Heart, 10),
|
||||
slash:clone(Card.Heart, 11),
|
||||
slash:clone(Card.Heart, 10),
|
||||
slash:clone(Card.Heart, 10),
|
||||
slash:clone(Card.Heart, 11),
|
||||
|
||||
slash:clone(Card.Diamond, 6),
|
||||
slash:clone(Card.Diamond, 7),
|
||||
slash:clone(Card.Diamond, 8),
|
||||
slash:clone(Card.Diamond, 9),
|
||||
slash:clone(Card.Diamond, 10),
|
||||
slash:clone(Card.Diamond, 13),
|
||||
slash:clone(Card.Diamond, 6),
|
||||
slash:clone(Card.Diamond, 7),
|
||||
slash:clone(Card.Diamond, 8),
|
||||
slash:clone(Card.Diamond, 9),
|
||||
slash:clone(Card.Diamond, 10),
|
||||
slash:clone(Card.Diamond, 13),
|
||||
})
|
||||
|
||||
local jink = fk.CreateBasicCard{
|
||||
name = "jink",
|
||||
suit = Card.Heart,
|
||||
number = 2,
|
||||
name = "jink",
|
||||
suit = Card.Heart,
|
||||
number = 2,
|
||||
}
|
||||
Fk:loadTranslationTable{
|
||||
["jink"] = "闪",
|
||||
["jink"] = "闪",
|
||||
}
|
||||
|
||||
extension:addCards({
|
||||
jink,
|
||||
jink:clone(Card.Heart, 2),
|
||||
jink:clone(Card.Heart, 13),
|
||||
jink,
|
||||
jink:clone(Card.Heart, 2),
|
||||
jink:clone(Card.Heart, 13),
|
||||
|
||||
jink:clone(Card.Diamond, 2),
|
||||
jink:clone(Card.Diamond, 2),
|
||||
jink:clone(Card.Diamond, 3),
|
||||
jink:clone(Card.Diamond, 4),
|
||||
jink:clone(Card.Diamond, 5),
|
||||
jink:clone(Card.Diamond, 6),
|
||||
jink:clone(Card.Diamond, 7),
|
||||
jink:clone(Card.Diamond, 8),
|
||||
jink:clone(Card.Diamond, 9),
|
||||
jink:clone(Card.Diamond, 10),
|
||||
jink:clone(Card.Diamond, 11),
|
||||
jink:clone(Card.Diamond, 11),
|
||||
jink:clone(Card.Diamond, 2),
|
||||
jink:clone(Card.Diamond, 2),
|
||||
jink:clone(Card.Diamond, 3),
|
||||
jink:clone(Card.Diamond, 4),
|
||||
jink:clone(Card.Diamond, 5),
|
||||
jink:clone(Card.Diamond, 6),
|
||||
jink:clone(Card.Diamond, 7),
|
||||
jink:clone(Card.Diamond, 8),
|
||||
jink:clone(Card.Diamond, 9),
|
||||
jink:clone(Card.Diamond, 10),
|
||||
jink:clone(Card.Diamond, 11),
|
||||
jink:clone(Card.Diamond, 11),
|
||||
})
|
||||
|
||||
local peach = fk.CreateBasicCard{
|
||||
name = "peach",
|
||||
suit = Card.Heart,
|
||||
number = 3,
|
||||
name = "peach",
|
||||
suit = Card.Heart,
|
||||
number = 3,
|
||||
}
|
||||
Fk:loadTranslationTable{
|
||||
["peach"] = "桃",
|
||||
["peach"] = "桃",
|
||||
}
|
||||
|
||||
extension:addCards({
|
||||
peach,
|
||||
peach:clone(Card.Heart, 4),
|
||||
peach:clone(Card.Heart, 6),
|
||||
peach:clone(Card.Heart, 7),
|
||||
peach:clone(Card.Heart, 8),
|
||||
peach:clone(Card.Heart, 9),
|
||||
peach:clone(Card.Heart, 12),
|
||||
peach:clone(Card.Heart, 12),
|
||||
peach,
|
||||
peach:clone(Card.Heart, 4),
|
||||
peach:clone(Card.Heart, 6),
|
||||
peach:clone(Card.Heart, 7),
|
||||
peach:clone(Card.Heart, 8),
|
||||
peach:clone(Card.Heart, 9),
|
||||
peach:clone(Card.Heart, 12),
|
||||
peach:clone(Card.Heart, 12),
|
||||
})
|
||||
|
||||
local dismantlement = fk.CreateTrickCard{
|
||||
name = "dismantlement",
|
||||
suit = Card.Spade,
|
||||
number = 3,
|
||||
name = "dismantlement",
|
||||
suit = Card.Spade,
|
||||
number = 3,
|
||||
}
|
||||
Fk:loadTranslationTable{
|
||||
["dismantlement"] = "过河拆桥",
|
||||
["dismantlement"] = "过河拆桥",
|
||||
}
|
||||
|
||||
extension:addCards({
|
||||
dismantlement,
|
||||
dismantlement:clone(Card.Spade, 4),
|
||||
dismantlement:clone(Card.Spade, 12),
|
||||
dismantlement,
|
||||
dismantlement:clone(Card.Spade, 4),
|
||||
dismantlement:clone(Card.Spade, 12),
|
||||
|
||||
dismantlement:clone(Card.Club, 3),
|
||||
dismantlement:clone(Card.Club, 4),
|
||||
dismantlement:clone(Card.Club, 3),
|
||||
dismantlement:clone(Card.Club, 4),
|
||||
|
||||
dismantlement:clone(Card.Heart, 12),
|
||||
dismantlement:clone(Card.Heart, 12),
|
||||
})
|
||||
|
||||
local snatch = fk.CreateTrickCard{
|
||||
name = "snatch",
|
||||
suit = Card.Spade,
|
||||
number = 3,
|
||||
name = "snatch",
|
||||
suit = Card.Spade,
|
||||
number = 3,
|
||||
}
|
||||
Fk:loadTranslationTable{
|
||||
["snatch"] = "顺手牵羊",
|
||||
["snatch"] = "顺手牵羊",
|
||||
}
|
||||
|
||||
extension:addCards({
|
||||
snatch,
|
||||
snatch:clone(Card.Spade, 4),
|
||||
snatch:clone(Card.Spade, 11),
|
||||
snatch,
|
||||
snatch:clone(Card.Spade, 4),
|
||||
snatch:clone(Card.Spade, 11),
|
||||
|
||||
snatch:clone(Card.Diamond, 3),
|
||||
snatch:clone(Card.Diamond, 4),
|
||||
snatch:clone(Card.Diamond, 3),
|
||||
snatch:clone(Card.Diamond, 4),
|
||||
})
|
||||
|
||||
local duel = fk.CreateTrickCard{
|
||||
name = "duel",
|
||||
suit = Card.Spade,
|
||||
number = 1,
|
||||
name = "duel",
|
||||
suit = Card.Spade,
|
||||
number = 1,
|
||||
}
|
||||
Fk:loadTranslationTable{
|
||||
["duel"] = "决斗",
|
||||
["duel"] = "决斗",
|
||||
}
|
||||
|
||||
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{
|
||||
name = "collateral",
|
||||
suit = Card.Club,
|
||||
number = 12,
|
||||
name = "collateral",
|
||||
suit = Card.Club,
|
||||
number = 12,
|
||||
}
|
||||
Fk:loadTranslationTable{
|
||||
["collateral"] = "借刀杀人",
|
||||
["collateral"] = "借刀杀人",
|
||||
}
|
||||
|
||||
extension:addCards({
|
||||
collateral,
|
||||
collateral:clone(Card.Club, 13),
|
||||
collateral,
|
||||
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{
|
||||
name = "ex_nihilo",
|
||||
suit = Card.Heart,
|
||||
number = 7,
|
||||
name = "ex_nihilo",
|
||||
suit = Card.Heart,
|
||||
number = 7,
|
||||
skill = exNihiloSkill,
|
||||
}
|
||||
Fk:loadTranslationTable{
|
||||
["ex_nihilo"] = "无中生有",
|
||||
["ex_nihilo"] = "无中生有",
|
||||
}
|
||||
|
||||
extension:addCards({
|
||||
exNihilo,
|
||||
exNihilo:clone(Card.Heart, 8),
|
||||
exNihilo:clone(Card.Heart, 9),
|
||||
exNihilo:clone(Card.Heart, 11),
|
||||
exNihilo,
|
||||
exNihilo:clone(Card.Heart, 8),
|
||||
exNihilo:clone(Card.Heart, 9),
|
||||
exNihilo:clone(Card.Heart, 11),
|
||||
})
|
||||
|
||||
local nullification = fk.CreateTrickCard{
|
||||
name = "nullification",
|
||||
suit = Card.Spade,
|
||||
number = 11,
|
||||
name = "nullification",
|
||||
suit = Card.Spade,
|
||||
number = 11,
|
||||
}
|
||||
Fk:loadTranslationTable{
|
||||
["nullification"] = "无懈可击",
|
||||
["nullification"] = "无懈可击",
|
||||
}
|
||||
|
||||
extension:addCards({
|
||||
nullification,
|
||||
nullification,
|
||||
|
||||
nullification:clone(Card.Club, 12),
|
||||
nullification:clone(Card.Club, 13),
|
||||
nullification:clone(Card.Club, 12),
|
||||
nullification:clone(Card.Club, 13),
|
||||
|
||||
nullification:clone(Card.Diamond, 12),
|
||||
nullification:clone(Card.Diamond, 12),
|
||||
})
|
||||
|
||||
local savageAssault = fk.CreateTrickCard{
|
||||
name = "savage_assault",
|
||||
suit = Card.Spade,
|
||||
number = 7,
|
||||
name = "savage_assault",
|
||||
suit = Card.Spade,
|
||||
number = 7,
|
||||
}
|
||||
Fk:loadTranslationTable{
|
||||
["savage_assault"] = "南蛮入侵",
|
||||
["savage_assault"] = "南蛮入侵",
|
||||
}
|
||||
|
||||
extension:addCards({
|
||||
savageAssault,
|
||||
savageAssault:clone(Card.Spade, 13),
|
||||
savageAssault:clone(Card.Club, 7),
|
||||
savageAssault,
|
||||
savageAssault:clone(Card.Spade, 13),
|
||||
savageAssault:clone(Card.Club, 7),
|
||||
})
|
||||
|
||||
local archeryAttack = fk.CreateTrickCard{
|
||||
name = "archery_attack",
|
||||
suit = Card.Heart,
|
||||
number = 1,
|
||||
name = "archery_attack",
|
||||
suit = Card.Heart,
|
||||
number = 1,
|
||||
}
|
||||
Fk:loadTranslationTable{
|
||||
["archery_attack"] = "万箭齐发",
|
||||
["archery_attack"] = "万箭齐发",
|
||||
}
|
||||
|
||||
extension:addCards({
|
||||
archeryAttack,
|
||||
archeryAttack,
|
||||
})
|
||||
|
||||
local godSalvation = fk.CreateTrickCard{
|
||||
name = "god_salvation",
|
||||
suit = Card.Heart,
|
||||
number = 1,
|
||||
name = "god_salvation",
|
||||
suit = Card.Heart,
|
||||
number = 1,
|
||||
}
|
||||
Fk:loadTranslationTable{
|
||||
["god_salvation"] = "桃园结义",
|
||||
["god_salvation"] = "桃园结义",
|
||||
}
|
||||
|
||||
extension:addCards({
|
||||
godSalvation,
|
||||
godSalvation,
|
||||
})
|
||||
|
||||
local amazingGrace = fk.CreateTrickCard{
|
||||
name = "amazing_grace",
|
||||
suit = Card.Heart,
|
||||
number = 3,
|
||||
name = "amazing_grace",
|
||||
suit = Card.Heart,
|
||||
number = 3,
|
||||
}
|
||||
Fk:loadTranslationTable{
|
||||
["amazing_grace"] = "五谷丰登",
|
||||
["amazing_grace"] = "五谷丰登",
|
||||
}
|
||||
|
||||
extension:addCards({
|
||||
amazingGrace,
|
||||
amazingGrace:clone(Card.Heart, 4),
|
||||
amazingGrace,
|
||||
amazingGrace:clone(Card.Heart, 4),
|
||||
})
|
||||
|
||||
local lightning = fk.CreateDelayedTrickCard{
|
||||
name = "lightning",
|
||||
suit = Card.Spade,
|
||||
number = 1,
|
||||
name = "lightning",
|
||||
suit = Card.Spade,
|
||||
number = 1,
|
||||
}
|
||||
Fk:loadTranslationTable{
|
||||
["lightning"] = "闪电",
|
||||
["lightning"] = "闪电",
|
||||
}
|
||||
|
||||
extension:addCards({
|
||||
lightning,
|
||||
lightning:clone(Card.Heart, 12),
|
||||
lightning,
|
||||
lightning:clone(Card.Heart, 12),
|
||||
})
|
||||
|
||||
local indulgence = fk.CreateDelayedTrickCard{
|
||||
name = "indulgence",
|
||||
suit = Card.Spade,
|
||||
number = 6,
|
||||
name = "indulgence",
|
||||
suit = Card.Spade,
|
||||
number = 6,
|
||||
}
|
||||
Fk:loadTranslationTable{
|
||||
["indulgence"] = "乐不思蜀",
|
||||
["indulgence"] = "乐不思蜀",
|
||||
}
|
||||
|
||||
extension:addCards({
|
||||
indulgence,
|
||||
indulgence:clone(Card.Club, 6),
|
||||
indulgence:clone(Card.Heart, 6),
|
||||
indulgence,
|
||||
indulgence:clone(Card.Club, 6),
|
||||
indulgence:clone(Card.Heart, 6),
|
||||
})
|
||||
|
||||
local crossbow = fk.CreateWeapon{
|
||||
name = "crossbow",
|
||||
suit = Card.Club,
|
||||
number = 1,
|
||||
name = "crossbow",
|
||||
suit = Card.Club,
|
||||
number = 1,
|
||||
}
|
||||
Fk:loadTranslationTable{
|
||||
["crossbow"] = "诸葛连弩",
|
||||
["crossbow"] = "诸葛连弩",
|
||||
}
|
||||
|
||||
extension:addCards({
|
||||
crossbow,
|
||||
crossbow:clone(Card.Diamond, 1),
|
||||
crossbow,
|
||||
crossbow:clone(Card.Diamond, 1),
|
||||
})
|
||||
|
||||
local qingGang = fk.CreateWeapon{
|
||||
name = "qinggang_sword",
|
||||
suit = Card.Spade,
|
||||
number = 6,
|
||||
name = "qinggang_sword",
|
||||
suit = Card.Spade,
|
||||
number = 6,
|
||||
}
|
||||
Fk:loadTranslationTable{
|
||||
["qinggang_sword"] = "青釭剑",
|
||||
["qinggang_sword"] = "青釭剑",
|
||||
}
|
||||
|
||||
extension:addCards({
|
||||
qingGang,
|
||||
qingGang,
|
||||
})
|
||||
|
||||
local iceSword = fk.CreateWeapon{
|
||||
name = "ice_sword",
|
||||
suit = Card.Spade,
|
||||
number = 2,
|
||||
name = "ice_sword",
|
||||
suit = Card.Spade,
|
||||
number = 2,
|
||||
}
|
||||
Fk:loadTranslationTable{
|
||||
["ice_sword"] = "寒冰剑",
|
||||
["ice_sword"] = "寒冰剑",
|
||||
}
|
||||
|
||||
extension:addCards({
|
||||
iceSword,
|
||||
iceSword,
|
||||
})
|
||||
|
||||
local doubleSwords = fk.CreateWeapon{
|
||||
name = "double_swords",
|
||||
suit = Card.Spade,
|
||||
number = 2,
|
||||
name = "double_swords",
|
||||
suit = Card.Spade,
|
||||
number = 2,
|
||||
}
|
||||
Fk:loadTranslationTable{
|
||||
["double_swords"] = "雌雄双股剑",
|
||||
["double_swords"] = "雌雄双股剑",
|
||||
}
|
||||
|
||||
extension:addCards({
|
||||
doubleSwords,
|
||||
doubleSwords,
|
||||
})
|
||||
|
||||
local blade = fk.CreateWeapon{
|
||||
name = "blade",
|
||||
suit = Card.Spade,
|
||||
number = 5,
|
||||
name = "blade",
|
||||
suit = Card.Spade,
|
||||
number = 5,
|
||||
}
|
||||
Fk:loadTranslationTable{
|
||||
["blade"] = "青龙偃月刀",
|
||||
["blade"] = "青龙偃月刀",
|
||||
}
|
||||
|
||||
extension:addCards({
|
||||
blade,
|
||||
blade,
|
||||
})
|
||||
|
||||
local spear = fk.CreateWeapon{
|
||||
name = "spear",
|
||||
suit = Card.Spade,
|
||||
number = 12,
|
||||
name = "spear",
|
||||
suit = Card.Spade,
|
||||
number = 12,
|
||||
}
|
||||
Fk:loadTranslationTable{
|
||||
["spear"] = "丈八蛇矛",
|
||||
["spear"] = "丈八蛇矛",
|
||||
}
|
||||
|
||||
extension:addCards({
|
||||
spear,
|
||||
spear,
|
||||
})
|
||||
|
||||
local axe = fk.CreateWeapon{
|
||||
name = "axe",
|
||||
suit = Card.Diamond,
|
||||
number = 5,
|
||||
name = "axe",
|
||||
suit = Card.Diamond,
|
||||
number = 5,
|
||||
}
|
||||
Fk:loadTranslationTable{
|
||||
["axe"] = "贯石斧",
|
||||
["axe"] = "贯石斧",
|
||||
}
|
||||
|
||||
extension:addCards({
|
||||
axe,
|
||||
axe,
|
||||
})
|
||||
|
||||
local halberd = fk.CreateWeapon{
|
||||
name = "halberd",
|
||||
suit = Card.Diamond,
|
||||
number = 12,
|
||||
name = "halberd",
|
||||
suit = Card.Diamond,
|
||||
number = 12,
|
||||
}
|
||||
Fk:loadTranslationTable{
|
||||
["halberd"] = "方天画戟",
|
||||
["halberd"] = "方天画戟",
|
||||
}
|
||||
|
||||
extension:addCards({
|
||||
halberd,
|
||||
halberd,
|
||||
})
|
||||
|
||||
local kylinBow = fk.CreateWeapon{
|
||||
name = "kylin_bow",
|
||||
suit = Card.Heart,
|
||||
number = 5,
|
||||
name = "kylin_bow",
|
||||
suit = Card.Heart,
|
||||
number = 5,
|
||||
}
|
||||
Fk:loadTranslationTable{
|
||||
["kylin_bow"] = "麒麟弓",
|
||||
["kylin_bow"] = "麒麟弓",
|
||||
}
|
||||
|
||||
extension:addCards({
|
||||
kylinBow,
|
||||
kylinBow,
|
||||
})
|
||||
|
||||
local eightDiagram = fk.CreateArmor{
|
||||
name = "eight_diagram",
|
||||
suit = Card.Spade,
|
||||
number = 2,
|
||||
name = "eight_diagram",
|
||||
suit = Card.Spade,
|
||||
number = 2,
|
||||
}
|
||||
Fk:loadTranslationTable{
|
||||
["eight_diagram"] = "八卦阵",
|
||||
["eight_diagram"] = "八卦阵",
|
||||
}
|
||||
|
||||
extension:addCards({
|
||||
eightDiagram,
|
||||
eightDiagram:clone(Card.Club, 2),
|
||||
eightDiagram,
|
||||
eightDiagram:clone(Card.Club, 2),
|
||||
})
|
||||
|
||||
local niohShield = fk.CreateArmor{
|
||||
name = "nioh_shield",
|
||||
suit = Card.Club,
|
||||
number = 2,
|
||||
name = "nioh_shield",
|
||||
suit = Card.Club,
|
||||
number = 2,
|
||||
}
|
||||
Fk:loadTranslationTable{
|
||||
["nioh_shield"] = "仁王盾",
|
||||
["nioh_shield"] = "仁王盾",
|
||||
}
|
||||
|
||||
extension:addCards({
|
||||
niohShield,
|
||||
niohShield,
|
||||
})
|
||||
|
||||
local diLu = fk.CreateDefensiveRide{
|
||||
name = "dilu",
|
||||
suit = Card.Club,
|
||||
number = 5,
|
||||
name = "dilu",
|
||||
suit = Card.Club,
|
||||
number = 5,
|
||||
}
|
||||
Fk:loadTranslationTable{
|
||||
["dilu"] = "的卢",
|
||||
["dilu"] = "的卢",
|
||||
}
|
||||
|
||||
extension:addCards({
|
||||
diLu,
|
||||
diLu,
|
||||
})
|
||||
|
||||
local jueYing = fk.CreateDefensiveRide{
|
||||
name = "jueying",
|
||||
suit = Card.Spade,
|
||||
number = 5,
|
||||
name = "jueying",
|
||||
suit = Card.Spade,
|
||||
number = 5,
|
||||
}
|
||||
Fk:loadTranslationTable{
|
||||
["jueying"] = "绝影",
|
||||
["jueying"] = "绝影",
|
||||
}
|
||||
|
||||
extension:addCards({
|
||||
jueYing,
|
||||
jueYing,
|
||||
})
|
||||
|
||||
local zhuaHuangFeiDian = fk.CreateDefensiveRide{
|
||||
name = "zhuahuangfeidian",
|
||||
suit = Card.Heart,
|
||||
number = 13,
|
||||
name = "zhuahuangfeidian",
|
||||
suit = Card.Heart,
|
||||
number = 13,
|
||||
}
|
||||
Fk:loadTranslationTable{
|
||||
["zhuahuangfeidian"] = "爪黄飞电",
|
||||
["zhuahuangfeidian"] = "爪黄飞电",
|
||||
}
|
||||
|
||||
extension:addCards({
|
||||
zhuaHuangFeiDian,
|
||||
zhuaHuangFeiDian,
|
||||
})
|
||||
|
||||
local chiTu = fk.CreateOffensiveRide{
|
||||
name = "chitu",
|
||||
suit = Card.Heart,
|
||||
number = 5,
|
||||
name = "chitu",
|
||||
suit = Card.Heart,
|
||||
number = 5,
|
||||
}
|
||||
Fk:loadTranslationTable{
|
||||
["chitu"] = "赤兔",
|
||||
["chitu"] = "赤兔",
|
||||
}
|
||||
|
||||
extension:addCards({
|
||||
chiTu,
|
||||
chiTu,
|
||||
})
|
||||
|
||||
local daYuan = fk.CreateOffensiveRide{
|
||||
name = "dayuan",
|
||||
suit = Card.Spade,
|
||||
number = 13,
|
||||
name = "dayuan",
|
||||
suit = Card.Spade,
|
||||
number = 13,
|
||||
}
|
||||
Fk:loadTranslationTable{
|
||||
["dayuan"] = "大宛",
|
||||
["dayuan"] = "大宛",
|
||||
}
|
||||
|
||||
extension:addCards({
|
||||
daYuan,
|
||||
daYuan,
|
||||
})
|
||||
|
||||
local ziXing = fk.CreateOffensiveRide{
|
||||
name = "zixing",
|
||||
suit = Card.Heart,
|
||||
number = 5,
|
||||
name = "zixing",
|
||||
suit = Card.Heart,
|
||||
number = 5,
|
||||
}
|
||||
Fk:loadTranslationTable{
|
||||
["zixing"] = "紫骍",
|
||||
["zixing"] = "紫骍",
|
||||
}
|
||||
|
||||
extension:addCards({
|
||||
ziXing,
|
||||
ziXing,
|
||||
})
|
||||
|
||||
return extension
|
||||
|
|
|
@ -1,14 +1,14 @@
|
|||
return {
|
||||
name = "standard_cards",
|
||||
author = "official",
|
||||
description = "",
|
||||
collaborators = {
|
||||
program = {},
|
||||
designer = {},
|
||||
cv = {},
|
||||
illustrator = {},
|
||||
},
|
||||
name = "standard_cards",
|
||||
author = "official",
|
||||
description = "",
|
||||
collaborators = {
|
||||
program = {},
|
||||
designer = {},
|
||||
cv = {},
|
||||
illustrator = {},
|
||||
},
|
||||
|
||||
dependencies = {},
|
||||
extra_files = {},
|
||||
dependencies = {},
|
||||
extra_files = {},
|
||||
}
|
||||
|
|
|
@ -1,13 +1,13 @@
|
|||
import QtQuick 2.15
|
||||
|
||||
QtObject {
|
||||
// Client configuration
|
||||
// Client configuration
|
||||
|
||||
// Player property of client
|
||||
property string screenName: ""
|
||||
property string password: ""
|
||||
// Player property of client
|
||||
property string screenName: ""
|
||||
property string password: ""
|
||||
|
||||
// Client data
|
||||
property int roomCapacity: 0
|
||||
property int roomTimeout: 0
|
||||
// Client data
|
||||
property int roomCapacity: 0
|
||||
property int roomTimeout: 0
|
||||
}
|
||||
|
|
|
@ -3,62 +3,62 @@ import QtQuick.Controls 2.0
|
|||
import QtQuick.Layouts 1.15
|
||||
|
||||
Item {
|
||||
id: root
|
||||
id: root
|
||||
|
||||
width: childrenRect.width
|
||||
height: childrenRect.height
|
||||
width: childrenRect.width
|
||||
height: childrenRect.height
|
||||
|
||||
signal finished()
|
||||
signal finished()
|
||||
|
||||
ColumnLayout {
|
||||
spacing: 20
|
||||
ColumnLayout {
|
||||
spacing: 20
|
||||
|
||||
RowLayout {
|
||||
anchors.rightMargin: 8
|
||||
spacing: 16
|
||||
Text {
|
||||
text: "Room Name"
|
||||
}
|
||||
TextField {
|
||||
id: roomName
|
||||
font.pixelSize: 18
|
||||
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: "Room Name"
|
||||
}
|
||||
TextField {
|
||||
id: roomName
|
||||
font.pixelSize: 18
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,98 +3,98 @@ import QtQuick.Controls 2.0
|
|||
import QtQuick.Layouts 1.15
|
||||
|
||||
Item {
|
||||
id: root
|
||||
id: root
|
||||
|
||||
width: childrenRect.width
|
||||
height: childrenRect.height
|
||||
width: childrenRect.width
|
||||
height: childrenRect.height
|
||||
|
||||
signal finished()
|
||||
signal finished()
|
||||
|
||||
ColumnLayout {
|
||||
spacing: 20
|
||||
ColumnLayout {
|
||||
spacing: 20
|
||||
|
||||
RowLayout {
|
||||
anchors.rightMargin: 8
|
||||
spacing: 16
|
||||
Text {
|
||||
text: "Username"
|
||||
}
|
||||
Text {
|
||||
text: Self.screenName
|
||||
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: "Username"
|
||||
}
|
||||
Text {
|
||||
text: Self.screenName
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import QtQuick 2.0
|
||||
|
||||
Text {
|
||||
text: "dsdsd"
|
||||
text: "dsdsd"
|
||||
}
|
66
qml/Logic.js
66
qml/Logic.js
|
@ -1,53 +1,53 @@
|
|||
var callbacks = {};
|
||||
|
||||
callbacks["NetworkDelayTest"] = function(jsonData) {
|
||||
ClientInstance.notifyServer("Setup", JSON.stringify([
|
||||
config.screenName,
|
||||
config.password
|
||||
]));
|
||||
ClientInstance.notifyServer("Setup", JSON.stringify([
|
||||
config.screenName,
|
||||
config.password
|
||||
]));
|
||||
}
|
||||
|
||||
callbacks["ErrorMsg"] = function(jsonData) {
|
||||
console.log("ERROR: " + jsonData);
|
||||
toast.show(jsonData, 5000);
|
||||
mainWindow.busy = false;
|
||||
console.log("ERROR: " + jsonData);
|
||||
toast.show(jsonData, 5000);
|
||||
mainWindow.busy = false;
|
||||
}
|
||||
|
||||
callbacks["BackToStart"] = function(jsonData) {
|
||||
while (mainStack.depth > 1) {
|
||||
mainStack.pop();
|
||||
}
|
||||
while (mainStack.depth > 1) {
|
||||
mainStack.pop();
|
||||
}
|
||||
}
|
||||
|
||||
callbacks["EnterLobby"] = function(jsonData) {
|
||||
// depth == 1 means the lobby page is not present in mainStack
|
||||
if (mainStack.depth === 1) {
|
||||
mainStack.push(lobby);
|
||||
} else {
|
||||
mainStack.pop();
|
||||
}
|
||||
mainWindow.busy = false;
|
||||
// depth == 1 means the lobby page is not present in mainStack
|
||||
if (mainStack.depth === 1) {
|
||||
mainStack.push(lobby);
|
||||
} else {
|
||||
mainStack.pop();
|
||||
}
|
||||
mainWindow.busy = false;
|
||||
}
|
||||
|
||||
callbacks["EnterRoom"] = function(jsonData) {
|
||||
// jsonData: int capacity, int timeout
|
||||
let data = JSON.parse(jsonData);
|
||||
config.roomCapacity = data[0];
|
||||
config.roomTimeout = data[1];
|
||||
mainStack.push(room);
|
||||
mainWindow.busy = false;
|
||||
// jsonData: int capacity, int timeout
|
||||
let data = JSON.parse(jsonData);
|
||||
config.roomCapacity = data[0];
|
||||
config.roomTimeout = data[1];
|
||||
mainStack.push(room);
|
||||
mainWindow.busy = false;
|
||||
}
|
||||
|
||||
callbacks["UpdateRoomList"] = function(jsonData) {
|
||||
let current = mainStack.currentItem; // should be lobby
|
||||
current.roomModel.clear();
|
||||
JSON.parse(jsonData).forEach(function(room) {
|
||||
current.roomModel.append({
|
||||
roomId: room[0],
|
||||
roomName: room[1],
|
||||
gameMode: room[2],
|
||||
playerNum: room[3],
|
||||
capacity: room[4],
|
||||
});
|
||||
let current = mainStack.currentItem; // should be lobby
|
||||
current.roomModel.clear();
|
||||
JSON.parse(jsonData).forEach(function(room) {
|
||||
current.roomModel.append({
|
||||
roomId: room[0],
|
||||
roomName: room[1],
|
||||
gameMode: room[2],
|
||||
playerNum: room[3],
|
||||
capacity: room[4],
|
||||
});
|
||||
});
|
||||
}
|
||||
|
|
|
@ -4,49 +4,49 @@ import QtQuick.Controls 2.0
|
|||
import "RoomElement"
|
||||
|
||||
Item {
|
||||
id: root
|
||||
id: root
|
||||
|
||||
property bool loaded: false
|
||||
property bool loaded: false
|
||||
|
||||
ListView {
|
||||
width: Math.floor(root.width / 98) * 98
|
||||
height: parent.height
|
||||
anchors.centerIn: parent
|
||||
ScrollBar.vertical: ScrollBar {}
|
||||
model: ListModel {
|
||||
id: packages
|
||||
}
|
||||
ListView {
|
||||
width: Math.floor(root.width / 98) * 98
|
||||
height: parent.height
|
||||
anchors.centerIn: parent
|
||||
ScrollBar.vertical: ScrollBar {}
|
||||
model: ListModel {
|
||||
id: packages
|
||||
}
|
||||
|
||||
delegate: ColumnLayout {
|
||||
Text { text: Backend.translate(name) }
|
||||
GridLayout {
|
||||
columns: root.width / 98
|
||||
Repeater {
|
||||
model: JSON.parse(Backend.getCards(name))
|
||||
CardItem {
|
||||
autoBack: false
|
||||
Component.onCompleted: {
|
||||
let data = JSON.parse(Backend.getCardData(modelData));
|
||||
setData(data);
|
||||
}
|
||||
}
|
||||
}
|
||||
delegate: ColumnLayout {
|
||||
Text { text: Backend.translate(name) }
|
||||
GridLayout {
|
||||
columns: root.width / 98
|
||||
Repeater {
|
||||
model: JSON.parse(Backend.callLuaFunction("GetCards", [name]))
|
||||
CardItem {
|
||||
autoBack: false
|
||||
Component.onCompleted: {
|
||||
let data = JSON.parse(Backend.callLuaFunction("GetCardData", [modelData]));
|
||||
setData(data);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Button {
|
||||
text: "Quit"
|
||||
anchors.right: parent.right
|
||||
onClicked: {
|
||||
mainStack.pop();
|
||||
}
|
||||
Button {
|
||||
text: "Quit"
|
||||
anchors.right: parent.right
|
||||
onClicked: {
|
||||
mainStack.pop();
|
||||
}
|
||||
}
|
||||
|
||||
function loadPackages() {
|
||||
if (loaded) return;
|
||||
let packs = JSON.parse(Backend.getAllCardPack());
|
||||
packs.forEach((name) => packages.append({ name: name }));
|
||||
loaded = true;
|
||||
}
|
||||
function loadPackages() {
|
||||
if (loaded) return;
|
||||
let packs = JSON.parse(Backend.callLuaFunction("GetAllCardPack", []));
|
||||
packs.forEach((name) => packages.append({ name: name }));
|
||||
loaded = true;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,50 +4,50 @@ import QtQuick.Controls 2.0
|
|||
import "RoomElement"
|
||||
|
||||
Item {
|
||||
id: root
|
||||
id: root
|
||||
|
||||
property bool loaded: false
|
||||
property bool loaded: false
|
||||
|
||||
ListView {
|
||||
width: Math.floor(root.width / 98) * 98
|
||||
height: parent.height
|
||||
anchors.centerIn: parent
|
||||
ScrollBar.vertical: ScrollBar {}
|
||||
model: ListModel {
|
||||
id: packages
|
||||
}
|
||||
ListView {
|
||||
width: Math.floor(root.width / 98) * 98
|
||||
height: parent.height
|
||||
anchors.centerIn: parent
|
||||
ScrollBar.vertical: ScrollBar {}
|
||||
model: ListModel {
|
||||
id: packages
|
||||
}
|
||||
|
||||
delegate: ColumnLayout {
|
||||
Text { text: Backend.translate(name) }
|
||||
GridLayout {
|
||||
columns: root.width / 98
|
||||
Repeater {
|
||||
model: JSON.parse(Backend.getGenerals(name))
|
||||
GeneralCardItem {
|
||||
autoBack: false
|
||||
Component.onCompleted: {
|
||||
let data = JSON.parse(Backend.getGeneralData(modelData));
|
||||
name = modelData;
|
||||
kingdom = data.kingdom;
|
||||
}
|
||||
}
|
||||
}
|
||||
delegate: ColumnLayout {
|
||||
Text { text: Backend.translate(name) }
|
||||
GridLayout {
|
||||
columns: root.width / 98
|
||||
Repeater {
|
||||
model: JSON.parse(Backend.callLuaFunction("GetGenerals", [name]))
|
||||
GeneralCardItem {
|
||||
autoBack: false
|
||||
Component.onCompleted: {
|
||||
let data = JSON.parse(Backend.callLuaFunction("GetGeneralData", [modelData]));
|
||||
name = modelData;
|
||||
kingdom = data.kingdom;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Button {
|
||||
text: "Quit"
|
||||
anchors.right: parent.right
|
||||
onClicked: {
|
||||
mainStack.pop();
|
||||
}
|
||||
Button {
|
||||
text: "Quit"
|
||||
anchors.right: parent.right
|
||||
onClicked: {
|
||||
mainStack.pop();
|
||||
}
|
||||
}
|
||||
|
||||
function loadPackages() {
|
||||
if (loaded) return;
|
||||
let packs = JSON.parse(Backend.getAllGeneralPack());
|
||||
packs.forEach((name) => packages.append({ name: name }));
|
||||
loaded = true;
|
||||
}
|
||||
function loadPackages() {
|
||||
if (loaded) return;
|
||||
let packs = JSON.parse(Backend.callLuaFunction("GetAllGeneralPack", []));
|
||||
packs.forEach((name) => packages.append({ name: name }));
|
||||
loaded = true;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,50 +2,50 @@ import QtQuick 2.15
|
|||
import QtQuick.Controls 2.0
|
||||
|
||||
Item {
|
||||
id: root
|
||||
id: root
|
||||
|
||||
Frame {
|
||||
id: join_server
|
||||
anchors.centerIn: parent
|
||||
Column {
|
||||
spacing: 8
|
||||
TextField {
|
||||
id: server_addr
|
||||
text: "127.0.0.1"
|
||||
}
|
||||
TextField {
|
||||
id: screenNameEdit
|
||||
text: "player"
|
||||
}
|
||||
/*TextField {
|
||||
id: avatarEdit
|
||||
text: "liubei"
|
||||
}*/
|
||||
TextField {
|
||||
id: passwordEdit
|
||||
text: ""
|
||||
echoMode: TextInput.Password
|
||||
passwordCharacter: "*"
|
||||
}
|
||||
Button {
|
||||
text: "Join Server"
|
||||
onClicked: {
|
||||
config.screenName = screenNameEdit.text;
|
||||
config.password = passwordEdit.text;
|
||||
mainWindow.busy = true;
|
||||
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");
|
||||
}
|
||||
}
|
||||
Frame {
|
||||
id: join_server
|
||||
anchors.centerIn: parent
|
||||
Column {
|
||||
spacing: 8
|
||||
TextField {
|
||||
id: server_addr
|
||||
text: "127.0.0.1"
|
||||
}
|
||||
TextField {
|
||||
id: screenNameEdit
|
||||
text: "player"
|
||||
}
|
||||
/*TextField {
|
||||
id: avatarEdit
|
||||
text: "liubei"
|
||||
}*/
|
||||
TextField {
|
||||
id: passwordEdit
|
||||
text: ""
|
||||
echoMode: TextInput.Password
|
||||
passwordCharacter: "*"
|
||||
}
|
||||
Button {
|
||||
text: "Join Server"
|
||||
onClicked: {
|
||||
config.screenName = screenNameEdit.text;
|
||||
config.password = passwordEdit.text;
|
||||
mainWindow.busy = true;
|
||||
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");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,154 +5,154 @@ import QtQuick.Layouts 1.15
|
|||
import "Logic.js" as Logic
|
||||
|
||||
Item {
|
||||
id: root
|
||||
property alias roomModel: roomModel
|
||||
Component {
|
||||
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
|
||||
}
|
||||
id: root
|
||||
property alias roomModel: roomModel
|
||||
Component {
|
||||
id: roomDelegate
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
width: roomList.width * 0.9
|
||||
spacing: 16
|
||||
Text {
|
||||
text: roomId
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
Button {
|
||||
text: "Edit Profile"
|
||||
onClicked: {
|
||||
globalPopup.source = "EditProfile.qml";
|
||||
globalPopup.open();
|
||||
}
|
||||
}
|
||||
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();
|
||||
}
|
||||
}
|
||||
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 {
|
||||
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 {
|
||||
id: lobby_dialog
|
||||
z: 1000
|
||||
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();
|
||||
ColumnLayout {
|
||||
Button {
|
||||
text: "Edit Profile"
|
||||
onClicked: {
|
||||
globalPopup.source = "EditProfile.qml";
|
||||
globalPopup.open();
|
||||
}
|
||||
}
|
||||
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()
|
||||
{
|
||||
item.x = Math.round((root.width - item.width) / 2);
|
||||
item.y = Math.round(root.height * 0.67 - item.height / 2);
|
||||
}
|
||||
Loader {
|
||||
id: lobby_dialog
|
||||
z: 1000
|
||||
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: {
|
||||
toast.show("Welcome to FreeKill lobby!");
|
||||
function moveToCenter()
|
||||
{
|
||||
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) {
|
||||
mainWindow.busy = false;
|
||||
Self.avatar = jsonData;
|
||||
toast.show("Update avatar done.");
|
||||
mainWindow.busy = false;
|
||||
Self.avatar = jsonData;
|
||||
toast.show("Update avatar done.");
|
||||
}
|
||||
|
||||
callbacks["UpdatePassword"] = function(jsonData) {
|
||||
mainWindow.busy = false;
|
||||
if (jsonData === "1")
|
||||
toast.show("Update password done.");
|
||||
else
|
||||
toast.show("Old password wrong!", 5000);
|
||||
mainWindow.busy = false;
|
||||
if (jsonData === "1")
|
||||
toast.show("Update password done.");
|
||||
else
|
||||
toast.show("Old password wrong!", 5000);
|
||||
}
|
||||
|
|
|
@ -1,69 +1,69 @@
|
|||
import QtQuick 2.15
|
||||
|
||||
Item {
|
||||
property bool enabled: true
|
||||
property alias text: title.text
|
||||
property alias textColor: title.color
|
||||
property alias textFont: title.font
|
||||
property alias backgroundColor: bg.color
|
||||
property alias border: bg.border
|
||||
property alias iconSource: icon.source
|
||||
property int padding: 5
|
||||
property bool enabled: true
|
||||
property alias text: title.text
|
||||
property alias textColor: title.color
|
||||
property alias textFont: title.font
|
||||
property alias backgroundColor: bg.color
|
||||
property alias border: bg.border
|
||||
property alias iconSource: icon.source
|
||||
property int padding: 5
|
||||
|
||||
signal clicked
|
||||
signal clicked
|
||||
|
||||
id: button
|
||||
width: icon.width + title.implicitWidth + padding * 2
|
||||
height: Math.max(icon.height, title.implicitHeight) + padding * 2
|
||||
id: button
|
||||
width: icon.width + title.implicitWidth + padding * 2
|
||||
height: Math.max(icon.height, title.implicitHeight) + padding * 2
|
||||
|
||||
Rectangle {
|
||||
id: bg
|
||||
anchors.fill: parent
|
||||
color: "black"
|
||||
border.width: 2
|
||||
border.color: "white"
|
||||
opacity: 0.8
|
||||
Rectangle {
|
||||
id: bg
|
||||
anchors.fill: parent
|
||||
color: "black"
|
||||
border.width: 2
|
||||
border.color: "white"
|
||||
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: [
|
||||
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
|
||||
}
|
||||
|
||||
Text {
|
||||
id: title
|
||||
font.pixelSize: 18
|
||||
// font.family: "WenQuanYi Micro Hei"
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
text: ""
|
||||
color: "white"
|
||||
}
|
||||
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
|
||||
|
||||
Item {
|
||||
id: roomScene
|
||||
id: roomScene
|
||||
|
||||
property int playerNum: 0
|
||||
property var dashboardModel
|
||||
property int playerNum: 0
|
||||
property var dashboardModel
|
||||
|
||||
property bool isOwner: false
|
||||
property bool isStarted: false
|
||||
property bool isOwner: false
|
||||
property bool isStarted: false
|
||||
|
||||
property alias popupBox: popupBox
|
||||
property alias promptText: prompt.text
|
||||
property alias popupBox: popupBox
|
||||
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 {
|
||||
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],
|
||||
}])}}
|
||||
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 {
|
||||
text: "quit"
|
||||
anchors.top: parent.top
|
||||
anchors.right: parent.right
|
||||
onClicked: {
|
||||
ClientInstance.clearPlayers();
|
||||
ClientInstance.notifyServer("QuitRoom", "[]");
|
||||
}
|
||||
id: endPhaseButton
|
||||
text: "End"
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.bottomMargin: 40
|
||||
anchors.right: parent.right
|
||||
anchors.rightMargin: 30
|
||||
visible: false;
|
||||
onClicked: Logic.doCancelButton();
|
||||
}
|
||||
Button {
|
||||
text: "add robot"
|
||||
visible: dashboardModel.isOwner && !isStarted
|
||||
anchors.centerIn: parent
|
||||
onClicked: {
|
||||
ClientInstance.notifyServer("AddRobot", "[]");
|
||||
}
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
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;
|
||||
function moveToCenter()
|
||||
{
|
||||
item.x = Math.round((roomArea.width - item.width) / 2);
|
||||
item.y = Math.round(roomArea.height * 0.67 - item.height / 2);
|
||||
}
|
||||
}
|
||||
|
||||
if (popupBox.item != null) {
|
||||
popupBox.item.finished();
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
Component.onCompleted: {
|
||||
toast.show("Sucesessfully entered room.");
|
||||
|
||||
Transition {
|
||||
from: "*"; to: "playing"
|
||||
ScriptAction {
|
||||
script: {
|
||||
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
|
||||
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
|
||||
}
|
||||
|
||||
Item {
|
||||
id: roomArea
|
||||
width: roomScene.width
|
||||
height: roomScene.height - dashboard.height
|
||||
playerNum = config.roomCapacity;
|
||||
|
||||
Repeater {
|
||||
id: photos
|
||||
model: photoModel
|
||||
Photo {
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
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
|
||||
});
|
||||
}
|
||||
|
||||
Dashboard {
|
||||
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();
|
||||
}
|
||||
Logic.arrangePhotos();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -3,75 +3,75 @@ import QtQuick 2.15
|
|||
// CardArea stores CardItem.
|
||||
|
||||
Item {
|
||||
property var cards: []
|
||||
property int length: 0
|
||||
property var cards: []
|
||||
property int length: 0
|
||||
|
||||
id: root
|
||||
id: root
|
||||
|
||||
function add(inputs)
|
||||
{
|
||||
if (inputs instanceof Array) {
|
||||
cards.push(...inputs);
|
||||
} else {
|
||||
cards.push(inputs);
|
||||
function add(inputs)
|
||||
{
|
||||
if (inputs instanceof Array) {
|
||||
cards.push(...inputs);
|
||||
} else {
|
||||
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)
|
||||
{
|
||||
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;
|
||||
return result;
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -13,242 +13,242 @@ import "../skin-bank.js" as SkinBank
|
|||
*/
|
||||
|
||||
Item {
|
||||
id: root
|
||||
width: 93
|
||||
height: 130
|
||||
id: root
|
||||
width: 93
|
||||
height: 130
|
||||
|
||||
// properties for the view
|
||||
property string suit: "club"
|
||||
property int number: 7
|
||||
property string name: "slash"
|
||||
property string subtype: ""
|
||||
property string color: "" // only use when suit is empty
|
||||
property string footnote: "" // footnote, e.g. "A use card to B"
|
||||
property bool footnoteVisible: true
|
||||
property bool known: true // if false it only show a card back
|
||||
property bool enabled: true // if false the card will be grey
|
||||
property alias card: cardItem
|
||||
property alias glow: glowItem
|
||||
// properties for the view
|
||||
property string suit: "club"
|
||||
property int number: 7
|
||||
property string name: "slash"
|
||||
property string subtype: ""
|
||||
property string color: "" // only use when suit is empty
|
||||
property string footnote: "" // footnote, e.g. "A use card to B"
|
||||
property bool footnoteVisible: true
|
||||
property bool known: true // if false it only show a card back
|
||||
property bool enabled: true // if false the card will be grey
|
||||
property alias card: cardItem
|
||||
property alias glow: glowItem
|
||||
|
||||
function getColor() {
|
||||
if (suit != "")
|
||||
return (suit == "heart" || suit == "diamond") ? "red" : "black";
|
||||
else return color;
|
||||
function getColor() {
|
||||
if (suit != "")
|
||||
return (suit == "heart" || suit == "diamond") ? "red" : "black";
|
||||
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
|
||||
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
|
||||
onEntered: {
|
||||
parent.entered();
|
||||
if (draggable) {
|
||||
glow.visible = true;
|
||||
root.z++;
|
||||
}
|
||||
}
|
||||
|
||||
Image {
|
||||
id: cardItem
|
||||
source: known ? (name != "" ? SkinBank.CARD_DIR + name : "")
|
||||
: (SkinBank.CARD_DIR + "card-back")
|
||||
anchors.fill: parent
|
||||
fillMode: Image.PreserveAspectCrop
|
||||
onExited: {
|
||||
parent.exited();
|
||||
if (draggable) {
|
||||
glow.visible = false;
|
||||
root.z--;
|
||||
}
|
||||
}
|
||||
|
||||
Image {
|
||||
id: suitItem
|
||||
visible: known
|
||||
source: suit != "" ? SkinBank.CARD_SUIT_DIR + suit : ""
|
||||
x: 3
|
||||
y: 19
|
||||
width: 21
|
||||
height: 17
|
||||
onClicked: {
|
||||
selected = selectable ? !selected : false;
|
||||
parent.clicked();
|
||||
}
|
||||
}
|
||||
|
||||
ParallelAnimation {
|
||||
id: goBackAnimation
|
||||
|
||||
PropertyAnimation {
|
||||
target: root
|
||||
property: "x"
|
||||
to: origX
|
||||
easing.type: Easing.OutQuad
|
||||
duration: goBackDuration
|
||||
}
|
||||
|
||||
Image {
|
||||
id: numberItem
|
||||
visible: known
|
||||
source: (suit != "" && number > 0) ? SkinBank.CARD_DIR
|
||||
+ "number/" + root.getColor() + "/" + number : ""
|
||||
x: 0
|
||||
y: 0
|
||||
width: 27
|
||||
height: 28
|
||||
PropertyAnimation {
|
||||
target: root
|
||||
property: "y"
|
||||
to: origY
|
||||
easing.type: Easing.OutQuad
|
||||
duration: goBackDuration
|
||||
}
|
||||
|
||||
Image {
|
||||
id: colorItem
|
||||
visible: known && suit == ""
|
||||
source: (visible && color != "") ? SkinBank.CARD_SUIT_DIR + "/" + color : ""
|
||||
x: 1
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
onStopped: {
|
||||
if (!moveAborted)
|
||||
root.moveFinished();
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
visible: !root.selectable
|
||||
anchors.fill: parent
|
||||
color: Qt.rgba(0, 0, 0, 0.5)
|
||||
opacity: 0.7
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
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();
|
||||
});
|
||||
}
|
||||
function destroyOnStop()
|
||||
{
|
||||
root.moveFinished.connect(function(){
|
||||
root.destroy();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,33 +2,33 @@ import QtQuick 2.15
|
|||
import ".."
|
||||
|
||||
GraphicsBox {
|
||||
property var options: []
|
||||
property string skill_name: ""
|
||||
property int result
|
||||
property var options: []
|
||||
property string skill_name: ""
|
||||
property int result
|
||||
|
||||
id: root
|
||||
title.text: skill_name + ": Please choose"
|
||||
width: Math.max(140, body.width + 20)
|
||||
height: body.height + title.height + 20
|
||||
id: root
|
||||
title.text: skill_name + ": Please choose"
|
||||
width: Math.max(140, body.width + 20)
|
||||
height: body.height + title.height + 20
|
||||
|
||||
Column {
|
||||
id: body
|
||||
x: 10
|
||||
y: title.height + 5
|
||||
spacing: 10
|
||||
Column {
|
||||
id: body
|
||||
x: 10
|
||||
y: title.height + 5
|
||||
spacing: 10
|
||||
|
||||
Repeater {
|
||||
model: options
|
||||
Repeater {
|
||||
model: options
|
||||
|
||||
MetroButton {
|
||||
text: modelData
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
MetroButton {
|
||||
text: modelData
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
|
||||
onClicked: {
|
||||
result = index;
|
||||
root.close();
|
||||
}
|
||||
}
|
||||
onClicked: {
|
||||
result = index;
|
||||
root.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,175 +3,175 @@ import ".."
|
|||
import "../skin-bank.js" as SkinBank
|
||||
|
||||
GraphicsBox {
|
||||
property alias generalList: generalList
|
||||
// property var generalList: []
|
||||
property int choiceNum: 1
|
||||
property var choices: []
|
||||
property var selectedItem: []
|
||||
property bool loaded: false
|
||||
property alias generalList: generalList
|
||||
// property var generalList: []
|
||||
property int choiceNum: 1
|
||||
property var choices: []
|
||||
property var selectedItem: []
|
||||
property bool loaded: false
|
||||
|
||||
ListModel {
|
||||
id: generalList
|
||||
ListModel {
|
||||
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
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
}
|
||||
Item {
|
||||
id: splitLine
|
||||
width: parent.width - 80
|
||||
height: 6
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
clip: true
|
||||
}
|
||||
|
||||
Repeater {
|
||||
id: generalCardList
|
||||
model: generalList
|
||||
Item {
|
||||
width: parent.width
|
||||
height: 165
|
||||
|
||||
GeneralCardItem {
|
||||
name: model.name
|
||||
selectable: true
|
||||
draggable: true
|
||||
Row {
|
||||
id: resultArea
|
||||
anchors.centerIn: parent
|
||||
spacing: 10
|
||||
|
||||
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.push(this);
|
||||
updatePosition();
|
||||
}
|
||||
Repeater {
|
||||
id: resultList
|
||||
model: choiceNum
|
||||
|
||||
onReleased: {
|
||||
if (!isClicked)
|
||||
arrangeCards();
|
||||
}
|
||||
Rectangle {
|
||||
color: "#1D1E19"
|
||||
radius: 3
|
||||
width: 93
|
||||
height: 130
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function arrangeCards()
|
||||
{
|
||||
let item, i;
|
||||
Item {
|
||||
id: buttonArea
|
||||
width: parent.width
|
||||
height: 40
|
||||
|
||||
selectedItem = [];
|
||||
for (i = 0; i < generalList.count; i++) {
|
||||
item = generalCardList.itemAt(i);
|
||||
if (item.y > splitLine.y)
|
||||
selectedItem.push(item);
|
||||
MetroButton {
|
||||
id: fightButton
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
anchors.bottom: parent.bottom
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
selectedItem.sort((a, b) => a.x - b.x);
|
||||
|
||||
if (selectedItem.length > choiceNum)
|
||||
selectedItem.splice(choiceNum, selectedItem.length - choiceNum);
|
||||
|
||||
if (toSelect && selectedItem.length < choiceNum)
|
||||
selectedItem.push(this);
|
||||
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()
|
||||
{
|
||||
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);
|
||||
}
|
||||
}
|
||||
selectedItem.sort((a, b) => a.x - b.x);
|
||||
|
||||
fightButton.enabled = (choices.length == choiceNum);
|
||||
if (selectedItem.length > choiceNum)
|
||||
selectedItem.splice(choiceNum, selectedItem.length - choiceNum);
|
||||
|
||||
for (i = 0; i < generalCardList.count; i++) {
|
||||
item = generalCardList.itemAt(i);
|
||||
if (selectedItem.indexOf(item) != -1)
|
||||
continue;
|
||||
updatePosition();
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
function updatePosition()
|
||||
{
|
||||
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);
|
||||
|
||||
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
|
||||
|
||||
RowLayout {
|
||||
id: root
|
||||
id: root
|
||||
|
||||
property alias self: selfPhoto
|
||||
property alias handcardArea: handcardAreaItem
|
||||
property alias equipArea: selfPhoto.equipArea
|
||||
property alias delayedTrickArea: selfPhoto.delayedTrickArea
|
||||
property alias specialArea: selfPhoto.specialArea
|
||||
property alias self: selfPhoto
|
||||
property alias handcardArea: handcardAreaItem
|
||||
property alias equipArea: selfPhoto.equipArea
|
||||
property alias delayedTrickArea: selfPhoto.delayedTrickArea
|
||||
property alias specialArea: selfPhoto.specialArea
|
||||
|
||||
Item {
|
||||
width: 40
|
||||
property bool selected: selfPhoto.selected
|
||||
|
||||
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 {
|
||||
id: handcardAreaItem
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredHeight: 130
|
||||
Layout.alignment: Qt.AlignVCenter
|
||||
}
|
||||
|
||||
Photo {
|
||||
id: selfPhoto
|
||||
handcards: handcardAreaItem.length
|
||||
}
|
||||
|
||||
Item { width: 5 }
|
||||
pendings = [];
|
||||
handcardAreaItem.adjustCards();
|
||||
cardSelected(-1);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -13,12 +13,12 @@ import "../skin-bank.js" as SkinBank
|
|||
*/
|
||||
|
||||
CardItem {
|
||||
property string kingdom: "qun"
|
||||
name: "caocao"
|
||||
// description: Sanguosha.getGeneralDescription(name)
|
||||
suit: ""
|
||||
number: 0
|
||||
footnote: ""
|
||||
card.source: SkinBank.GENERAL_DIR + name
|
||||
glow.color: "white" //Engine.kingdomColor[kingdom]
|
||||
property string kingdom: "qun"
|
||||
name: "caocao"
|
||||
// description: Sanguosha.getGeneralDescription(name)
|
||||
suit: ""
|
||||
number: 0
|
||||
footnote: ""
|
||||
card.source: SkinBank.GENERAL_DIR + name
|
||||
glow.color: "white" //Engine.kingdomColor[kingdom]
|
||||
}
|
||||
|
|
|
@ -2,29 +2,29 @@ import QtQuick 2.15
|
|||
import QtGraphicalEffects 1.0
|
||||
|
||||
Item {
|
||||
property alias text: textItem.text
|
||||
property alias color: textItem.color
|
||||
property alias font: textItem.font
|
||||
property alias fontSizeMode: textItem.fontSizeMode
|
||||
property alias horizontalAlignment: textItem.horizontalAlignment
|
||||
property alias verticalAlignment: textItem.verticalAlignment
|
||||
property alias style: textItem.style
|
||||
property alias styleColor: textItem.styleColor
|
||||
property alias wrapMode: textItem.wrapMode
|
||||
property alias lineHeight: textItem.lineHeight
|
||||
property alias glow: glowItem
|
||||
property alias text: textItem.text
|
||||
property alias color: textItem.color
|
||||
property alias font: textItem.font
|
||||
property alias fontSizeMode: textItem.fontSizeMode
|
||||
property alias horizontalAlignment: textItem.horizontalAlignment
|
||||
property alias verticalAlignment: textItem.verticalAlignment
|
||||
property alias style: textItem.style
|
||||
property alias styleColor: textItem.styleColor
|
||||
property alias wrapMode: textItem.wrapMode
|
||||
property alias lineHeight: textItem.lineHeight
|
||||
property alias glow: glowItem
|
||||
|
||||
width: textItem.implicitWidth
|
||||
height: textItem.implicitHeight
|
||||
width: textItem.implicitWidth
|
||||
height: textItem.implicitHeight
|
||||
|
||||
Text {
|
||||
id: textItem
|
||||
anchors.fill: parent
|
||||
}
|
||||
Text {
|
||||
id: textItem
|
||||
anchors.fill: parent
|
||||
}
|
||||
|
||||
Glow {
|
||||
id: glowItem
|
||||
source: textItem
|
||||
anchors.fill: textItem
|
||||
}
|
||||
Glow {
|
||||
id: glowItem
|
||||
source: textItem
|
||||
anchors.fill: textItem
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,52 +2,52 @@ import QtQuick 2.15
|
|||
import QtGraphicalEffects 1.0
|
||||
|
||||
Item {
|
||||
property alias title: titleItem
|
||||
signal accepted() //Read result
|
||||
signal finished() //Close the box
|
||||
property alias title: titleItem
|
||||
signal accepted() //Read result
|
||||
signal finished() //Close the box
|
||||
|
||||
id: root
|
||||
id: root
|
||||
|
||||
Rectangle {
|
||||
id: background
|
||||
anchors.fill: parent
|
||||
color: "#B0000000"
|
||||
radius: 5
|
||||
border.color: "#A6967A"
|
||||
border.width: 1
|
||||
}
|
||||
Rectangle {
|
||||
id: background
|
||||
anchors.fill: parent
|
||||
color: "#B0000000"
|
||||
radius: 5
|
||||
border.color: "#A6967A"
|
||||
border.width: 1
|
||||
}
|
||||
|
||||
DropShadow {
|
||||
source: background
|
||||
anchors.fill: background
|
||||
color: "#B0000000"
|
||||
radius: 5
|
||||
samples: 12
|
||||
spread: 0.2
|
||||
horizontalOffset: 5
|
||||
verticalOffset: 4
|
||||
transparentBorder: true
|
||||
}
|
||||
DropShadow {
|
||||
source: background
|
||||
anchors.fill: background
|
||||
color: "#B0000000"
|
||||
radius: 5
|
||||
samples: 12
|
||||
spread: 0.2
|
||||
horizontalOffset: 5
|
||||
verticalOffset: 4
|
||||
transparentBorder: true
|
||||
}
|
||||
|
||||
Text {
|
||||
id: titleItem
|
||||
color: "#E4D5A0"
|
||||
font.pixelSize: 18
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
anchors.top: parent.top
|
||||
anchors.topMargin: 4
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
}
|
||||
Text {
|
||||
id: titleItem
|
||||
color: "#E4D5A0"
|
||||
font.pixelSize: 18
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
anchors.top: parent.top
|
||||
anchors.topMargin: 4
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
drag.target: parent
|
||||
drag.axis: Drag.XAndYAxis
|
||||
}
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
drag.target: parent
|
||||
drag.axis: Drag.XAndYAxis
|
||||
}
|
||||
|
||||
function close()
|
||||
{
|
||||
accepted();
|
||||
finished();
|
||||
}
|
||||
function close()
|
||||
{
|
||||
accepted();
|
||||
finished();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,130 +2,130 @@ import QtQuick 2.15
|
|||
import "../../util.js" as Utility
|
||||
|
||||
Item {
|
||||
property alias cards: cardArea.cards
|
||||
property alias length: cardArea.length
|
||||
property var selectedCards: []
|
||||
property alias cards: cardArea.cards
|
||||
property alias length: cardArea.length
|
||||
property var selectedCards: []
|
||||
|
||||
signal cardSelected(int cardId, bool selected)
|
||||
signal cardSelected(int cardId, bool selected)
|
||||
|
||||
id: area
|
||||
id: area
|
||||
|
||||
CardArea {
|
||||
anchors.fill: parent
|
||||
id: cardArea
|
||||
onLengthChanged: area.updateCardPosition(true);
|
||||
CardArea {
|
||||
anchors.fill: parent
|
||||
id: cardArea
|
||||
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)
|
||||
{
|
||||
cardArea.add(inputs);
|
||||
if (inputs instanceof Array) {
|
||||
for (let i = 0; i < inputs.length; i++)
|
||||
filterInputCard(inputs[i]);
|
||||
} else {
|
||||
filterInputCard(inputs);
|
||||
}
|
||||
if (animated) {
|
||||
for (i = 0; i < cards.length; i++)
|
||||
cards[i].goBack(true)
|
||||
}
|
||||
}
|
||||
|
||||
function filterInputCard(card)
|
||||
{
|
||||
card.autoBack = true;
|
||||
card.draggable = true;
|
||||
card.selectable = false;
|
||||
card.clicked.connect(adjustCards);
|
||||
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 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 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 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 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;
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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);
|
||||
if (card === undefined) {
|
||||
selectedCards = [];
|
||||
} else {
|
||||
selectedCards = [card];
|
||||
}
|
||||
updateCardPosition(true);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,101 +1,101 @@
|
|||
import QtQuick 2.15
|
||||
|
||||
Item {
|
||||
property point start: Qt.point(0, 0)
|
||||
property var end: []
|
||||
property alias running: pointToAnimation.running
|
||||
property color color: "#96943D"
|
||||
property real ratio: 0
|
||||
property int lineWidth: 6
|
||||
property point start: Qt.point(0, 0)
|
||||
property var end: []
|
||||
property alias running: pointToAnimation.running
|
||||
property color color: "#96943D"
|
||||
property real ratio: 0
|
||||
property int lineWidth: 6
|
||||
|
||||
signal finished()
|
||||
signal finished()
|
||||
|
||||
id: root
|
||||
anchors.fill: parent
|
||||
id: root
|
||||
anchors.fill: parent
|
||||
|
||||
Repeater {
|
||||
model: end
|
||||
Repeater {
|
||||
model: end
|
||||
|
||||
Rectangle {
|
||||
width: 6
|
||||
height: Math.sqrt(Math.pow(modelData.x - start.x, 2) + Math.pow(modelData.y - start.y, 2)) * ratio
|
||||
x: start.x
|
||||
y: start.y
|
||||
antialiasing: true
|
||||
Rectangle {
|
||||
width: 6
|
||||
height: Math.sqrt(Math.pow(modelData.x - start.x, 2) + Math.pow(modelData.y - start.y, 2)) * ratio
|
||||
x: start.x
|
||||
y: start.y
|
||||
antialiasing: true
|
||||
|
||||
gradient: Gradient {
|
||||
GradientStop {
|
||||
position: 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
gradient: Gradient {
|
||||
GradientStop {
|
||||
position: 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SequentialAnimation {
|
||||
id: pointToAnimation
|
||||
|
||||
PropertyAnimation {
|
||||
target: root
|
||||
property: "ratio"
|
||||
to: 1
|
||||
easing.type: Easing.OutCubic
|
||||
duration: 200
|
||||
}
|
||||
|
||||
SequentialAnimation {
|
||||
id: pointToAnimation
|
||||
|
||||
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();
|
||||
}
|
||||
PauseAnimation {
|
||||
duration: 200
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
Item {
|
||||
property var cards: []
|
||||
property int length: 0
|
||||
property var pendingInput: []
|
||||
property bool checkExisting: false
|
||||
property var cards: []
|
||||
property int length: 0
|
||||
property var pendingInput: []
|
||||
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 (inputs instanceof Array) {
|
||||
for (let i = 0; i < inputs.length; i++) {
|
||||
card = inputs[i];
|
||||
pendingInput.push(card);
|
||||
cards.push(card.toData());
|
||||
}
|
||||
if (cards[i].cid === cid)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
if (checkExisting)
|
||||
length = cards.length;
|
||||
else
|
||||
length += inputs.length;
|
||||
} else {
|
||||
pendingInput.push(inputs);
|
||||
cards.push(inputs.toData());
|
||||
function remove(outputs)
|
||||
{
|
||||
let component = Qt.createComponent("CardItem.qml");
|
||||
if (component.status !== Component.Ready)
|
||||
return [];
|
||||
|
||||
if (checkExisting)
|
||||
length = cards.length;
|
||||
else
|
||||
length++;
|
||||
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.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)
|
||||
{
|
||||
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 = [];
|
||||
}
|
||||
pendingInput = [];
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,309 +5,377 @@ import "PhotoElement"
|
|||
import "../skin-bank.js" as SkinBank
|
||||
|
||||
Item {
|
||||
id: root
|
||||
width: 175
|
||||
height: 233
|
||||
scale: 0.8
|
||||
property string general: ""
|
||||
property string screenName: ""
|
||||
property string role: "unknown"
|
||||
property string kingdom: "qun"
|
||||
property string netstate: "online"
|
||||
property alias handcards: handcardAreaItem.length
|
||||
property int maxHp: 0
|
||||
property int hp: 0
|
||||
property int seatNumber: 1
|
||||
property bool isDead: false
|
||||
property bool dying: false
|
||||
property bool faceup: true
|
||||
property bool chained: false
|
||||
property bool drank: false
|
||||
property bool isOwner: false
|
||||
property string status: "normal"
|
||||
id: root
|
||||
width: 175
|
||||
height: 233
|
||||
scale: 0.8
|
||||
property int playerid
|
||||
property string general: ""
|
||||
property string screenName: ""
|
||||
property string role: "unknown"
|
||||
property string kingdom: "qun"
|
||||
property string netstate: "online"
|
||||
property alias handcards: handcardAreaItem.length
|
||||
property int maxHp: 0
|
||||
property int hp: 0
|
||||
property int seatNumber: 1
|
||||
property bool isDead: false
|
||||
property bool dying: false
|
||||
property bool faceup: true
|
||||
property bool chained: false
|
||||
property bool drank: false
|
||||
property bool isOwner: false
|
||||
property string status: "normal"
|
||||
|
||||
property alias handcardArea: handcardAreaItem
|
||||
property alias equipArea: equipAreaItem
|
||||
property alias delayedTrickArea: delayedTrickAreaItem
|
||||
property alias specialArea: handcardAreaItem
|
||||
property alias handcardArea: handcardAreaItem
|
||||
property alias equipArea: equipAreaItem
|
||||
property alias delayedTrickArea: delayedTrickAreaItem
|
||||
property alias specialArea: handcardAreaItem
|
||||
|
||||
property alias progressBar: progressBar
|
||||
property alias progressTip: progressTip.text
|
||||
property alias progressBar: progressBar
|
||||
property alias progressTip: progressTip.text
|
||||
|
||||
property bool selectable: false
|
||||
property bool selected: false
|
||||
property bool selectable: false
|
||||
property bool selected: false
|
||||
|
||||
Behavior on x {
|
||||
NumberAnimation { duration: 600; easing.type: Easing.InOutQuad }
|
||||
Behavior on x {
|
||||
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 {
|
||||
NumberAnimation { duration: 600; easing.type: Easing.InOutQuad }
|
||||
}
|
||||
PixmapAnimation {
|
||||
id: animPlaying
|
||||
source: "playing"
|
||||
anchors.centerIn: parent
|
||||
loop: true
|
||||
scale: 1.1
|
||||
visible: root.state === "playing"
|
||||
}
|
||||
|
||||
PixmapAnimation {
|
||||
id: animFrame
|
||||
source: "selected"
|
||||
anchors.centerIn: parent
|
||||
loop: true
|
||||
scale: 1.1
|
||||
}
|
||||
PixmapAnimation {
|
||||
id: animSelected
|
||||
source: "selected"
|
||||
anchors.centerIn: parent
|
||||
loop: true
|
||||
scale: 1.1
|
||||
visible: root.state === "candidate" && selected
|
||||
}
|
||||
|
||||
Image {
|
||||
id: back
|
||||
source: SkinBank.PHOTO_BACK_DIR + root.kingdom
|
||||
}
|
||||
Image {
|
||||
id: back
|
||||
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 {
|
||||
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: ""
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
HpBar {
|
||||
id: hp
|
||||
x: 8
|
||||
value: root.hp
|
||||
maxValue: root.maxHp
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.bottomMargin: 36
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
onClicked: {
|
||||
if (parent.state != "candidate" || !parent.selectable)
|
||||
return;
|
||||
parent.selected = !parent.selected;
|
||||
}
|
||||
}
|
||||
|
||||
Image {
|
||||
id: generalImage
|
||||
width: 138
|
||||
height: 222
|
||||
smooth: true
|
||||
visible: false
|
||||
fillMode: Image.PreserveAspectCrop
|
||||
source: (general != "") ? SkinBank.GENERAL_DIR + general : ""
|
||||
RoleComboBox {
|
||||
id: role
|
||||
value: root.role
|
||||
anchors.top: parent.top
|
||||
anchors.topMargin: -4
|
||||
anchors.right: parent.right
|
||||
anchors.rightMargin: -4
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
id: photoMask
|
||||
x: 31
|
||||
y: 5
|
||||
width: 138
|
||||
height: 222
|
||||
radius: 8
|
||||
visible: false
|
||||
PropertyAnimation {
|
||||
target: root
|
||||
property: "x"
|
||||
to: root.x
|
||||
easing.type: Easing.OutQuad
|
||||
duration: 100
|
||||
}
|
||||
}
|
||||
|
||||
function tremble() {
|
||||
trembleAnimation.start()
|
||||
}
|
||||
|
||||
OpacityMask {
|
||||
anchors.fill: photoMask
|
||||
source: generalImage
|
||||
maskSource: photoMask
|
||||
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 = "";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Colorize {
|
||||
anchors.fill: photoMask
|
||||
source: generalImage
|
||||
saturation: 0
|
||||
visible: root.isDead
|
||||
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: ""
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
PixmapAnimation {
|
||||
id: animSelectable
|
||||
source: "selectable"
|
||||
anchors.centerIn: parent
|
||||
loop: true
|
||||
visible: root.state === "candidate" && selectable
|
||||
}
|
||||
|
||||
Image {
|
||||
visible: equipAreaItem.length > 0
|
||||
source: SkinBank.PHOTO_DIR + "equipbg"
|
||||
x: 31
|
||||
y: 121
|
||||
}
|
||||
InvisibleCardArea {
|
||||
id: handcardAreaItem
|
||||
anchors.centerIn: parent
|
||||
}
|
||||
|
||||
Image {
|
||||
source: root.status != "normal" ? SkinBank.STATUS_DIR + root.status : ""
|
||||
x: -6
|
||||
}
|
||||
DelayedTrickArea {
|
||||
id: delayedTrickAreaItem
|
||||
rows: 1
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.bottomMargin: 8
|
||||
}
|
||||
|
||||
Image {
|
||||
id: turnedOver
|
||||
visible: !root.faceup
|
||||
source: SkinBank.PHOTO_DIR + "faceturned"
|
||||
x: 29; y: 5
|
||||
}
|
||||
InvisibleCardArea {
|
||||
id: defaultArea
|
||||
anchors.centerIn: parent
|
||||
}
|
||||
|
||||
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: 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;
|
||||
}
|
||||
onGeneralChanged: {
|
||||
if (!roomScene.isStarted) return;
|
||||
generalName.text = Backend.translate(general);
|
||||
let data = JSON.parse(Backend.callLuaFunction("GetGeneralData", [general]));
|
||||
kingdom = data.kingdom;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,62 +4,62 @@ import ".."
|
|||
import "../../skin-bank.js" as SkinBank
|
||||
|
||||
Item {
|
||||
property alias rows: grid.rows
|
||||
property alias columns: grid.columns
|
||||
property alias rows: grid.rows
|
||||
property alias columns: grid.columns
|
||||
|
||||
InvisibleCardArea {
|
||||
id: area
|
||||
checkExisting: true
|
||||
InvisibleCardArea {
|
||||
id: area
|
||||
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 {
|
||||
id: cards
|
||||
function add(inputs)
|
||||
{
|
||||
area.add(inputs);
|
||||
if (inputs instanceof Array) {
|
||||
cards.append(...inputs);
|
||||
} else {
|
||||
cards.append(inputs);
|
||||
}
|
||||
}
|
||||
|
||||
Grid {
|
||||
id: grid
|
||||
anchors.fill: parent
|
||||
rows: 100
|
||||
columns: 100
|
||||
|
||||
Repeater {
|
||||
model: cards
|
||||
|
||||
Image {
|
||||
source: SkinBank.DELAYED_TRICK_DIR + name
|
||||
}
|
||||
function remove(outputs)
|
||||
{
|
||||
let result = area.remove(outputs);
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function add(inputs)
|
||||
{
|
||||
area.add(inputs);
|
||||
if (inputs instanceof Array) {
|
||||
cards.append(...inputs);
|
||||
} else {
|
||||
cards.append(inputs);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
function remove(outputs)
|
||||
{
|
||||
let result = area.remove(outputs);
|
||||
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);
|
||||
}
|
||||
function updateCardPosition(animated)
|
||||
{
|
||||
area.updateCardPosition(animated);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,110 +11,110 @@ import "../../skin-bank.js" as SkinBank
|
|||
*/
|
||||
|
||||
Column {
|
||||
height: 88
|
||||
width: 138
|
||||
property int itemHeight: Math.floor(height / 4)
|
||||
property var items: [treasureItem, weaponItem, armorItem, defensiveHorseItem, offensiveHorseItem]
|
||||
property var subtypes: ["treasure", "weapon", "armor", "defensive_horse", "offensive_horse"]
|
||||
property int length: area.length
|
||||
height: 88
|
||||
width: 138
|
||||
property int itemHeight: Math.floor(height / 4)
|
||||
property var items: [treasureItem, weaponItem, armorItem, defensiveHorseItem, offensiveHorseItem]
|
||||
property var subtypes: ["treasure", "weapon", "armor", "defensive_horse", "offensive_horse"]
|
||||
property int length: area.length
|
||||
|
||||
InvisibleCardArea {
|
||||
id: area
|
||||
checkExisting: true
|
||||
}
|
||||
InvisibleCardArea {
|
||||
id: area
|
||||
checkExisting: true
|
||||
}
|
||||
|
||||
EquipItem {
|
||||
id: treasureItem
|
||||
EquipItem {
|
||||
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
|
||||
height: itemHeight
|
||||
icon: "horse"
|
||||
opacity: 0
|
||||
}
|
||||
}
|
||||
|
||||
EquipItem {
|
||||
id: weaponItem
|
||||
Item {
|
||||
width: Math.floor(parent.width / 2)
|
||||
height: itemHeight
|
||||
|
||||
EquipItem {
|
||||
id: offensiveHorseItem
|
||||
width: parent.width
|
||||
height: itemHeight
|
||||
icon: "horse"
|
||||
opacity: 0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
EquipItem {
|
||||
id: armorItem
|
||||
width: parent.width
|
||||
height: itemHeight
|
||||
opacity: 0
|
||||
function add(inputs)
|
||||
{
|
||||
area.add(inputs);
|
||||
|
||||
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 {
|
||||
width: parent.width
|
||||
height: itemHeight
|
||||
|
||||
Item {
|
||||
width: Math.ceil(parent.width / 2)
|
||||
height: itemHeight
|
||||
|
||||
EquipItem {
|
||||
id: defensiveHorseItem
|
||||
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 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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function add(inputs)
|
||||
{
|
||||
area.add(inputs);
|
||||
return result;
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
function updateCardPosition(animated)
|
||||
{
|
||||
area.updateCardPosition(animated);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -4,139 +4,139 @@ import "../../../util.js" as Utility
|
|||
import "../../skin-bank.js" as SkinBank
|
||||
|
||||
Item {
|
||||
property int cid: 0
|
||||
property string name: ""
|
||||
property string suit: ""
|
||||
property int number: 0
|
||||
property int cid: 0
|
||||
property string name: ""
|
||||
property string suit: ""
|
||||
property int number: 0
|
||||
|
||||
property string icon: ""
|
||||
property alias text: textItem.text
|
||||
property string icon: ""
|
||||
property alias text: textItem.text
|
||||
|
||||
id: root
|
||||
id: root
|
||||
|
||||
Image {
|
||||
id: iconItem
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
x: 3
|
||||
Image {
|
||||
id: iconItem
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
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 {
|
||||
id: suitItem
|
||||
anchors.right: parent.right
|
||||
source: suit ? SkinBank.CARD_SUIT_DIR + suit : ""
|
||||
width: implicitWidth / implicitHeight * height
|
||||
height: 16
|
||||
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
|
||||
}
|
||||
|
||||
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
|
||||
NumberAnimation {
|
||||
target: root
|
||||
property: "opacity"
|
||||
duration: 200
|
||||
easing.type: Easing.InOutQuad
|
||||
from: 1
|
||||
to: 0
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
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 = Backend.translate(name);
|
||||
icon = name;
|
||||
}
|
||||
}
|
||||
|
||||
ParallelAnimation {
|
||||
id: showAnime
|
||||
function show()
|
||||
{
|
||||
showAnime.start();
|
||||
}
|
||||
|
||||
NumberAnimation {
|
||||
target: root
|
||||
property: "x"
|
||||
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();
|
||||
}
|
||||
function hide()
|
||||
{
|
||||
hideAnime.start();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,71 +2,71 @@ import QtQuick 2.15
|
|||
import ".."
|
||||
|
||||
Column {
|
||||
id: root
|
||||
property int maxValue: 4
|
||||
property int value: 4
|
||||
property var colors: ["#F4180E", "#F4180E", "#E3B006", "#25EC27"]
|
||||
id: root
|
||||
property int maxValue: 4
|
||||
property int value: 4
|
||||
property var colors: ["#F4180E", "#F4180E", "#E3B006", "#25EC27"]
|
||||
|
||||
Repeater {
|
||||
id: repeater
|
||||
model: maxValue <= 4 ? maxValue : 0
|
||||
Magatama {
|
||||
state: (maxValue - 1 - index) >= value ? 0 : (value >= 3 || value >= maxValue ? 3 : (value <= 0 ? 0 : value))
|
||||
}
|
||||
Repeater {
|
||||
id: repeater
|
||||
model: maxValue <= 4 ? maxValue : 0
|
||||
Magatama {
|
||||
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 {
|
||||
visible: maxValue > 4
|
||||
spacing: -4
|
||||
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
|
||||
|
||||
Magatama {
|
||||
state: (value >= 3 || value >= maxValue) ? 3 : (value <= 0 ? 0 : value)
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -2,57 +2,57 @@ import QtQuick 2.15
|
|||
import "../../skin-bank.js" as SkinBank
|
||||
|
||||
Image {
|
||||
source: SkinBank.MAGATAMA_DIR + "0"
|
||||
state: "3"
|
||||
source: SkinBank.MAGATAMA_DIR + "0"
|
||||
state: "3"
|
||||
|
||||
states: [
|
||||
State {
|
||||
name: "3"
|
||||
PropertyChanges {
|
||||
target: main
|
||||
source: SkinBank.MAGATAMA_DIR + "3"
|
||||
opacity: 1
|
||||
scale: 1
|
||||
}
|
||||
},
|
||||
State {
|
||||
name: "2"
|
||||
PropertyChanges {
|
||||
target: main
|
||||
source: SkinBank.MAGATAMA_DIR + "2"
|
||||
opacity: 1
|
||||
scale: 1
|
||||
}
|
||||
},
|
||||
State {
|
||||
name: "1"
|
||||
PropertyChanges {
|
||||
target: main
|
||||
source: SkinBank.MAGATAMA_DIR + "1"
|
||||
opacity: 1
|
||||
scale: 1
|
||||
}
|
||||
},
|
||||
State {
|
||||
name: "0"
|
||||
PropertyChanges {
|
||||
target: main
|
||||
source: SkinBank.MAGATAMA_DIR + "0"
|
||||
opacity: 0
|
||||
scale: 4
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
transitions: Transition {
|
||||
PropertyAnimation {
|
||||
properties: "opacity,scale"
|
||||
}
|
||||
states: [
|
||||
State {
|
||||
name: "3"
|
||||
PropertyChanges {
|
||||
target: main
|
||||
source: SkinBank.MAGATAMA_DIR + "3"
|
||||
opacity: 1
|
||||
scale: 1
|
||||
}
|
||||
},
|
||||
State {
|
||||
name: "2"
|
||||
PropertyChanges {
|
||||
target: main
|
||||
source: SkinBank.MAGATAMA_DIR + "2"
|
||||
opacity: 1
|
||||
scale: 1
|
||||
}
|
||||
},
|
||||
State {
|
||||
name: "1"
|
||||
PropertyChanges {
|
||||
target: main
|
||||
source: SkinBank.MAGATAMA_DIR + "1"
|
||||
opacity: 1
|
||||
scale: 1
|
||||
}
|
||||
},
|
||||
State {
|
||||
name: "0"
|
||||
PropertyChanges {
|
||||
target: main
|
||||
source: SkinBank.MAGATAMA_DIR + "0"
|
||||
opacity: 0
|
||||
scale: 4
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
Image {
|
||||
id: main
|
||||
anchors.centerIn: parent
|
||||
transitions: Transition {
|
||||
PropertyAnimation {
|
||||
properties: "opacity,scale"
|
||||
}
|
||||
}
|
||||
|
||||
Image {
|
||||
id: main
|
||||
anchors.centerIn: parent
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -3,45 +3,45 @@ import QtQuick 2.15
|
|||
import "../../skin-bank.js" as SkinBank
|
||||
|
||||
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 var options: ["unknown", "loyalist", "rebel", "renegade"]
|
||||
|
||||
id: root
|
||||
source: visible ? SkinBank.ROLE_DIR + value : ""
|
||||
visible: value != "hidden"
|
||||
id: assumptionBox
|
||||
source: SkinBank.ROLE_DIR + value
|
||||
visible: root.value == "unknown"
|
||||
|
||||
Image {
|
||||
property string value: "unknown"
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
onClicked: optionPopupBox.visible = true;
|
||||
}
|
||||
}
|
||||
|
||||
id: assumptionBox
|
||||
source: SkinBank.ROLE_DIR + value
|
||||
visible: root.value == "unknown"
|
||||
Column {
|
||||
id: optionPopupBox
|
||||
visible: false
|
||||
spacing: 2
|
||||
|
||||
Repeater {
|
||||
model: options
|
||||
|
||||
Image {
|
||||
source: SkinBank.ROLE_DIR + modelData
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
onClicked: optionPopupBox.visible = true;
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
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
|
||||
|
||||
Item {
|
||||
property string source: ""
|
||||
property int currentFrame: 0
|
||||
property alias interval: timer.interval
|
||||
property int loadedFrameCount: 0
|
||||
property bool autoStart: false
|
||||
property bool loop: false
|
||||
property string source: ""
|
||||
property int currentFrame: 0
|
||||
property alias interval: timer.interval
|
||||
property int loadedFrameCount: 0
|
||||
property bool autoStart: false
|
||||
property bool loop: false
|
||||
|
||||
signal loaded()
|
||||
signal started()
|
||||
signal finished()
|
||||
signal loaded()
|
||||
signal started()
|
||||
signal finished()
|
||||
|
||||
id: root
|
||||
width: childrenRect.width
|
||||
height: childrenRect.height
|
||||
id: root
|
||||
width: childrenRect.width
|
||||
height: childrenRect.height
|
||||
|
||||
FolderListModel {
|
||||
id: fileModel
|
||||
folder: SkinBank.PIXANIM_DIR + source
|
||||
nameFilters: ["*.png"]
|
||||
showDirs: false
|
||||
}
|
||||
FolderListModel {
|
||||
id: fileModel
|
||||
folder: SkinBank.PIXANIM_DIR + source
|
||||
nameFilters: ["*.png"]
|
||||
showDirs: false
|
||||
}
|
||||
|
||||
Repeater {
|
||||
id: frames
|
||||
model: fileModel
|
||||
Repeater {
|
||||
id: frames
|
||||
model: fileModel
|
||||
|
||||
Image {
|
||||
source: SkinBank.PIXANIM_DIR + root.source + "/" + index
|
||||
visible: false
|
||||
onStatusChanged: {
|
||||
if (status == Image.Ready) {
|
||||
loadedFrameCount++;
|
||||
if (loadedFrameCount == fileModel.count)
|
||||
root.loaded();
|
||||
}
|
||||
}
|
||||
Image {
|
||||
source: SkinBank.PIXANIM_DIR + root.source + "/" + index
|
||||
visible: false
|
||||
onStatusChanged: {
|
||||
if (status == Image.Ready) {
|
||||
loadedFrameCount++;
|
||||
if (loadedFrameCount == fileModel.count)
|
||||
root.loaded();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
onLoaded: {
|
||||
if (autoStart)
|
||||
timer.start();
|
||||
}
|
||||
onLoaded: {
|
||||
if (autoStart)
|
||||
timer.start();
|
||||
}
|
||||
|
||||
Timer {
|
||||
id: timer
|
||||
interval: 50
|
||||
repeat: true
|
||||
onTriggered: {
|
||||
if (currentFrame >= fileModel.count) {
|
||||
frames.itemAt(fileModel.count - 1).visible = false;
|
||||
if (loop) {
|
||||
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();
|
||||
Timer {
|
||||
id: timer
|
||||
interval: 50
|
||||
repeat: true
|
||||
onTriggered: {
|
||||
if (currentFrame >= fileModel.count) {
|
||||
frames.itemAt(fileModel.count - 1).visible = false;
|
||||
if (loop) {
|
||||
currentFrame = 0;
|
||||
} else {
|
||||
root.loaded.connect(function(){
|
||||
timer.start();
|
||||
});
|
||||
timer.stop();
|
||||
root.finished();
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function stop()
|
||||
{
|
||||
timer.stop();
|
||||
if (currentFrame > 0)
|
||||
frames.itemAt(currentFrame - 1).visible = false;
|
||||
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
|
||||
|
||||
Flickable {
|
||||
id: root
|
||||
id: root
|
||||
}
|
||||
|
|
|
@ -1,135 +1,135 @@
|
|||
import QtQuick 2.15
|
||||
|
||||
Item {
|
||||
property var discardedCards: []
|
||||
property alias cards: area.cards
|
||||
property bool toVanish: false
|
||||
property var discardedCards: []
|
||||
property alias cards: area.cards
|
||||
property bool toVanish: false
|
||||
|
||||
id: root
|
||||
id: root
|
||||
|
||||
CardArea {
|
||||
id: area
|
||||
}
|
||||
CardArea {
|
||||
id: area
|
||||
}
|
||||
|
||||
InvisibleCardArea {
|
||||
id: invisibleArea
|
||||
}
|
||||
InvisibleCardArea {
|
||||
id: invisibleArea
|
||||
}
|
||||
|
||||
Timer {
|
||||
id: vanishTimer
|
||||
interval: 1500
|
||||
repeat: true
|
||||
running: true
|
||||
triggeredOnStart: true
|
||||
onTriggered: {
|
||||
let i, card;
|
||||
if (toVanish) {
|
||||
for (i = 0; i < discardedCards.length; i++) {
|
||||
card = discardedCards[i];
|
||||
card.origOpacity = 0;
|
||||
card.goBack(true);
|
||||
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
|
||||
}
|
||||
Timer {
|
||||
id: vanishTimer
|
||||
interval: 1500
|
||||
repeat: true
|
||||
running: true
|
||||
triggeredOnStart: true
|
||||
onTriggered: {
|
||||
let i, card;
|
||||
if (toVanish) {
|
||||
for (i = 0; i < discardedCards.length; i++) {
|
||||
card = discardedCards[i];
|
||||
card.origOpacity = 0;
|
||||
card.goBack(true);
|
||||
card.destroyOnStop()
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
cards.splice(0, discardedCards.length);
|
||||
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 (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;
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,377 +1,465 @@
|
|||
var Card = {
|
||||
Unknown : 0,
|
||||
PlayerHand : 1,
|
||||
PlayerEquip : 2,
|
||||
PlayerJudge : 3,
|
||||
PlayerSpecial : 4,
|
||||
Processing : 5,
|
||||
DrawPile : 6,
|
||||
DiscardPile : 7,
|
||||
Void : 8
|
||||
Unknown : 0,
|
||||
PlayerHand : 1,
|
||||
PlayerEquip : 2,
|
||||
PlayerJudge : 3,
|
||||
PlayerSpecial : 4,
|
||||
Processing : 5,
|
||||
DrawPile : 6,
|
||||
DiscardPile : 7,
|
||||
Void : 8
|
||||
}
|
||||
|
||||
function arrangePhotos() {
|
||||
/* Layout of photos:
|
||||
* +---------------+
|
||||
* | 6 5 4 3 2 |
|
||||
* | 7 1 |
|
||||
* | dashboard |
|
||||
* +---------------+
|
||||
*/
|
||||
/* Layout of photos:
|
||||
* +---------------+
|
||||
* | 6 5 4 3 2 |
|
||||
* | 7 1 |
|
||||
* | dashboard |
|
||||
* +---------------+
|
||||
*/
|
||||
|
||||
const photoWidth = 175;
|
||||
const roomAreaPadding = 10;
|
||||
let verticalPadding = Math.max(10, roomArea.width * 0.01);
|
||||
let horizontalSpacing = Math.max(30, roomArea.height * 0.1);
|
||||
let verticalSpacing = (roomArea.width - photoWidth * 7 - verticalPadding * 2) / 6;
|
||||
const photoWidth = 175;
|
||||
const roomAreaPadding = 10;
|
||||
let verticalPadding = Math.max(10, roomArea.width * 0.01);
|
||||
let horizontalSpacing = Math.max(30, roomArea.height * 0.1);
|
||||
let verticalSpacing = (roomArea.width - photoWidth * 7 - verticalPadding * 2) / 6;
|
||||
|
||||
// Position 1-7
|
||||
const regions = [
|
||||
{ x: verticalPadding + (photoWidth + verticalSpacing) * 6, y: roomAreaPadding + horizontalSpacing * 2 },
|
||||
{ x: verticalPadding + (photoWidth + verticalSpacing) * 5, y: roomAreaPadding + horizontalSpacing },
|
||||
{ x: verticalPadding + (photoWidth + verticalSpacing) * 4, y: roomAreaPadding },
|
||||
{ x: verticalPadding + (photoWidth + verticalSpacing) * 3, y: roomAreaPadding },
|
||||
{ x: verticalPadding + (photoWidth + verticalSpacing) * 2, y: roomAreaPadding },
|
||||
{ x: verticalPadding + photoWidth + verticalSpacing, y: roomAreaPadding + horizontalSpacing },
|
||||
{ x: verticalPadding, y: roomAreaPadding + horizontalSpacing * 2 },
|
||||
];
|
||||
// Position 1-7
|
||||
const regions = [
|
||||
{ x: verticalPadding + (photoWidth + verticalSpacing) * 6, y: roomAreaPadding + horizontalSpacing * 2 },
|
||||
{ x: verticalPadding + (photoWidth + verticalSpacing) * 5, y: roomAreaPadding + horizontalSpacing },
|
||||
{ x: verticalPadding + (photoWidth + verticalSpacing) * 4, y: roomAreaPadding },
|
||||
{ x: verticalPadding + (photoWidth + verticalSpacing) * 3, y: roomAreaPadding },
|
||||
{ x: verticalPadding + (photoWidth + verticalSpacing) * 2, y: roomAreaPadding },
|
||||
{ x: verticalPadding + photoWidth + verticalSpacing, y: roomAreaPadding + horizontalSpacing },
|
||||
{ x: verticalPadding, y: roomAreaPadding + horizontalSpacing * 2 },
|
||||
];
|
||||
|
||||
const regularSeatIndex = [
|
||||
[4],
|
||||
[3, 5],
|
||||
[1, 4, 7],
|
||||
[1, 3, 5, 7],
|
||||
[1, 3, 4, 5, 7],
|
||||
[1, 2, 3, 5, 6, 7],
|
||||
[1, 2, 3, 4, 5, 6, 7],
|
||||
];
|
||||
let seatIndex = regularSeatIndex[playerNum - 2];
|
||||
const regularSeatIndex = [
|
||||
[4],
|
||||
[3, 5],
|
||||
[1, 4, 7],
|
||||
[1, 3, 5, 7],
|
||||
[1, 3, 4, 5, 7],
|
||||
[1, 2, 3, 5, 6, 7],
|
||||
[1, 2, 3, 4, 5, 6, 7],
|
||||
];
|
||||
let seatIndex = regularSeatIndex[playerNum - 2];
|
||||
|
||||
let item, region, i;
|
||||
let item, region, i;
|
||||
|
||||
for (i = 0; i < playerNum - 1; i++) {
|
||||
item = photos.itemAt(i);
|
||||
if (!item)
|
||||
continue;
|
||||
for (i = 0; i < playerNum - 1; i++) {
|
||||
item = photos.itemAt(i);
|
||||
if (!item)
|
||||
continue;
|
||||
|
||||
region = regions[seatIndex[photoModel.get(i).index] - 1];
|
||||
item.x = region.x;
|
||||
item.y = region.y;
|
||||
}
|
||||
region = regions[seatIndex[photoModel.get(i).index] - 1];
|
||||
item.x = region.x;
|
||||
item.y = region.y;
|
||||
}
|
||||
}
|
||||
|
||||
function doOkButton() {
|
||||
replyToServer("1");
|
||||
if (roomScene.state == "playing") {
|
||||
replyToServer(JSON.stringify(
|
||||
{
|
||||
card: dashboard.getSelectedCard(),
|
||||
targets: selected_targets
|
||||
}
|
||||
));
|
||||
return;
|
||||
}
|
||||
replyToServer("1");
|
||||
}
|
||||
|
||||
function doCancelButton() {
|
||||
replyToServer("");
|
||||
replyToServer("");
|
||||
}
|
||||
|
||||
function replyToServer(jsonData) {
|
||||
roomScene.state = "notactive";
|
||||
ClientInstance.replyToServer("", jsonData);
|
||||
roomScene.state = "notactive";
|
||||
ClientInstance.replyToServer("", jsonData);
|
||||
}
|
||||
|
||||
function getPhotoModel(id) {
|
||||
for (let i = 0; i < photoModel.count; i++) {
|
||||
let item = photoModel.get(i);
|
||||
if (item.id === id) {
|
||||
return item;
|
||||
}
|
||||
for (let i = 0; i < photoModel.count; i++) {
|
||||
let item = photoModel.get(i);
|
||||
if (item.id === id) {
|
||||
return item;
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
function getPhoto(id) {
|
||||
for (let i = 0; i < photoModel.count; i++) {
|
||||
let item = photoModel.get(i);
|
||||
if (item.id === id) {
|
||||
return photos.itemAt(i);
|
||||
}
|
||||
for (let i = 0; i < photoModel.count; i++) {
|
||||
let item = photoModel.get(i);
|
||||
if (item.id === id) {
|
||||
return photos.itemAt(i);
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
function getPhotoOrDashboard(id) {
|
||||
let photo = getPhoto(id);
|
||||
if (!photo) {
|
||||
if (id === Self.id)
|
||||
return dashboard;
|
||||
}
|
||||
return photo;
|
||||
let photo = getPhoto(id);
|
||||
if (!photo) {
|
||||
if (id === Self.id)
|
||||
return dashboard;
|
||||
}
|
||||
return photo;
|
||||
}
|
||||
|
||||
function getAreaItem(area, id) {
|
||||
if (area === Card.DrawPile) {
|
||||
return drawPile;
|
||||
} else if (area === Card.DiscardPile || area === Card.Processing) {
|
||||
return tablePile;
|
||||
} else if (area === Card.AG) {
|
||||
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;
|
||||
if (area === Card.DrawPile) {
|
||||
return drawPile;
|
||||
} else if (area === Card.DiscardPile || area === Card.Processing) {
|
||||
return tablePile;
|
||||
} else if (area === Card.AG) {
|
||||
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;
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
function moveCards(moves) {
|
||||
for (let i = 0; i < moves.length; i++) {
|
||||
let move = moves[i];
|
||||
let from = getAreaItem(move.fromArea, move.from);
|
||||
let to = getAreaItem(move.toArea, move.to);
|
||||
if (!from || !to || from === to)
|
||||
continue;
|
||||
let items = from.remove(move.ids);
|
||||
if (items.length > 0)
|
||||
to.add(items);
|
||||
to.updateCardPosition(true);
|
||||
}
|
||||
for (let i = 0; i < moves.length; i++) {
|
||||
let move = moves[i];
|
||||
let from = getAreaItem(move.fromArea, move.from);
|
||||
let to = getAreaItem(move.toArea, move.to);
|
||||
if (!from || !to || from === to)
|
||||
continue;
|
||||
let items = from.remove(move.ids);
|
||||
if (items.length > 0)
|
||||
to.add(items);
|
||||
to.updateCardPosition(true);
|
||||
}
|
||||
}
|
||||
|
||||
function setEmotion(id, emotion) {
|
||||
let component = Qt.createComponent("RoomElement/PixmapAnimation.qml");
|
||||
if (component.status !== Component.Ready)
|
||||
return;
|
||||
let component = Qt.createComponent("RoomElement/PixmapAnimation.qml");
|
||||
if (component.status !== Component.Ready)
|
||||
return;
|
||||
|
||||
let photo = getPhoto(id);
|
||||
if (!photo) {
|
||||
if (id === dashboardModel.id) {
|
||||
photo = dashboard.self;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
let photo = getPhoto(id);
|
||||
if (!photo) {
|
||||
if (id === dashboardModel.id) {
|
||||
photo = dashboard.self;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
let animation = component.createObject(photo, {source: emotion, anchors: {centerIn: photo}});
|
||||
animation.finished.connect(() => animation.destroy());
|
||||
animation.start();
|
||||
let animation = component.createObject(photo, {source: emotion, anchors: {centerIn: photo}});
|
||||
animation.finished.connect(() => animation.destroy());
|
||||
animation.start();
|
||||
}
|
||||
|
||||
function changeHp(id, delta, losthp) {
|
||||
let photo = getPhoto(id);
|
||||
if (!photo) {
|
||||
if (id === dashboardModel.id) {
|
||||
photo = dashboard.self;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
let photo = getPhoto(id);
|
||||
if (!photo) {
|
||||
if (id === dashboardModel.id) {
|
||||
photo = dashboard.self;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
if (delta < 0) {
|
||||
if (!losthp) {
|
||||
setEmotion(id, "damage")
|
||||
photo.tremble()
|
||||
}
|
||||
}
|
||||
if (delta < 0) {
|
||||
if (!losthp) {
|
||||
setEmotion(id, "damage")
|
||||
photo.tremble()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function doIndicate(from, tos) {
|
||||
let component = Qt.createComponent("RoomElement/IndicatorLine.qml");
|
||||
if (component.status !== Component.Ready)
|
||||
return;
|
||||
let component = Qt.createComponent("RoomElement/IndicatorLine.qml");
|
||||
if (component.status !== Component.Ready)
|
||||
return;
|
||||
|
||||
let fromItem = getPhotoOrDashboard(from);
|
||||
let fromPos = mapFromItem(fromItem, fromItem.width / 2, fromItem.height / 2);
|
||||
let fromItem = getPhotoOrDashboard(from);
|
||||
let fromPos = mapFromItem(fromItem, fromItem.width / 2, fromItem.height / 2);
|
||||
|
||||
let end = [];
|
||||
for (let i = 0; i < tos.length; i++) {
|
||||
if (from === tos[i])
|
||||
continue;
|
||||
let toItem = getPhotoOrDashboard(tos[i]);
|
||||
let toPos = mapFromItem(toItem, toItem.width / 2, toItem.height / 2);
|
||||
end.push(toPos);
|
||||
}
|
||||
let end = [];
|
||||
for (let i = 0; i < tos.length; i++) {
|
||||
if (from === tos[i])
|
||||
continue;
|
||||
let toItem = getPhotoOrDashboard(tos[i]);
|
||||
let toPos = mapFromItem(toItem, toItem.width / 2, toItem.height / 2);
|
||||
end.push(toPos);
|
||||
}
|
||||
|
||||
let color = "#96943D";
|
||||
let line = component.createObject(roomScene, {start: fromPos, end: end, color: color});
|
||||
line.finished.connect(() => line.destroy());
|
||||
line.running = true;
|
||||
let color = "#96943D";
|
||||
let line = component.createObject(roomScene, {start: fromPos, end: end, color: color});
|
||||
line.finished.connect(() => line.destroy());
|
||||
line.running = true;
|
||||
}
|
||||
|
||||
callbacks["AddPlayer"] = function(jsonData) {
|
||||
// jsonData: int id, string screenName, string avatar
|
||||
for (let i = 0; i < photoModel.count; i++) {
|
||||
let item = photoModel.get(i);
|
||||
if (item.id === -1) {
|
||||
let data = JSON.parse(jsonData);
|
||||
let uid = data[0];
|
||||
let name = data[1];
|
||||
let avatar = data[2];
|
||||
item.id = uid;
|
||||
item.screenName = name;
|
||||
item.general = avatar;
|
||||
return;
|
||||
}
|
||||
// jsonData: int id, string screenName, string avatar
|
||||
for (let i = 0; i < photoModel.count; i++) {
|
||||
let item = photoModel.get(i);
|
||||
if (item.id === -1) {
|
||||
let data = JSON.parse(jsonData);
|
||||
let uid = data[0];
|
||||
let name = data[1];
|
||||
let avatar = data[2];
|
||||
item.id = uid;
|
||||
item.screenName = name;
|
||||
item.general = avatar;
|
||||
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) {
|
||||
// jsonData: int uid
|
||||
let uid = JSON.parse(jsonData)[0];
|
||||
let model = getPhotoModel(uid);
|
||||
if (typeof(model) !== "undefined") {
|
||||
model.id = -1;
|
||||
model.screenName = "";
|
||||
model.general = "";
|
||||
}
|
||||
// jsonData: int uid
|
||||
let uid = JSON.parse(jsonData)[0];
|
||||
let model = getPhotoModel(uid);
|
||||
if (typeof(model) !== "undefined") {
|
||||
model.id = -1;
|
||||
model.screenName = "";
|
||||
model.general = "";
|
||||
}
|
||||
}
|
||||
|
||||
callbacks["RoomOwner"] = function(jsonData) {
|
||||
// jsonData: int uid of the owner
|
||||
let uid = JSON.parse(jsonData)[0];
|
||||
// jsonData: int uid of the owner
|
||||
let uid = JSON.parse(jsonData)[0];
|
||||
|
||||
if (dashboardModel.id === uid) {
|
||||
dashboardModel.isOwner = true;
|
||||
roomScene.dashboardModelChanged();
|
||||
return;
|
||||
}
|
||||
if (dashboardModel.id === uid) {
|
||||
dashboardModel.isOwner = true;
|
||||
roomScene.dashboardModelChanged();
|
||||
return;
|
||||
}
|
||||
|
||||
let model = getPhotoModel(uid);
|
||||
if (typeof(model) !== "undefined") {
|
||||
model.isOwner = true;
|
||||
}
|
||||
let model = getPhotoModel(uid);
|
||||
if (typeof(model) !== "undefined") {
|
||||
model.isOwner = true;
|
||||
}
|
||||
}
|
||||
|
||||
callbacks["PropertyUpdate"] = function(jsonData) {
|
||||
// jsonData: int id, string property_name, value
|
||||
let data = JSON.parse(jsonData);
|
||||
let uid = data[0];
|
||||
let property_name = data[1];
|
||||
let value = data[2];
|
||||
// jsonData: int id, string property_name, value
|
||||
let data = JSON.parse(jsonData);
|
||||
let uid = data[0];
|
||||
let property_name = data[1];
|
||||
let value = data[2];
|
||||
|
||||
if (Self.id === uid) {
|
||||
dashboardModel[property_name] = value;
|
||||
roomScene.dashboardModelChanged();
|
||||
return;
|
||||
}
|
||||
if (Self.id === uid) {
|
||||
dashboardModel[property_name] = value;
|
||||
roomScene.dashboardModelChanged();
|
||||
return;
|
||||
}
|
||||
|
||||
let model = getPhotoModel(uid);
|
||||
if (typeof(model) !== "undefined") {
|
||||
model[property_name] = value;
|
||||
}
|
||||
let model = getPhotoModel(uid);
|
||||
if (typeof(model) !== "undefined") {
|
||||
model[property_name] = value;
|
||||
}
|
||||
}
|
||||
|
||||
callbacks["ArrangeSeats"] = function(jsonData) {
|
||||
// jsonData: seat order
|
||||
let order = JSON.parse(jsonData);
|
||||
roomScene.isStarted = true;
|
||||
// jsonData: seat order
|
||||
let order = JSON.parse(jsonData);
|
||||
roomScene.isStarted = true;
|
||||
|
||||
for (let i = 0; i < photoModel.count; i++) {
|
||||
let item = photoModel.get(i);
|
||||
item.seatNumber = order.indexOf(item.id) + 1;
|
||||
}
|
||||
for (let i = 0; i < photoModel.count; i++) {
|
||||
let item = photoModel.get(i);
|
||||
item.seatNumber = order.indexOf(item.id) + 1;
|
||||
}
|
||||
|
||||
dashboardModel.seatNumber = order.indexOf(Self.id) + 1;
|
||||
roomScene.dashboardModelChanged();
|
||||
|
||||
// make Self to the first of list, then reorder photomodel
|
||||
let selfIndex = order.indexOf(Self.id);
|
||||
let after = order.splice(selfIndex);
|
||||
after.push(...order);
|
||||
let photoOrder = after.slice(1);
|
||||
dashboardModel.seatNumber = order.indexOf(Self.id) + 1;
|
||||
roomScene.dashboardModelChanged();
|
||||
|
||||
// make Self to the first of list, then reorder photomodel
|
||||
let selfIndex = order.indexOf(Self.id);
|
||||
let after = order.splice(selfIndex);
|
||||
after.push(...order);
|
||||
let photoOrder = after.slice(1);
|
||||
|
||||
for (let i = 0; i < photoModel.count; i++) {
|
||||
let item = photoModel.get(i);
|
||||
item.index = photoOrder.indexOf(item.id);
|
||||
}
|
||||
|
||||
arrangePhotos();
|
||||
for (let i = 0; i < photoModel.count; i++) {
|
||||
let item = photoModel.get(i);
|
||||
item.index = photoOrder.indexOf(item.id);
|
||||
}
|
||||
|
||||
arrangePhotos();
|
||||
}
|
||||
|
||||
function cancelAllFocus() {
|
||||
let item;
|
||||
for (let i = 0; i < playerNum - 1; i++) {
|
||||
item = photos.itemAt(i);
|
||||
item.progressBar.visible = false;
|
||||
item.progressTip = "";
|
||||
}
|
||||
let item;
|
||||
for (let i = 0; i < playerNum - 1; i++) {
|
||||
item = photos.itemAt(i);
|
||||
item.progressBar.visible = false;
|
||||
item.progressTip = "";
|
||||
}
|
||||
}
|
||||
|
||||
callbacks["MoveFocus"] = function(jsonData) {
|
||||
// jsonData: int[] focuses, string command
|
||||
cancelAllFocus();
|
||||
let data = JSON.parse(jsonData);
|
||||
let focuses = data[0];
|
||||
let command = data[1];
|
||||
|
||||
let item, model;
|
||||
for (let i = 0; i < playerNum - 1; i++) {
|
||||
model = photoModel.get(i);
|
||||
if (focuses.indexOf(model.id) != -1) {
|
||||
item = photos.itemAt(i);
|
||||
item.progressBar.visible = true;
|
||||
item.progressTip = command + " thinking...";
|
||||
}
|
||||
// jsonData: int[] focuses, string command
|
||||
cancelAllFocus();
|
||||
let data = JSON.parse(jsonData);
|
||||
let focuses = data[0];
|
||||
let command = data[1];
|
||||
|
||||
let item, model;
|
||||
for (let i = 0; i < playerNum - 1; i++) {
|
||||
model = photoModel.get(i);
|
||||
if (focuses.indexOf(model.id) != -1) {
|
||||
item = photos.itemAt(i);
|
||||
item.progressBar.visible = true;
|
||||
item.progressTip = command + " thinking...";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
callbacks["PlayerRunned"] = function(jsonData) {
|
||||
// jsonData: int runner, int robot
|
||||
let data = JSON.parse(jsonData);
|
||||
let runner = data[0];
|
||||
let robot = data[1];
|
||||
// jsonData: int runner, int robot
|
||||
let data = JSON.parse(jsonData);
|
||||
let runner = data[0];
|
||||
let robot = data[1];
|
||||
|
||||
let model = getPhotoModel(runner);
|
||||
if (typeof(model) !== "undefined") {
|
||||
model.id = robot;
|
||||
}
|
||||
let model = getPhotoModel(runner);
|
||||
if (typeof(model) !== "undefined") {
|
||||
model.id = robot;
|
||||
}
|
||||
}
|
||||
|
||||
callbacks["AskForGeneral"] = function(jsonData) {
|
||||
// jsonData: string[] Generals
|
||||
// TODO: choose multiple generals
|
||||
let data = JSON.parse(jsonData);
|
||||
roomScene.promptText = "Please choose 1 general";
|
||||
roomScene.state = "replying";
|
||||
roomScene.popupBox.source = "RoomElement/ChooseGeneralBox.qml";
|
||||
let box = roomScene.popupBox.item;
|
||||
box.choiceNum = 1;
|
||||
box.accepted.connect(() => {
|
||||
replyToServer(JSON.stringify([box.choices[0]]));
|
||||
});
|
||||
for (let i = 0; i < data.length; i++)
|
||||
box.generalList.append({ "name": data[i] });
|
||||
box.updatePosition();
|
||||
// jsonData: string[] Generals
|
||||
// TODO: choose multiple generals
|
||||
let data = JSON.parse(jsonData);
|
||||
roomScene.promptText = "Please choose 1 general";
|
||||
roomScene.state = "replying";
|
||||
roomScene.popupBox.source = "RoomElement/ChooseGeneralBox.qml";
|
||||
let box = roomScene.popupBox.item;
|
||||
box.choiceNum = 1;
|
||||
box.accepted.connect(() => {
|
||||
replyToServer(JSON.stringify([box.choices[0]]));
|
||||
});
|
||||
for (let i = 0; i < data.length; i++)
|
||||
box.generalList.append({ "name": data[i] });
|
||||
box.updatePosition();
|
||||
}
|
||||
|
||||
callbacks["AskForSkillInvoke"] = function(jsonData) {
|
||||
// jsonData: string name
|
||||
roomScene.promptText = "Do you want to invoke '" + jsonData + "' ?";
|
||||
roomScene.state = "responding";
|
||||
// jsonData: string name
|
||||
roomScene.promptText = "Do you want to invoke '" + jsonData + "' ?";
|
||||
roomScene.state = "responding";
|
||||
}
|
||||
|
||||
callbacks["AskForChoice"] = function(jsonData) {
|
||||
// jsonData: [ string[] choices, string skill ]
|
||||
// TODO: multiple choices, e.g. benxi_ol
|
||||
let data = JSON.parse(jsonData);
|
||||
let choices = data[0];
|
||||
let skill_name = data[1];
|
||||
roomScene.promptText = skill_name + ": Please make choice";
|
||||
roomScene.state = "replying";
|
||||
roomScene.popupBox.source = "RoomElement/ChoiceBox.qml";
|
||||
let box = roomScene.popupBox.item;
|
||||
box.options = choices;
|
||||
box.skill_name = skill_name;
|
||||
box.accepted.connect(() => {
|
||||
replyToServer(choices[box.result]);
|
||||
});
|
||||
// jsonData: [ string[] choices, string skill ]
|
||||
// TODO: multiple choices, e.g. benxi_ol
|
||||
let data = JSON.parse(jsonData);
|
||||
let choices = data[0];
|
||||
let skill_name = data[1];
|
||||
roomScene.promptText = skill_name + ": Please make choice";
|
||||
roomScene.state = "replying";
|
||||
roomScene.popupBox.source = "RoomElement/ChoiceBox.qml";
|
||||
let box = roomScene.popupBox.item;
|
||||
box.options = choices;
|
||||
box.skill_name = skill_name;
|
||||
box.accepted.connect(() => {
|
||||
replyToServer(choices[box.result]);
|
||||
});
|
||||
}
|
||||
|
||||
callbacks["MoveCards"] = function(jsonData) {
|
||||
// jsonData: merged moves
|
||||
let moves = JSON.parse(jsonData);
|
||||
moveCards(moves);
|
||||
// jsonData: merged moves
|
||||
let moves = JSON.parse(jsonData);
|
||||
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
|
||||
|
||||
Rectangle {
|
||||
function show(text, duration) {
|
||||
message.text = text;
|
||||
time = Math.max(duration, 2 * fadeTime);
|
||||
animation.start();
|
||||
function show(text, duration) {
|
||||
message.text = text;
|
||||
time = Math.max(duration, 2 * fadeTime);
|
||||
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
|
||||
|
||||
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
|
||||
PauseAnimation {
|
||||
duration: time - 2 * fadeTime
|
||||
}
|
||||
|
||||
SequentialAnimation on opacity {
|
||||
id: animation
|
||||
running: false
|
||||
|
||||
|
||||
NumberAnimation {
|
||||
to: .9
|
||||
duration: fadeTime
|
||||
}
|
||||
|
||||
PauseAnimation {
|
||||
duration: time - 2 * fadeTime
|
||||
}
|
||||
|
||||
NumberAnimation {
|
||||
to: 0
|
||||
duration: fadeTime
|
||||
}
|
||||
|
||||
onRunningChanged: {
|
||||
if (!running) {
|
||||
toast.model.remove(index);
|
||||
}
|
||||
}
|
||||
NumberAnimation {
|
||||
to: 0
|
||||
duration: fadeTime
|
||||
}
|
||||
|
||||
onRunningChanged: {
|
||||
if (!running) {
|
||||
toast.model.remove(index);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,35 +3,35 @@ import QtQuick 2.15
|
|||
// copy from https://gist.github.com/jonmcclung/bae669101d17b103e94790341301c129
|
||||
// and modified some code
|
||||
ListView {
|
||||
function show(text, duration) {
|
||||
if (duration === undefined) {
|
||||
duration = 3000;
|
||||
}
|
||||
model.insert(0, {text: text, duration: duration});
|
||||
function show(text, duration) {
|
||||
if (duration === undefined) {
|
||||
duration = 3000;
|
||||
}
|
||||
model.insert(0, {text: text, duration: duration});
|
||||
}
|
||||
|
||||
id: root
|
||||
id: root
|
||||
|
||||
z: Infinity
|
||||
spacing: 5
|
||||
anchors.fill: parent
|
||||
anchors.bottomMargin: 10
|
||||
verticalLayoutDirection: ListView.BottomToTop
|
||||
z: Infinity
|
||||
spacing: 5
|
||||
anchors.fill: parent
|
||||
anchors.bottomMargin: 10
|
||||
verticalLayoutDirection: ListView.BottomToTop
|
||||
|
||||
interactive: false
|
||||
interactive: false
|
||||
|
||||
displaced: Transition {
|
||||
NumberAnimation {
|
||||
properties: "y"
|
||||
easing.type: Easing.InOutQuad
|
||||
}
|
||||
displaced: Transition {
|
||||
NumberAnimation {
|
||||
properties: "y"
|
||||
easing.type: Easing.InOutQuad
|
||||
}
|
||||
|
||||
delegate: Toast {
|
||||
Component.onCompleted: {
|
||||
show(text, duration);
|
||||
}
|
||||
}
|
||||
|
||||
delegate: Toast {
|
||||
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"
|
||||
|
||||
Window {
|
||||
id: mainWindow
|
||||
visible: true
|
||||
width: 720
|
||||
height: 480
|
||||
property var callbacks: Logic.callbacks
|
||||
id: mainWindow
|
||||
visible: true
|
||||
width: 720
|
||||
height: 480
|
||||
property var callbacks: Logic.callbacks
|
||||
|
||||
StackView {
|
||||
id: mainStack
|
||||
visible: !mainWindow.busy
|
||||
initialItem: init
|
||||
anchors.fill: parent
|
||||
StackView {
|
||||
id: mainStack
|
||||
visible: !mainWindow.busy
|
||||
initialItem: init
|
||||
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 {} }
|
||||
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
|
||||
enter: Transition {
|
||||
NumberAnimation { properties: "opacity"; from: 0; to: 1 }
|
||||
NumberAnimation { properties: "scale"; from: 0.4; to: 1 }
|
||||
}
|
||||
|
||||
Config {
|
||||
id: config
|
||||
onAboutToHide: {
|
||||
globalPopupDim.stateVisible = false
|
||||
}
|
||||
|
||||
// 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 }
|
||||
}
|
||||
exit: Transition {
|
||||
NumberAnimation { properties: "opacity"; from: 1; to: 0 }
|
||||
NumberAnimation { properties: "scale"; from: 1; to: 0.4 }
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
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 = "";
|
||||
});
|
||||
}
|
||||
}
|
||||
Loader {
|
||||
visible: !mainWindow.busy
|
||||
source: globalPopup.source === "" ? "" : "GlobalPopups/" + globalPopup.source
|
||||
onSourceChanged: {
|
||||
if (item === null)
|
||||
return;
|
||||
item.finished.connect(() => {
|
||||
globalPopup.close();
|
||||
globalPopup.source = "";
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ToastManager {
|
||||
id: toast
|
||||
}
|
||||
ToastManager {
|
||||
id: toast
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: Backend
|
||||
function onNotifyUI(command, jsonData) {
|
||||
let cb = callbacks[command]
|
||||
if (typeof(cb) === "function") {
|
||||
cb(jsonData);
|
||||
} else {
|
||||
callbacks["ErrorMsg"]("Unknown command " + command + "!");
|
||||
}
|
||||
}
|
||||
Connections {
|
||||
target: Backend
|
||||
function onNotifyUI(command, jsonData) {
|
||||
let cb = callbacks[command]
|
||||
if (typeof(cb) === "function") {
|
||||
cb(jsonData);
|
||||
} else {
|
||||
callbacks["ErrorMsg"]("Unknown command " + command + "!");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
22
qml/util.js
22
qml/util.js
|
@ -1,21 +1,21 @@
|
|||
.pragma library
|
||||
|
||||
function convertNumber(number) {
|
||||
if (number === 1)
|
||||
return "A";
|
||||
if (number >= 2 && number <= 10)
|
||||
return number;
|
||||
if (number >= 11 && number <= 13) {
|
||||
const strs = ["J", "Q", "K"];
|
||||
return strs[number - 11];
|
||||
}
|
||||
return "";
|
||||
if (number === 1)
|
||||
return "A";
|
||||
if (number >= 2 && number <= 10)
|
||||
return number;
|
||||
if (number >= 11 && number <= 13) {
|
||||
const strs = ["J", "Q", "K"];
|
||||
return strs[number - 11];
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
Array.prototype.contains = function(element) {
|
||||
return this.indexOf(element) != -1;
|
||||
return this.indexOf(element) != -1;
|
||||
}
|
||||
|
||||
Array.prototype.prepend = function() {
|
||||
this.splice(0, 0, ...arguments);
|
||||
this.splice(0, 0, ...arguments);
|
||||
}
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
CREATE TABLE userinfo (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
name VARCHAR(255),
|
||||
password CHAR(64),
|
||||
avatar VARCHAR(64),
|
||||
lastLoginIp VARCHAR(64),
|
||||
banned BOOLEAN
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
name VARCHAR(255),
|
||||
password CHAR(64),
|
||||
avatar VARCHAR(64),
|
||||
lastLoginIp VARCHAR(64),
|
||||
banned BOOLEAN
|
||||
);
|
||||
|
||||
CREATE TABLE banip (
|
||||
ip VARCHAR(64)
|
||||
ip VARCHAR(64)
|
||||
);
|
||||
|
|
|
@ -1,39 +1,39 @@
|
|||
set(freekill_SRCS
|
||||
"main.cpp"
|
||||
"core/player.cpp"
|
||||
"core/util.cpp"
|
||||
"network/server_socket.cpp"
|
||||
"network/client_socket.cpp"
|
||||
"network/router.cpp"
|
||||
"server/server.cpp"
|
||||
"server/serverplayer.cpp"
|
||||
"server/room.cpp"
|
||||
"client/client.cpp"
|
||||
"client/clientplayer.cpp"
|
||||
"ui/qmlbackend.cpp"
|
||||
"swig/freekill-wrap.cxx"
|
||||
"main.cpp"
|
||||
"core/player.cpp"
|
||||
"core/util.cpp"
|
||||
"network/server_socket.cpp"
|
||||
"network/client_socket.cpp"
|
||||
"network/router.cpp"
|
||||
"server/server.cpp"
|
||||
"server/serverplayer.cpp"
|
||||
"server/room.cpp"
|
||||
"client/client.cpp"
|
||||
"client/clientplayer.cpp"
|
||||
"ui/qmlbackend.cpp"
|
||||
"swig/freekill-wrap.cxx"
|
||||
)
|
||||
|
||||
set(freekill_HEADERS
|
||||
"core/util.h"
|
||||
"core/player.h"
|
||||
"network/server_socket.h"
|
||||
"network/client_socket.h"
|
||||
"network/router.h"
|
||||
"server/server.h"
|
||||
"server/serverplayer.h"
|
||||
"server/room.h"
|
||||
"client/client.h"
|
||||
"client/clientplayer.h"
|
||||
"ui/qmlbackend.h"
|
||||
"core/util.h"
|
||||
"core/player.h"
|
||||
"network/server_socket.h"
|
||||
"network/client_socket.h"
|
||||
"network/router.h"
|
||||
"server/server.h"
|
||||
"server/serverplayer.h"
|
||||
"server/room.h"
|
||||
"client/client.h"
|
||||
"client/clientplayer.h"
|
||||
"ui/qmlbackend.h"
|
||||
)
|
||||
|
||||
if (WIN32)
|
||||
set(LUA_LIB ${PROJECT_SOURCE_DIR}/lib/win/lua54.dll)
|
||||
set(SQLITE3_LIB ${PROJECT_SOURCE_DIR}/lib/win/sqlite3.dll)
|
||||
set(LUA_LIB ${PROJECT_SOURCE_DIR}/lib/win/lua54.dll)
|
||||
set(SQLITE3_LIB ${PROJECT_SOURCE_DIR}/lib/win/sqlite3.dll)
|
||||
else ()
|
||||
set(LUA_LIB lua5.4)
|
||||
set(SQLITE3_LIB sqlite3)
|
||||
set(LUA_LIB lua5.4)
|
||||
set(SQLITE3_LIB sqlite3)
|
||||
endif ()
|
||||
|
||||
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)
|
||||
file(GLOB SWIG_FILES "${PROJECT_SOURCE_DIR}/src/swig/*.i")
|
||||
add_custom_command(
|
||||
OUTPUT ${PROJECT_SOURCE_DIR}/src/swig/freekill-wrap.cxx
|
||||
DEPENDS ${SWIG_FILES}
|
||||
COMMENT "Generating freekill-wrap.cxx"
|
||||
COMMAND swig -c++ -lua -Wall -o
|
||||
${PROJECT_SOURCE_DIR}/src/swig/freekill-wrap.cxx
|
||||
${PROJECT_SOURCE_DIR}/src/swig/freekill.i
|
||||
OUTPUT ${PROJECT_SOURCE_DIR}/src/swig/freekill-wrap.cxx
|
||||
DEPENDS ${SWIG_FILES}
|
||||
COMMENT "Generating freekill-wrap.cxx"
|
||||
COMMAND swig -c++ -lua -Wall -o
|
||||
${PROJECT_SOURCE_DIR}/src/swig/freekill-wrap.cxx
|
||||
${PROJECT_SOURCE_DIR}/src/swig/freekill.i
|
||||
)
|
||||
|
|
|
@ -7,67 +7,67 @@ Client *ClientInstance;
|
|||
ClientPlayer *Self;
|
||||
|
||||
Client::Client(QObject* parent)
|
||||
: QObject(parent), callback(0)
|
||||
: QObject(parent), callback(0)
|
||||
{
|
||||
ClientInstance = this;
|
||||
Self = new ClientPlayer(0, this);
|
||||
QQmlApplicationEngine *engine = Backend->getEngine();
|
||||
engine->rootContext()->setContextProperty("ClientInstance", ClientInstance);
|
||||
engine->rootContext()->setContextProperty("Self", Self);
|
||||
ClientInstance = this;
|
||||
Self = new ClientPlayer(0, this);
|
||||
QQmlApplicationEngine *engine = Backend->getEngine();
|
||||
engine->rootContext()->setContextProperty("ClientInstance", ClientInstance);
|
||||
engine->rootContext()->setContextProperty("Self", Self);
|
||||
|
||||
ClientSocket *socket = new ClientSocket;
|
||||
connect(socket, &ClientSocket::error_message, this, &Client::error_message);
|
||||
router = new Router(this, socket, Router::TYPE_CLIENT);
|
||||
ClientSocket *socket = new ClientSocket;
|
||||
connect(socket, &ClientSocket::error_message, this, &Client::error_message);
|
||||
router = new Router(this, socket, Router::TYPE_CLIENT);
|
||||
|
||||
L = CreateLuaState();
|
||||
DoLuaScript(L, "lua/freekill.lua");
|
||||
DoLuaScript(L, "lua/client/client.lua");
|
||||
L = CreateLuaState();
|
||||
DoLuaScript(L, "lua/freekill.lua");
|
||||
DoLuaScript(L, "lua/client/client.lua");
|
||||
}
|
||||
|
||||
Client::~Client()
|
||||
{
|
||||
ClientInstance = nullptr;
|
||||
lua_close(L);
|
||||
router->getSocket()->disconnectFromHost();
|
||||
router->getSocket()->deleteLater();
|
||||
ClientInstance = nullptr;
|
||||
lua_close(L);
|
||||
router->getSocket()->disconnectFromHost();
|
||||
router->getSocket()->deleteLater();
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
int type = Router::TYPE_REPLY | Router::SRC_CLIENT | Router::DEST_SERVER;
|
||||
router->reply(type, command, jsonData);
|
||||
int type = Router::TYPE_REPLY | Router::SRC_CLIENT | Router::DEST_SERVER;
|
||||
router->reply(type, command, jsonData);
|
||||
}
|
||||
|
||||
void Client::notifyServer(const QString& command, const QString& jsonData)
|
||||
{
|
||||
int type = Router::TYPE_NOTIFICATION | Router::SRC_CLIENT | Router::DEST_SERVER;
|
||||
router->notify(type, command, jsonData);
|
||||
int type = Router::TYPE_NOTIFICATION | Router::SRC_CLIENT | Router::DEST_SERVER;
|
||||
router->notify(type, command, jsonData);
|
||||
}
|
||||
|
||||
ClientPlayer *Client::addPlayer(int id, const QString &name, const QString &avatar) {
|
||||
ClientPlayer *player = new ClientPlayer(id);
|
||||
player->setScreenName(name);
|
||||
player->setAvatar(avatar);
|
||||
ClientPlayer *player = new ClientPlayer(id);
|
||||
player->setScreenName(name);
|
||||
player->setAvatar(avatar);
|
||||
|
||||
players[id] = player;
|
||||
return player;
|
||||
players[id] = player;
|
||||
return player;
|
||||
}
|
||||
|
||||
void Client::removePlayer(int id) {
|
||||
ClientPlayer *p = players[id];
|
||||
p->deleteLater();
|
||||
players[id] = nullptr;
|
||||
ClientPlayer *p = players[id];
|
||||
p->deleteLater();
|
||||
players[id] = nullptr;
|
||||
}
|
||||
|
||||
void Client::clearPlayers() {
|
||||
players.clear();
|
||||
players.clear();
|
||||
}
|
||||
|
||||
lua_State *Client::getLuaState() {
|
||||
return L;
|
||||
return L;
|
||||
}
|
||||
|
|
|
@ -6,33 +6,33 @@
|
|||
#include "qmlbackend.h"
|
||||
|
||||
class Client : public QObject {
|
||||
Q_OBJECT
|
||||
Q_OBJECT
|
||||
public:
|
||||
Client(QObject *parent = nullptr);
|
||||
~Client();
|
||||
Client(QObject *parent = nullptr);
|
||||
~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 notifyServer(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 callLua(const QString &command, const QString &jsonData);
|
||||
LuaFunction callback;
|
||||
Q_INVOKABLE void callLua(const QString &command, const QString &jsonData);
|
||||
LuaFunction callback;
|
||||
|
||||
ClientPlayer *addPlayer(int id, const QString &name, const QString &avatar);
|
||||
void removePlayer(int id);
|
||||
Q_INVOKABLE void clearPlayers();
|
||||
ClientPlayer *addPlayer(int id, const QString &name, const QString &avatar);
|
||||
void removePlayer(int id);
|
||||
Q_INVOKABLE void clearPlayers();
|
||||
|
||||
lua_State *getLuaState();
|
||||
lua_State *getLuaState();
|
||||
|
||||
signals:
|
||||
void error_message(const QString &msg);
|
||||
void error_message(const QString &msg);
|
||||
|
||||
private:
|
||||
Router *router;
|
||||
QMap<int, ClientPlayer *> players;
|
||||
Router *router;
|
||||
QMap<int, ClientPlayer *> players;
|
||||
|
||||
lua_State *L;
|
||||
lua_State *L;
|
||||
};
|
||||
|
||||
extern Client *ClientInstance;
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
#include "clientplayer.h"
|
||||
|
||||
ClientPlayer::ClientPlayer(int id, QObject* parent)
|
||||
: Player(parent)
|
||||
: Player(parent)
|
||||
{
|
||||
setId(id);
|
||||
setId(id);
|
||||
}
|
||||
|
||||
ClientPlayer::~ClientPlayer()
|
||||
|
|
|
@ -4,23 +4,23 @@
|
|||
#include "player.h"
|
||||
|
||||
class ClientPlayer : public Player {
|
||||
Q_OBJECT
|
||||
Q_OBJECT
|
||||
|
||||
Q_PROPERTY(int id READ getId CONSTANT)
|
||||
Q_PROPERTY(QString screenName
|
||||
READ getScreenName
|
||||
WRITE setScreenName
|
||||
NOTIFY screenNameChanged
|
||||
)
|
||||
Q_PROPERTY(QString avatar
|
||||
READ getAvatar
|
||||
WRITE setAvatar
|
||||
NOTIFY avatarChanged
|
||||
)
|
||||
Q_PROPERTY(int id READ getId CONSTANT)
|
||||
Q_PROPERTY(QString screenName
|
||||
READ getScreenName
|
||||
WRITE setScreenName
|
||||
NOTIFY screenNameChanged
|
||||
)
|
||||
Q_PROPERTY(QString avatar
|
||||
READ getAvatar
|
||||
WRITE setAvatar
|
||||
NOTIFY avatarChanged
|
||||
)
|
||||
|
||||
public:
|
||||
ClientPlayer(int id, QObject *parent = nullptr);
|
||||
~ClientPlayer();
|
||||
ClientPlayer(int id, QObject *parent = nullptr);
|
||||
~ClientPlayer();
|
||||
|
||||
private:
|
||||
};
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
#include "player.h"
|
||||
|
||||
Player::Player(QObject* parent)
|
||||
: QObject(parent)
|
||||
, id(0)
|
||||
, state(Player::Invalid)
|
||||
, ready(false)
|
||||
: QObject(parent)
|
||||
, id(0)
|
||||
, state(Player::Invalid)
|
||||
, ready(false)
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -14,85 +14,85 @@ Player::~Player()
|
|||
|
||||
int Player::getId() const
|
||||
{
|
||||
return id;
|
||||
return id;
|
||||
}
|
||||
|
||||
void Player::setId(int id)
|
||||
{
|
||||
this->id = id;
|
||||
this->id = id;
|
||||
}
|
||||
|
||||
QString Player::getScreenName() const
|
||||
{
|
||||
return screenName;
|
||||
return screenName;
|
||||
}
|
||||
|
||||
void Player::setScreenName(const QString& name)
|
||||
{
|
||||
this->screenName = name;
|
||||
emit screenNameChanged();
|
||||
this->screenName = name;
|
||||
emit screenNameChanged();
|
||||
}
|
||||
|
||||
QString Player::getAvatar() const
|
||||
{
|
||||
return avatar;
|
||||
return avatar;
|
||||
}
|
||||
|
||||
void Player::setAvatar(const QString& avatar)
|
||||
{
|
||||
this->avatar = avatar;
|
||||
emit avatarChanged();
|
||||
this->avatar = avatar;
|
||||
emit avatarChanged();
|
||||
}
|
||||
|
||||
Player::State Player::getState() const
|
||||
{
|
||||
return state;
|
||||
return state;
|
||||
}
|
||||
|
||||
QString Player::getStateString() const
|
||||
{
|
||||
switch (state) {
|
||||
case Online:
|
||||
return QStringLiteral("online");
|
||||
case Trust:
|
||||
return QStringLiteral("trust");
|
||||
case Robot:
|
||||
return QStringLiteral("robot");
|
||||
case Offline:
|
||||
return QStringLiteral("offline");
|
||||
default:
|
||||
return QStringLiteral("invalid");
|
||||
}
|
||||
switch (state) {
|
||||
case Online:
|
||||
return QStringLiteral("online");
|
||||
case Trust:
|
||||
return QStringLiteral("trust");
|
||||
case Robot:
|
||||
return QStringLiteral("robot");
|
||||
case Offline:
|
||||
return QStringLiteral("offline");
|
||||
default:
|
||||
return QStringLiteral("invalid");
|
||||
}
|
||||
}
|
||||
|
||||
void Player::setState(Player::State state)
|
||||
{
|
||||
this->state = state;
|
||||
emit stateChanged();
|
||||
this->state = state;
|
||||
emit stateChanged();
|
||||
}
|
||||
|
||||
void Player::setStateString(const QString &state)
|
||||
{
|
||||
if (state == QStringLiteral("online"))
|
||||
setState(Online);
|
||||
else if (state == QStringLiteral("trust"))
|
||||
setState(Trust);
|
||||
else if (state == QStringLiteral("robot"))
|
||||
setState(Robot);
|
||||
else if (state == QStringLiteral("offline"))
|
||||
setState(Offline);
|
||||
else
|
||||
setState(Invalid);
|
||||
if (state == QStringLiteral("online"))
|
||||
setState(Online);
|
||||
else if (state == QStringLiteral("trust"))
|
||||
setState(Trust);
|
||||
else if (state == QStringLiteral("robot"))
|
||||
setState(Robot);
|
||||
else if (state == QStringLiteral("offline"))
|
||||
setState(Offline);
|
||||
else
|
||||
setState(Invalid);
|
||||
}
|
||||
|
||||
bool Player::isReady() const
|
||||
{
|
||||
return ready;
|
||||
return ready;
|
||||
}
|
||||
|
||||
void Player::setReady(bool ready)
|
||||
{
|
||||
this->ready = ready;
|
||||
emit readyChanged();
|
||||
this->ready = ready;
|
||||
emit readyChanged();
|
||||
}
|
||||
|
||||
|
|
|
@ -4,49 +4,49 @@
|
|||
// Common part of ServerPlayer and ClientPlayer
|
||||
// dont initialize it directly
|
||||
class Player : public QObject {
|
||||
Q_OBJECT
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
enum State{
|
||||
Invalid,
|
||||
Online,
|
||||
Trust, // Trust or run
|
||||
Robot, // only for real robot
|
||||
Offline
|
||||
};
|
||||
enum State{
|
||||
Invalid,
|
||||
Online,
|
||||
Trust, // Trust or run
|
||||
Robot, // only for real robot
|
||||
Offline
|
||||
};
|
||||
|
||||
explicit Player(QObject *parent = nullptr);
|
||||
~Player();
|
||||
explicit Player(QObject *parent = nullptr);
|
||||
~Player();
|
||||
|
||||
int getId() const;
|
||||
void setId(int id);
|
||||
int getId() const;
|
||||
void setId(int id);
|
||||
|
||||
QString getScreenName() const;
|
||||
void setScreenName(const QString &name);
|
||||
QString getScreenName() const;
|
||||
void setScreenName(const QString &name);
|
||||
|
||||
QString getAvatar() const;
|
||||
void setAvatar(const QString &avatar);
|
||||
QString getAvatar() const;
|
||||
void setAvatar(const QString &avatar);
|
||||
|
||||
State getState() const;
|
||||
QString getStateString() const;
|
||||
void setState(State state);
|
||||
void setStateString(const QString &state);
|
||||
State getState() const;
|
||||
QString getStateString() const;
|
||||
void setState(State state);
|
||||
void setStateString(const QString &state);
|
||||
|
||||
bool isReady() const;
|
||||
void setReady(bool ready);
|
||||
bool isReady() const;
|
||||
void setReady(bool ready);
|
||||
|
||||
signals:
|
||||
void screenNameChanged();
|
||||
void avatarChanged();
|
||||
void stateChanged();
|
||||
void readyChanged();
|
||||
void screenNameChanged();
|
||||
void avatarChanged();
|
||||
void stateChanged();
|
||||
void readyChanged();
|
||||
|
||||
private:
|
||||
int id;
|
||||
QString screenName; // screenName should not be same.
|
||||
QString avatar;
|
||||
State state;
|
||||
bool ready;
|
||||
int id;
|
||||
QString screenName; // screenName should not be same.
|
||||
QString avatar;
|
||||
State state;
|
||||
bool ready;
|
||||
};
|
||||
|
||||
#endif // _PLAYER_H
|
||||
|
|
|
@ -1,123 +1,123 @@
|
|||
#include "util.h"
|
||||
|
||||
extern "C" {
|
||||
int luaopen_fk(lua_State *);
|
||||
int luaopen_fk(lua_State *);
|
||||
}
|
||||
|
||||
lua_State *CreateLuaState()
|
||||
{
|
||||
lua_State *L = luaL_newstate();
|
||||
luaL_openlibs(L);
|
||||
luaopen_fk(L);
|
||||
lua_State *L = luaL_newstate();
|
||||
luaL_openlibs(L);
|
||||
luaopen_fk(L);
|
||||
|
||||
return L;
|
||||
return L;
|
||||
}
|
||||
|
||||
bool DoLuaScript(lua_State *L, const char *script)
|
||||
{
|
||||
lua_getglobal(L, "debug");
|
||||
lua_getfield(L, -1, "traceback");
|
||||
lua_replace(L, -2);
|
||||
lua_getglobal(L, "debug");
|
||||
lua_getfield(L, -1, "traceback");
|
||||
lua_replace(L, -2);
|
||||
|
||||
luaL_loadfile(L, script);
|
||||
int error = lua_pcall(L, 0, LUA_MULTRET, -2);
|
||||
luaL_loadfile(L, script);
|
||||
int error = lua_pcall(L, 0, LUA_MULTRET, -2);
|
||||
|
||||
if (error) {
|
||||
const char *error_msg = lua_tostring(L, -1);
|
||||
qDebug() << error_msg;
|
||||
lua_pop(L, 2);
|
||||
return false;
|
||||
}
|
||||
lua_pop(L, 1);
|
||||
return true;
|
||||
if (error) {
|
||||
const char *error_msg = lua_tostring(L, -1);
|
||||
qDebug() << error_msg;
|
||||
lua_pop(L, 2);
|
||||
return false;
|
||||
}
|
||||
lua_pop(L, 1);
|
||||
return true;
|
||||
}
|
||||
|
||||
// For Lua debugging
|
||||
void Dumpstack(lua_State *L)
|
||||
{
|
||||
int top = lua_gettop(L);
|
||||
for (int i = 1; i <= top; i++) {
|
||||
printf("%d\t%s\t", i, luaL_typename(L, i));
|
||||
switch (lua_type(L, i)) {
|
||||
case LUA_TNUMBER:
|
||||
printf("%g\n",lua_tonumber(L, i));
|
||||
break;
|
||||
case LUA_TSTRING:
|
||||
printf("%s\n",lua_tostring(L, i));
|
||||
break;
|
||||
case LUA_TBOOLEAN:
|
||||
printf("%s\n", (lua_toboolean(L, i) ? "true" : "false"));
|
||||
break;
|
||||
case LUA_TNIL:
|
||||
printf("%s\n", "nil");
|
||||
break;
|
||||
default:
|
||||
printf("%p\n",lua_topointer(L, i));
|
||||
break;
|
||||
}
|
||||
int top = lua_gettop(L);
|
||||
for (int i = 1; i <= top; i++) {
|
||||
printf("%d\t%s\t", i, luaL_typename(L, i));
|
||||
switch (lua_type(L, i)) {
|
||||
case LUA_TNUMBER:
|
||||
printf("%g\n",lua_tonumber(L, i));
|
||||
break;
|
||||
case LUA_TSTRING:
|
||||
printf("%s\n",lua_tostring(L, i));
|
||||
break;
|
||||
case LUA_TBOOLEAN:
|
||||
printf("%s\n", (lua_toboolean(L, i) ? "true" : "false"));
|
||||
break;
|
||||
case LUA_TNIL:
|
||||
printf("%s\n", "nil");
|
||||
break;
|
||||
default:
|
||||
printf("%p\n",lua_topointer(L, i));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sqlite3 *OpenDatabase(const QString &filename)
|
||||
{
|
||||
sqlite3 *ret;
|
||||
int rc;
|
||||
if (!QFile::exists(filename)) {
|
||||
QFile file("./server/init.sql");
|
||||
if (!file.open(QIODevice::ReadOnly)) {
|
||||
qDebug() << "cannot open init.sql. Quit now.";
|
||||
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);
|
||||
}
|
||||
sqlite3 *ret;
|
||||
int rc;
|
||||
if (!QFile::exists(filename)) {
|
||||
QFile file("./server/init.sql");
|
||||
if (!file.open(QIODevice::ReadOnly)) {
|
||||
qDebug() << "cannot open init.sql. Quit now.";
|
||||
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
|
||||
static int callback(void *jsonDoc, int argc, char **argv, char **cols) {
|
||||
QJsonObject obj;
|
||||
for (int i = 0; i < argc; i++) {
|
||||
QJsonArray arr = obj[QString(cols[i])].toArray();
|
||||
arr << QString(argv[i] ? argv[i] : "#null");
|
||||
obj[QString(cols[i])] = arr;
|
||||
}
|
||||
((QJsonObject *)jsonDoc)->swap(obj);
|
||||
return 0;
|
||||
QJsonObject obj;
|
||||
for (int i = 0; i < argc; i++) {
|
||||
QJsonArray arr = obj[QString(cols[i])].toArray();
|
||||
arr << QString(argv[i] ? argv[i] : "#null");
|
||||
obj[QString(cols[i])] = arr;
|
||||
}
|
||||
((QJsonObject *)jsonDoc)->swap(obj);
|
||||
return 0;
|
||||
}
|
||||
|
||||
QJsonObject SelectFromDatabase(sqlite3 *db, const QString &sql) {
|
||||
QJsonObject obj;
|
||||
sqlite3_exec(db, sql.toUtf8().data(), callback, (void *)&obj, nullptr);
|
||||
return obj;
|
||||
QJsonObject obj;
|
||||
sqlite3_exec(db, sql.toUtf8().data(), callback, (void *)&obj, nullptr);
|
||||
return obj;
|
||||
}
|
||||
|
||||
QString SelectFromDb(sqlite3 *db, const QString &sql) {
|
||||
QJsonObject obj = SelectFromDatabase(db, sql);
|
||||
return QJsonDocument(obj).toJson();
|
||||
QJsonObject obj = SelectFromDatabase(db, sql);
|
||||
return QJsonDocument(obj).toJson();
|
||||
}
|
||||
|
||||
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) {
|
||||
sqlite3_close(db);
|
||||
sqlite3_close(db);
|
||||
}
|
||||
|
|
86
src/main.cpp
86
src/main.cpp
|
@ -3,59 +3,61 @@
|
|||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
QCoreApplication *app;
|
||||
QCoreApplication::setApplicationName("FreeKill");
|
||||
QCoreApplication::setApplicationVersion("Alpha 0.0.1");
|
||||
QCoreApplication *app;
|
||||
QCoreApplication::setApplicationName("FreeKill");
|
||||
QCoreApplication::setApplicationVersion("Alpha 0.0.1");
|
||||
|
||||
QCommandLineParser parser;
|
||||
parser.setApplicationDescription("FreeKill server");
|
||||
parser.addHelpOption();
|
||||
parser.addVersionOption();
|
||||
parser.addOption({{"s", "server"}, "start server at <port>", "port"});
|
||||
QStringList cliOptions;
|
||||
for (int i = 0; i < argc; i++)
|
||||
cliOptions << argv[i];
|
||||
QCommandLineParser parser;
|
||||
parser.setApplicationDescription("FreeKill server");
|
||||
parser.addHelpOption();
|
||||
parser.addVersionOption();
|
||||
parser.addOption({{"s", "server"}, "start server at <port>", "port"});
|
||||
QStringList cliOptions;
|
||||
for (int i = 0; i < argc; i++)
|
||||
cliOptions << argv[i];
|
||||
|
||||
parser.parse(cliOptions);
|
||||
parser.parse(cliOptions);
|
||||
|
||||
bool startServer = parser.isSet("server");
|
||||
ushort serverPort = 9527;
|
||||
bool startServer = parser.isSet("server");
|
||||
ushort serverPort = 9527;
|
||||
|
||||
if (startServer) {
|
||||
app = new QCoreApplication(argc, argv);
|
||||
bool ok = false;
|
||||
if (parser.value("server").toInt(&ok) && ok)
|
||||
serverPort = parser.value("server").toInt();
|
||||
Server *server = new Server;
|
||||
if (!server->listen(QHostAddress::Any, serverPort)) {
|
||||
fprintf(stderr, "cannot listen on port %d!\n", serverPort);
|
||||
app->exit(1);
|
||||
}
|
||||
return app->exec();
|
||||
if (startServer) {
|
||||
app = new QCoreApplication(argc, argv);
|
||||
bool ok = false;
|
||||
if (parser.value("server").toInt(&ok) && ok)
|
||||
serverPort = parser.value("server").toInt();
|
||||
Server *server = new Server;
|
||||
if (!server->listen(QHostAddress::Any, serverPort)) {
|
||||
fprintf(stderr, "cannot listen on port %d!\n", serverPort);
|
||||
app->exit(1);
|
||||
}
|
||||
return app->exec();
|
||||
}
|
||||
|
||||
app = new QGuiApplication(argc, argv);
|
||||
app = new QGuiApplication(argc, argv);
|
||||
|
||||
QQmlApplicationEngine *engine = new QQmlApplicationEngine;
|
||||
|
||||
QmlBackend backend;
|
||||
backend.setEngine(engine);
|
||||
|
||||
engine->rootContext()->setContextProperty("Backend", &backend);
|
||||
engine->rootContext()->setContextProperty("AppPath", QUrl::fromLocalFile(QDir::currentPath()));
|
||||
QQmlApplicationEngine *engine = new QQmlApplicationEngine;
|
||||
|
||||
QmlBackend backend;
|
||||
backend.setEngine(engine);
|
||||
|
||||
engine->rootContext()->setContextProperty("Backend", &backend);
|
||||
engine->rootContext()->setContextProperty("AppPath", QUrl::fromLocalFile(QDir::currentPath()));
|
||||
#ifdef QT_DEBUG
|
||||
bool debugging = true;
|
||||
bool debugging = true;
|
||||
#else
|
||||
bool debugging = false;
|
||||
bool debugging = false;
|
||||
#endif
|
||||
engine->rootContext()->setContextProperty("Debugging", debugging);
|
||||
engine->load("qml/main.qml");
|
||||
engine->rootContext()->setContextProperty("Debugging", debugging);
|
||||
engine->load("qml/main.qml");
|
||||
if (engine->rootObjects().isEmpty())
|
||||
return -1;
|
||||
|
||||
int ret = app->exec();
|
||||
int ret = app->exec();
|
||||
|
||||
// delete the engine first
|
||||
// to avoid "TypeError: Cannot read property 'xxx' of null"
|
||||
delete engine;
|
||||
// delete the engine first
|
||||
// to avoid "TypeError: Cannot read property 'xxx' of null"
|
||||
delete engine;
|
||||
|
||||
return ret;
|
||||
return ret;
|
||||
}
|
||||
|
|
|
@ -2,94 +2,94 @@
|
|||
|
||||
ClientSocket::ClientSocket() : socket(new QTcpSocket(this))
|
||||
{
|
||||
init();
|
||||
init();
|
||||
}
|
||||
|
||||
ClientSocket::ClientSocket(QTcpSocket* socket)
|
||||
{
|
||||
socket->setParent(this);
|
||||
this->socket = socket;
|
||||
timerSignup.setSingleShot(true);
|
||||
connect(&timerSignup, &QTimer::timeout, this, &ClientSocket::disconnectFromHost);
|
||||
init();
|
||||
socket->setParent(this);
|
||||
this->socket = socket;
|
||||
timerSignup.setSingleShot(true);
|
||||
connect(&timerSignup, &QTimer::timeout, this, &ClientSocket::disconnectFromHost);
|
||||
init();
|
||||
}
|
||||
|
||||
void ClientSocket::init()
|
||||
{
|
||||
connect(socket, &QTcpSocket::connected,
|
||||
this, &ClientSocket::connected);
|
||||
connect(socket, &QTcpSocket::disconnected,
|
||||
this, &ClientSocket::disconnected);
|
||||
connect(socket, &QTcpSocket::readyRead,
|
||||
this, &ClientSocket::getMessage);
|
||||
connect(socket, &QTcpSocket::errorOccurred,
|
||||
this, &ClientSocket::raiseError);
|
||||
connect(socket, &QTcpSocket::connected,
|
||||
this, &ClientSocket::connected);
|
||||
connect(socket, &QTcpSocket::disconnected,
|
||||
this, &ClientSocket::disconnected);
|
||||
connect(socket, &QTcpSocket::readyRead,
|
||||
this, &ClientSocket::getMessage);
|
||||
connect(socket, &QTcpSocket::errorOccurred,
|
||||
this, &ClientSocket::raiseError);
|
||||
}
|
||||
|
||||
void ClientSocket::connectToHost(const QHostAddress &address, ushort port)
|
||||
{
|
||||
socket->connectToHost(address, port);
|
||||
socket->connectToHost(address, port);
|
||||
}
|
||||
|
||||
void ClientSocket::getMessage()
|
||||
{
|
||||
while (socket->canReadLine()) {
|
||||
char msg[16000]; // buffer
|
||||
socket->readLine(msg, sizeof(msg));
|
||||
emit message_got(msg);
|
||||
}
|
||||
while (socket->canReadLine()) {
|
||||
char msg[16000]; // buffer
|
||||
socket->readLine(msg, sizeof(msg));
|
||||
emit message_got(msg);
|
||||
}
|
||||
}
|
||||
|
||||
void ClientSocket::disconnectFromHost()
|
||||
{
|
||||
socket->disconnectFromHost();
|
||||
socket->disconnectFromHost();
|
||||
}
|
||||
|
||||
void ClientSocket::send(const QByteArray &msg)
|
||||
{
|
||||
socket->write(msg);
|
||||
if (!msg.endsWith("\n"))
|
||||
socket->write("\n");
|
||||
socket->flush();
|
||||
socket->write(msg);
|
||||
if (!msg.endsWith("\n"))
|
||||
socket->write("\n");
|
||||
socket->flush();
|
||||
}
|
||||
|
||||
bool ClientSocket::isConnected() const
|
||||
{
|
||||
return socket->state() == QTcpSocket::ConnectedState;
|
||||
return socket->state() == QTcpSocket::ConnectedState;
|
||||
}
|
||||
|
||||
QString ClientSocket::peerName() const
|
||||
{
|
||||
QString peer_name = socket->peerName();
|
||||
if (peer_name.isEmpty())
|
||||
peer_name = QString("%1:%2").arg(socket->peerAddress().toString()).arg(socket->peerPort());
|
||||
QString peer_name = socket->peerName();
|
||||
if (peer_name.isEmpty())
|
||||
peer_name = QString("%1:%2").arg(socket->peerAddress().toString()).arg(socket->peerPort());
|
||||
|
||||
return peer_name;
|
||||
return peer_name;
|
||||
}
|
||||
|
||||
QString ClientSocket::peerAddress() const
|
||||
{
|
||||
return socket->peerAddress().toString();
|
||||
return socket->peerAddress().toString();
|
||||
}
|
||||
|
||||
void ClientSocket::raiseError(QAbstractSocket::SocketError socket_error)
|
||||
{
|
||||
// translate error message
|
||||
QString reason;
|
||||
switch (socket_error) {
|
||||
case QAbstractSocket::ConnectionRefusedError:
|
||||
reason = tr("Connection was refused or timeout"); break;
|
||||
case QAbstractSocket::RemoteHostClosedError:
|
||||
reason = tr("Remote host close this connection"); break;
|
||||
case QAbstractSocket::HostNotFoundError:
|
||||
reason = tr("Host not found"); break;
|
||||
case QAbstractSocket::SocketAccessError:
|
||||
reason = tr("Socket access error"); break;
|
||||
case QAbstractSocket::NetworkError:
|
||||
return; // this error is ignored ...
|
||||
default: reason = tr("Unknow error"); break;
|
||||
}
|
||||
// translate error message
|
||||
QString reason;
|
||||
switch (socket_error) {
|
||||
case QAbstractSocket::ConnectionRefusedError:
|
||||
reason = tr("Connection was refused or timeout"); break;
|
||||
case QAbstractSocket::RemoteHostClosedError:
|
||||
reason = tr("Remote host close this connection"); break;
|
||||
case QAbstractSocket::HostNotFoundError:
|
||||
reason = tr("Host not found"); break;
|
||||
case QAbstractSocket::SocketAccessError:
|
||||
reason = tr("Socket access error"); break;
|
||||
case QAbstractSocket::NetworkError:
|
||||
return; // this error is ignored ...
|
||||
default: reason = tr("Unknow error"); break;
|
||||
}
|
||||
|
||||
emit error_message(tr("Connection failed, error code = %1\n reason: %2")
|
||||
.arg(socket_error).arg(reason));
|
||||
emit error_message(tr("Connection failed, error code = %1\n reason: %2")
|
||||
.arg(socket_error).arg(reason));
|
||||
}
|
||||
|
|
|
@ -2,34 +2,34 @@
|
|||
#define _CLIENT_SOCKET_H
|
||||
|
||||
class ClientSocket : public QObject {
|
||||
Q_OBJECT
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
ClientSocket();
|
||||
// For server use
|
||||
ClientSocket(QTcpSocket *socket);
|
||||
ClientSocket();
|
||||
// For server use
|
||||
ClientSocket(QTcpSocket *socket);
|
||||
|
||||
void connectToHost(const QHostAddress &address = QHostAddress::LocalHost, ushort port = 9527u);
|
||||
void disconnectFromHost();
|
||||
void send(const QByteArray& msg);
|
||||
bool isConnected() const;
|
||||
QString peerName() const;
|
||||
QString peerAddress() const;
|
||||
QTimer timerSignup;
|
||||
void connectToHost(const QHostAddress &address = QHostAddress::LocalHost, ushort port = 9527u);
|
||||
void disconnectFromHost();
|
||||
void send(const QByteArray& msg);
|
||||
bool isConnected() const;
|
||||
QString peerName() const;
|
||||
QString peerAddress() const;
|
||||
QTimer timerSignup;
|
||||
|
||||
signals:
|
||||
void message_got(const QByteArray& msg);
|
||||
void error_message(const QString &msg);
|
||||
void disconnected();
|
||||
void connected();
|
||||
void message_got(const QByteArray& msg);
|
||||
void error_message(const QString &msg);
|
||||
void disconnected();
|
||||
void connected();
|
||||
|
||||
private slots:
|
||||
void getMessage();
|
||||
void raiseError(QAbstractSocket::SocketError error);
|
||||
void getMessage();
|
||||
void raiseError(QAbstractSocket::SocketError error);
|
||||
|
||||
private:
|
||||
QTcpSocket *socket;
|
||||
void init();
|
||||
QTcpSocket *socket;
|
||||
void init();
|
||||
};
|
||||
|
||||
#endif // _CLIENT_SOCKET_H
|
||||
|
|
|
@ -5,200 +5,200 @@
|
|||
#include "serverplayer.h"
|
||||
|
||||
Router::Router(QObject *parent, ClientSocket *socket, RouterType type)
|
||||
: QObject(parent)
|
||||
: QObject(parent)
|
||||
{
|
||||
this->type = type;
|
||||
this->socket = nullptr;
|
||||
setSocket(socket);
|
||||
expectedReplyId = -1;
|
||||
replyTimeout = 0;
|
||||
extraReplyReadySemaphore = nullptr;
|
||||
this->type = type;
|
||||
this->socket = nullptr;
|
||||
setSocket(socket);
|
||||
expectedReplyId = -1;
|
||||
replyTimeout = 0;
|
||||
extraReplyReadySemaphore = nullptr;
|
||||
}
|
||||
|
||||
Router::~Router()
|
||||
{
|
||||
abortRequest();
|
||||
abortRequest();
|
||||
}
|
||||
|
||||
ClientSocket* Router::getSocket() const
|
||||
{
|
||||
return socket;
|
||||
return socket;
|
||||
}
|
||||
|
||||
void Router::setSocket(ClientSocket *socket)
|
||||
{
|
||||
if (this->socket != nullptr) {
|
||||
this->socket->disconnect(this);
|
||||
disconnect(this->socket);
|
||||
this->socket->deleteLater();
|
||||
}
|
||||
if (this->socket != nullptr) {
|
||||
this->socket->disconnect(this);
|
||||
disconnect(this->socket);
|
||||
this->socket->deleteLater();
|
||||
}
|
||||
|
||||
this->socket = nullptr;
|
||||
if (socket != nullptr) {
|
||||
connect(this, &Router::messageReady, socket, &ClientSocket::send);
|
||||
connect(socket, &ClientSocket::message_got, this, &Router::handlePacket);
|
||||
connect(socket, &ClientSocket::disconnected, this, &Router::abortRequest);
|
||||
socket->setParent(this);
|
||||
this->socket = socket;
|
||||
}
|
||||
this->socket = nullptr;
|
||||
if (socket != nullptr) {
|
||||
connect(this, &Router::messageReady, socket, &ClientSocket::send);
|
||||
connect(socket, &ClientSocket::message_got, this, &Router::handlePacket);
|
||||
connect(socket, &ClientSocket::disconnected, this, &Router::abortRequest);
|
||||
socket->setParent(this);
|
||||
this->socket = socket;
|
||||
}
|
||||
}
|
||||
|
||||
void Router::setReplyReadySemaphore(QSemaphore *semaphore)
|
||||
{
|
||||
extraReplyReadySemaphore = semaphore;
|
||||
extraReplyReadySemaphore = semaphore;
|
||||
}
|
||||
|
||||
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
|
||||
if (replyReadySemaphore.available() > 0)
|
||||
replyReadySemaphore.acquire(replyReadySemaphore.available());
|
||||
// In case a request is called without a following waitForReply call
|
||||
if (replyReadySemaphore.available() > 0)
|
||||
replyReadySemaphore.acquire(replyReadySemaphore.available());
|
||||
|
||||
static int requestId = 0;
|
||||
requestId++;
|
||||
static int requestId = 0;
|
||||
requestId++;
|
||||
|
||||
replyMutex.lock();
|
||||
expectedReplyId = requestId;
|
||||
replyTimeout = timeout;
|
||||
requestStartTime = QDateTime::currentDateTime();
|
||||
m_reply = QString();
|
||||
replyMutex.unlock();
|
||||
replyMutex.lock();
|
||||
expectedReplyId = requestId;
|
||||
replyTimeout = timeout;
|
||||
requestStartTime = QDateTime::currentDateTime();
|
||||
m_reply = QString();
|
||||
replyMutex.unlock();
|
||||
|
||||
QJsonArray body;
|
||||
body << requestId;
|
||||
body << type;
|
||||
body << command;
|
||||
body << jsonData;
|
||||
body << timeout;
|
||||
QJsonArray body;
|
||||
body << requestId;
|
||||
body << type;
|
||||
body << command;
|
||||
body << jsonData;
|
||||
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)
|
||||
{
|
||||
QJsonArray body;
|
||||
body << this->requestId;
|
||||
body << type;
|
||||
body << command;
|
||||
body << jsonData;
|
||||
QJsonArray body;
|
||||
body << this->requestId;
|
||||
body << type;
|
||||
body << command;
|
||||
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)
|
||||
{
|
||||
QJsonArray body;
|
||||
body << -2; // requestId = -2 mean this is for notification
|
||||
body << type;
|
||||
body << command;
|
||||
body << jsonData;
|
||||
QJsonArray body;
|
||||
body << -2; // requestId = -2 mean this is for notification
|
||||
body << type;
|
||||
body << command;
|
||||
body << jsonData;
|
||||
|
||||
emit messageReady(QJsonDocument(body).toJson(QJsonDocument::Compact));
|
||||
emit messageReady(QJsonDocument(body).toJson(QJsonDocument::Compact));
|
||||
}
|
||||
|
||||
int Router::getTimeout() const
|
||||
{
|
||||
return requestTimeout;
|
||||
return requestTimeout;
|
||||
}
|
||||
|
||||
// cancel last request from the sender
|
||||
void Router::cancelRequest()
|
||||
{
|
||||
replyMutex.lock();
|
||||
expectedReplyId = -1;
|
||||
replyTimeout = 0;
|
||||
extraReplyReadySemaphore = nullptr;
|
||||
replyMutex.unlock();
|
||||
replyMutex.lock();
|
||||
expectedReplyId = -1;
|
||||
replyTimeout = 0;
|
||||
extraReplyReadySemaphore = nullptr;
|
||||
replyMutex.unlock();
|
||||
|
||||
if (replyReadySemaphore.available() > 0)
|
||||
replyReadySemaphore.acquire(replyReadySemaphore.available());
|
||||
if (replyReadySemaphore.available() > 0)
|
||||
replyReadySemaphore.acquire(replyReadySemaphore.available());
|
||||
}
|
||||
|
||||
QString Router::waitForReply()
|
||||
{
|
||||
replyReadySemaphore.acquire();
|
||||
return m_reply;
|
||||
replyReadySemaphore.acquire();
|
||||
return m_reply;
|
||||
}
|
||||
|
||||
QString Router::waitForReply(int timeout)
|
||||
{
|
||||
replyReadySemaphore.tryAcquire(1, timeout * 1000);
|
||||
return m_reply;
|
||||
replyReadySemaphore.tryAcquire(1, timeout * 1000);
|
||||
return m_reply;
|
||||
}
|
||||
|
||||
void Router::abortRequest()
|
||||
{
|
||||
replyMutex.lock();
|
||||
if (expectedReplyId != -1) {
|
||||
replyReadySemaphore.release();
|
||||
if (extraReplyReadySemaphore)
|
||||
extraReplyReadySemaphore->release();
|
||||
expectedReplyId = -1;
|
||||
extraReplyReadySemaphore = nullptr;
|
||||
}
|
||||
replyMutex.unlock();
|
||||
replyMutex.lock();
|
||||
if (expectedReplyId != -1) {
|
||||
replyReadySemaphore.release();
|
||||
if (extraReplyReadySemaphore)
|
||||
extraReplyReadySemaphore->release();
|
||||
expectedReplyId = -1;
|
||||
extraReplyReadySemaphore = nullptr;
|
||||
}
|
||||
replyMutex.unlock();
|
||||
}
|
||||
|
||||
void Router::handlePacket(const QByteArray& rawPacket)
|
||||
{
|
||||
QJsonDocument packet = QJsonDocument::fromJson(rawPacket);
|
||||
if (packet.isNull() || !packet.isArray())
|
||||
return;
|
||||
QJsonDocument packet = QJsonDocument::fromJson(rawPacket);
|
||||
if (packet.isNull() || !packet.isArray())
|
||||
return;
|
||||
|
||||
int requestId = packet[0].toInt();
|
||||
int type = packet[1].toInt();
|
||||
QString command = packet[2].toString();
|
||||
QString jsonData = packet[3].toString();
|
||||
int requestId = packet[0].toInt();
|
||||
int type = packet[1].toInt();
|
||||
QString command = packet[2].toString();
|
||||
QString jsonData = packet[3].toString();
|
||||
|
||||
if (type & TYPE_NOTIFICATION) {
|
||||
if (type & DEST_CLIENT) {
|
||||
ClientInstance->callLua(command, jsonData);
|
||||
} else {
|
||||
ServerPlayer *player = qobject_cast<ServerPlayer *>(parent());
|
||||
// Add the uid of sender to jsonData
|
||||
QJsonArray arr = QJsonDocument::fromJson(jsonData.toUtf8()).array();
|
||||
arr.prepend(player->getId());
|
||||
if (type & TYPE_NOTIFICATION) {
|
||||
if (type & DEST_CLIENT) {
|
||||
ClientInstance->callLua(command, jsonData);
|
||||
} else {
|
||||
ServerPlayer *player = qobject_cast<ServerPlayer *>(parent());
|
||||
// Add the uid of sender to jsonData
|
||||
QJsonArray arr = QJsonDocument::fromJson(jsonData.toUtf8()).array();
|
||||
arr.prepend(player->getId());
|
||||
|
||||
Room *room = player->getRoom();
|
||||
room->lockLua(__FUNCTION__);
|
||||
room->callLua(command, QJsonDocument(arr).toJson());
|
||||
room->unlockLua(__FUNCTION__);
|
||||
}
|
||||
Room *room = player->getRoom();
|
||||
room->lockLua(__FUNCTION__);
|
||||
room->callLua(command, QJsonDocument(arr).toJson());
|
||||
room->unlockLua(__FUNCTION__);
|
||||
}
|
||||
else if (type & TYPE_REQUEST) {
|
||||
this->requestId = requestId;
|
||||
this->requestTimeout = packet[4].toInt();
|
||||
}
|
||||
else if (type & TYPE_REQUEST) {
|
||||
this->requestId = requestId;
|
||||
this->requestTimeout = packet[4].toInt();
|
||||
|
||||
if (type & DEST_CLIENT) {
|
||||
qobject_cast<Client *>(parent())->callLua(command, jsonData);
|
||||
} else {
|
||||
// requesting server is not allowed
|
||||
Q_ASSERT(false);
|
||||
}
|
||||
if (type & DEST_CLIENT) {
|
||||
qobject_cast<Client *>(parent())->callLua(command, jsonData);
|
||||
} else {
|
||||
// requesting server is not allowed
|
||||
Q_ASSERT(false);
|
||||
}
|
||||
else if (type & TYPE_REPLY) {
|
||||
QMutexLocker locker(&replyMutex);
|
||||
}
|
||||
else if (type & TYPE_REPLY) {
|
||||
QMutexLocker locker(&replyMutex);
|
||||
|
||||
if (requestId != this->expectedReplyId)
|
||||
return;
|
||||
if (requestId != this->expectedReplyId)
|
||||
return;
|
||||
|
||||
this->expectedReplyId = -1;
|
||||
this->expectedReplyId = -1;
|
||||
|
||||
if (replyTimeout >= 0 && replyTimeout <
|
||||
requestStartTime.secsTo(QDateTime::currentDateTime()))
|
||||
return;
|
||||
if (replyTimeout >= 0 && replyTimeout <
|
||||
requestStartTime.secsTo(QDateTime::currentDateTime()))
|
||||
return;
|
||||
|
||||
m_reply = jsonData;
|
||||
// TODO: callback?
|
||||
m_reply = jsonData;
|
||||
// TODO: callback?
|
||||
|
||||
replyReadySemaphore.release();
|
||||
if (extraReplyReadySemaphore) {
|
||||
extraReplyReadySemaphore->release();
|
||||
extraReplyReadySemaphore = nullptr;
|
||||
}
|
||||
locker.unlock();
|
||||
emit replyReady();
|
||||
replyReadySemaphore.release();
|
||||
if (extraReplyReadySemaphore) {
|
||||
extraReplyReadySemaphore->release();
|
||||
extraReplyReadySemaphore = nullptr;
|
||||
}
|
||||
locker.unlock();
|
||||
emit replyReady();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -4,75 +4,75 @@
|
|||
class ClientSocket;
|
||||
|
||||
class Router : public QObject {
|
||||
Q_OBJECT
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
enum PacketType {
|
||||
TYPE_REQUEST = 0x100,
|
||||
TYPE_REPLY = 0x200,
|
||||
TYPE_NOTIFICATION = 0x400,
|
||||
SRC_CLIENT = 0x010,
|
||||
SRC_SERVER = 0x020,
|
||||
SRC_LOBBY = 0x040,
|
||||
DEST_CLIENT = 0x001,
|
||||
DEST_SERVER = 0x002,
|
||||
DEST_LOBBY = 0x004
|
||||
};
|
||||
enum PacketType {
|
||||
TYPE_REQUEST = 0x100,
|
||||
TYPE_REPLY = 0x200,
|
||||
TYPE_NOTIFICATION = 0x400,
|
||||
SRC_CLIENT = 0x010,
|
||||
SRC_SERVER = 0x020,
|
||||
SRC_LOBBY = 0x040,
|
||||
DEST_CLIENT = 0x001,
|
||||
DEST_SERVER = 0x002,
|
||||
DEST_LOBBY = 0x004
|
||||
};
|
||||
|
||||
enum RouterType {
|
||||
TYPE_SERVER,
|
||||
TYPE_CLIENT
|
||||
};
|
||||
Router(QObject *parent, ClientSocket *socket, RouterType type);
|
||||
~Router();
|
||||
enum RouterType {
|
||||
TYPE_SERVER,
|
||||
TYPE_CLIENT
|
||||
};
|
||||
Router(QObject *parent, ClientSocket *socket, RouterType type);
|
||||
~Router();
|
||||
|
||||
ClientSocket *getSocket() const;
|
||||
void setSocket(ClientSocket *socket);
|
||||
ClientSocket *getSocket() const;
|
||||
void setSocket(ClientSocket *socket);
|
||||
|
||||
void setReplyReadySemaphore(QSemaphore *semaphore);
|
||||
void setReplyReadySemaphore(QSemaphore *semaphore);
|
||||
|
||||
void request(int type, const QString &command,
|
||||
const QString &jsonData, int timeout);
|
||||
void reply(int type, const QString &command, const QString &jsonData);
|
||||
void notify(int type, const QString &command, const QString &jsonData);
|
||||
void request(int type, const QString &command,
|
||||
const QString &jsonData, int timeout);
|
||||
void reply(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 abortRequest();
|
||||
void cancelRequest();
|
||||
void abortRequest();
|
||||
|
||||
QString waitForReply();
|
||||
QString waitForReply(int timeout);
|
||||
QString waitForReply();
|
||||
QString waitForReply(int timeout);
|
||||
|
||||
signals:
|
||||
void messageReady(const QByteArray &message);
|
||||
void unknownPacket(const QByteArray &packet);
|
||||
void replyReady();
|
||||
void messageReady(const QByteArray &message);
|
||||
void unknownPacket(const QByteArray &packet);
|
||||
void replyReady();
|
||||
|
||||
protected:
|
||||
void handlePacket(const QByteArray &rawPacket);
|
||||
void handlePacket(const QByteArray &rawPacket);
|
||||
|
||||
private:
|
||||
ClientSocket *socket;
|
||||
RouterType type;
|
||||
ClientSocket *socket;
|
||||
RouterType type;
|
||||
|
||||
// For sender
|
||||
int requestId;
|
||||
int requestTimeout;
|
||||
// For sender
|
||||
int requestId;
|
||||
int requestTimeout;
|
||||
|
||||
// For receiver
|
||||
QDateTime requestStartTime;
|
||||
QMutex replyMutex;
|
||||
int expectedReplyId;
|
||||
int replyTimeout;
|
||||
QString m_reply; // should be json string
|
||||
QSemaphore replyReadySemaphore;
|
||||
QSemaphore *extraReplyReadySemaphore;
|
||||
// For receiver
|
||||
QDateTime requestStartTime;
|
||||
QMutex replyMutex;
|
||||
int expectedReplyId;
|
||||
int replyTimeout;
|
||||
QString m_reply; // should be json string
|
||||
QSemaphore replyReadySemaphore;
|
||||
QSemaphore *extraReplyReadySemaphore;
|
||||
|
||||
// Two Lua global table for callbacks and interactions
|
||||
// stored in the lua_State of the sender
|
||||
// LuaTable interactions;
|
||||
// LuaTable callbacks;
|
||||
// Two Lua global table for callbacks and interactions
|
||||
// stored in the lua_State of the sender
|
||||
// LuaTable interactions;
|
||||
// LuaTable callbacks;
|
||||
};
|
||||
|
||||
#endif // _ROUTER_H
|
||||
|
|
|
@ -3,23 +3,23 @@
|
|||
|
||||
ServerSocket::ServerSocket()
|
||||
{
|
||||
server = new QTcpServer(this);
|
||||
connect(server, &QTcpServer::newConnection,
|
||||
this, &ServerSocket::processNewConnection);
|
||||
server = new QTcpServer(this);
|
||||
connect(server, &QTcpServer::newConnection,
|
||||
this, &ServerSocket::processNewConnection);
|
||||
}
|
||||
|
||||
bool ServerSocket::listen(const QHostAddress &address, ushort port)
|
||||
{
|
||||
return server->listen(address, port);
|
||||
return server->listen(address, port);
|
||||
}
|
||||
|
||||
void ServerSocket::processNewConnection()
|
||||
{
|
||||
QTcpSocket *socket = server->nextPendingConnection();
|
||||
ClientSocket *connection = new ClientSocket(socket);
|
||||
connect(connection, &ClientSocket::disconnected, this, [connection](){
|
||||
connection->deleteLater();
|
||||
});
|
||||
emit new_connection(connection);
|
||||
QTcpSocket *socket = server->nextPendingConnection();
|
||||
ClientSocket *connection = new ClientSocket(socket);
|
||||
connect(connection, &ClientSocket::disconnected, this, [connection](){
|
||||
connection->deleteLater();
|
||||
});
|
||||
emit new_connection(connection);
|
||||
}
|
||||
|
||||
|
|
|
@ -4,21 +4,21 @@
|
|||
class ClientSocket;
|
||||
|
||||
class ServerSocket : public QObject {
|
||||
Q_OBJECT
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
ServerSocket();
|
||||
ServerSocket();
|
||||
|
||||
bool listen(const QHostAddress &address = QHostAddress::Any, ushort port = 9527u);
|
||||
bool listen(const QHostAddress &address = QHostAddress::Any, ushort port = 9527u);
|
||||
|
||||
signals:
|
||||
void new_connection(ClientSocket *socket);
|
||||
void new_connection(ClientSocket *socket);
|
||||
|
||||
private slots:
|
||||
void processNewConnection();
|
||||
void processNewConnection();
|
||||
|
||||
private:
|
||||
QTcpServer *server;
|
||||
QTcpServer *server;
|
||||
};
|
||||
|
||||
#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)
|
||||
{
|
||||
id = server->nextRoomId;
|
||||
server->nextRoomId++;
|
||||
this->server = server;
|
||||
setParent(server);
|
||||
owner = nullptr;
|
||||
gameStarted = false;
|
||||
robot_id = -1;
|
||||
timeout = 15;
|
||||
if (!isLobby()) {
|
||||
connect(this, &Room::playerAdded, server->lobby(), &Room::removePlayer);
|
||||
connect(this, &Room::playerRemoved, server->lobby(), &Room::addPlayer);
|
||||
}
|
||||
id = server->nextRoomId;
|
||||
server->nextRoomId++;
|
||||
this->server = server;
|
||||
setParent(server);
|
||||
owner = nullptr;
|
||||
gameStarted = false;
|
||||
robot_id = -1;
|
||||
timeout = 15;
|
||||
if (!isLobby()) {
|
||||
connect(this, &Room::playerAdded, server->lobby(), &Room::removePlayer);
|
||||
connect(this, &Room::playerRemoved, server->lobby(), &Room::addPlayer);
|
||||
}
|
||||
|
||||
L = CreateLuaState();
|
||||
DoLuaScript(L, "lua/freekill.lua");
|
||||
if (isLobby()) {
|
||||
DoLuaScript(L, "lua/server/lobby.lua");
|
||||
} else {
|
||||
DoLuaScript(L, "lua/server/room.lua");
|
||||
}
|
||||
initLua();
|
||||
L = CreateLuaState();
|
||||
DoLuaScript(L, "lua/freekill.lua");
|
||||
if (isLobby()) {
|
||||
DoLuaScript(L, "lua/server/lobby.lua");
|
||||
} else {
|
||||
DoLuaScript(L, "lua/server/room.lua");
|
||||
}
|
||||
initLua();
|
||||
}
|
||||
|
||||
Room::~Room()
|
||||
{
|
||||
// TODO
|
||||
if (isRunning()) {
|
||||
callLua("RoomDeleted", "");
|
||||
unlockLua(__FUNCTION__);
|
||||
wait();
|
||||
}
|
||||
lua_close(L);
|
||||
// TODO
|
||||
if (isRunning()) {
|
||||
callLua("RoomDeleted", "");
|
||||
unlockLua(__FUNCTION__);
|
||||
wait();
|
||||
}
|
||||
lua_close(L);
|
||||
}
|
||||
|
||||
Server *Room::getServer() const
|
||||
{
|
||||
return server;
|
||||
return server;
|
||||
}
|
||||
|
||||
int Room::getId() const
|
||||
{
|
||||
return id;
|
||||
return id;
|
||||
}
|
||||
|
||||
bool Room::isLobby() const
|
||||
{
|
||||
return id == 0;
|
||||
return id == 0;
|
||||
}
|
||||
|
||||
QString Room::getName() const
|
||||
{
|
||||
return name;
|
||||
return name;
|
||||
}
|
||||
|
||||
void Room::setName(const QString &name)
|
||||
{
|
||||
this->name = name;
|
||||
this->name = name;
|
||||
}
|
||||
|
||||
int Room::getCapacity() const
|
||||
{
|
||||
return capacity;
|
||||
return capacity;
|
||||
}
|
||||
|
||||
void Room::setCapacity(int capacity)
|
||||
{
|
||||
this->capacity = capacity;
|
||||
this->capacity = capacity;
|
||||
}
|
||||
|
||||
bool Room::isFull() const
|
||||
{
|
||||
return players.count() == capacity;
|
||||
return players.count() == capacity;
|
||||
}
|
||||
|
||||
bool Room::isAbandoned() const
|
||||
{
|
||||
if (players.isEmpty())
|
||||
return true;
|
||||
|
||||
foreach (ServerPlayer *p, players) {
|
||||
if (p->getState() == Player::Online)
|
||||
return false;
|
||||
}
|
||||
if (players.isEmpty())
|
||||
return true;
|
||||
|
||||
foreach (ServerPlayer *p, players) {
|
||||
if (p->getState() == Player::Online)
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
ServerPlayer *Room::getOwner() const
|
||||
{
|
||||
return owner;
|
||||
return owner;
|
||||
}
|
||||
|
||||
void Room::setOwner(ServerPlayer *owner)
|
||||
{
|
||||
this->owner = owner;
|
||||
QJsonArray jsonData;
|
||||
jsonData << owner->getId();
|
||||
doBroadcastNotify(players, "RoomOwner", QJsonDocument(jsonData).toJson());
|
||||
this->owner = owner;
|
||||
QJsonArray jsonData;
|
||||
jsonData << owner->getId();
|
||||
doBroadcastNotify(players, "RoomOwner", QJsonDocument(jsonData).toJson());
|
||||
}
|
||||
|
||||
void Room::addPlayer(ServerPlayer *player)
|
||||
{
|
||||
if (!player) return;
|
||||
if (!player) return;
|
||||
|
||||
if (isFull() || gameStarted) {
|
||||
player->doNotify("ErrorMsg", "Room is full or already started!");
|
||||
if (runned_players.contains(player->getId())) {
|
||||
player->doNotify("ErrorMsg", "Running away is shameful.");
|
||||
}
|
||||
return;
|
||||
if (isFull() || gameStarted) {
|
||||
player->doNotify("ErrorMsg", "Room is full or already started!");
|
||||
if (runned_players.contains(player->getId())) {
|
||||
player->doNotify("ErrorMsg", "Running away is shameful.");
|
||||
}
|
||||
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;
|
||||
|
||||
// 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());
|
||||
if (this->owner != nullptr) {
|
||||
jsonData = QJsonArray();
|
||||
jsonData << this->owner->getId();
|
||||
player->doNotify("RoomOwner", 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());
|
||||
}
|
||||
|
||||
if (this->owner != nullptr) {
|
||||
jsonData = QJsonArray();
|
||||
jsonData << this->owner->getId();
|
||||
player->doNotify("RoomOwner", QJsonDocument(jsonData).toJson());
|
||||
}
|
||||
|
||||
if (isFull() && !gameStarted)
|
||||
start();
|
||||
}
|
||||
emit playerAdded(player);
|
||||
if (isFull() && !gameStarted)
|
||||
start();
|
||||
}
|
||||
emit playerAdded(player);
|
||||
}
|
||||
|
||||
void Room::addRobot(ServerPlayer *player)
|
||||
{
|
||||
if (player != owner || isFull()) return;
|
||||
if (player != owner || isFull()) return;
|
||||
|
||||
ServerPlayer *robot = new ServerPlayer(this);
|
||||
robot->setState(Player::Robot);
|
||||
robot->setId(robot_id);
|
||||
robot->setAvatar("guanyu");
|
||||
robot->setScreenName(QString("COMP-%1").arg(robot_id));
|
||||
robot_id--;
|
||||
ServerPlayer *robot = new ServerPlayer(this);
|
||||
robot->setState(Player::Robot);
|
||||
robot->setId(robot_id);
|
||||
robot->setAvatar("guanyu");
|
||||
robot->setScreenName(QString("COMP-%1").arg(robot_id));
|
||||
robot_id--;
|
||||
|
||||
addPlayer(robot);
|
||||
addPlayer(robot);
|
||||
}
|
||||
|
||||
void Room::removePlayer(ServerPlayer *player)
|
||||
{
|
||||
players.removeOne(player);
|
||||
emit playerRemoved(player);
|
||||
players.removeOne(player);
|
||||
emit playerRemoved(player);
|
||||
|
||||
if (isLobby()) return;
|
||||
if (isLobby()) return;
|
||||
|
||||
if (gameStarted) {
|
||||
// TODO: if the player is died..
|
||||
if (gameStarted) {
|
||||
// TODO: if the player is died..
|
||||
|
||||
// create robot first
|
||||
ServerPlayer *robot = new ServerPlayer(this);
|
||||
robot->setState(Player::Robot);
|
||||
robot->setId(robot_id);
|
||||
robot->setAvatar(player->getAvatar());
|
||||
robot->setScreenName(QString("COMP-%1").arg(robot_id));
|
||||
robot_id--;
|
||||
// create robot first
|
||||
ServerPlayer *robot = new ServerPlayer(this);
|
||||
robot->setState(Player::Robot);
|
||||
robot->setId(robot_id);
|
||||
robot->setAvatar(player->getAvatar());
|
||||
robot->setScreenName(QString("COMP-%1").arg(robot_id));
|
||||
robot_id--;
|
||||
|
||||
players.append(robot);
|
||||
players.append(robot);
|
||||
|
||||
// tell lua & clients
|
||||
QJsonArray jsonData;
|
||||
jsonData << player->getId();
|
||||
jsonData << robot->getId();
|
||||
callLua("PlayerRunned", QJsonDocument(jsonData).toJson());
|
||||
doBroadcastNotify(getPlayers(), "PlayerRunned", QJsonDocument(jsonData).toJson());
|
||||
runned_players << player->getId();
|
||||
// tell lua & clients
|
||||
QJsonArray jsonData;
|
||||
jsonData << player->getId();
|
||||
jsonData << robot->getId();
|
||||
callLua("PlayerRunned", QJsonDocument(jsonData).toJson());
|
||||
doBroadcastNotify(getPlayers(), "PlayerRunned", QJsonDocument(jsonData).toJson());
|
||||
runned_players << player->getId();
|
||||
|
||||
// FIXME: abortRequest here will result crash
|
||||
// but if dont abort and room is abandoned, the main thread will wait until replyed
|
||||
// player->abortRequest();
|
||||
} else {
|
||||
QJsonArray jsonData;
|
||||
jsonData << player->getId();
|
||||
doBroadcastNotify(getPlayers(), "RemovePlayer", QJsonDocument(jsonData).toJson());
|
||||
}
|
||||
// FIXME: abortRequest here will result crash
|
||||
// but if dont abort and room is abandoned, the main thread will wait until replyed
|
||||
// player->abortRequest();
|
||||
} else {
|
||||
QJsonArray jsonData;
|
||||
jsonData << player->getId();
|
||||
doBroadcastNotify(getPlayers(), "RemovePlayer", QJsonDocument(jsonData).toJson());
|
||||
}
|
||||
|
||||
if (isAbandoned()) {
|
||||
// FIXME: do not delete room here
|
||||
// create a new thread and delete the room
|
||||
emit abandoned();
|
||||
} else if (player == owner) {
|
||||
setOwner(players.first());
|
||||
}
|
||||
if (isAbandoned()) {
|
||||
// FIXME: do not delete room here
|
||||
// create a new thread and delete the room
|
||||
emit abandoned();
|
||||
} else if (player == owner) {
|
||||
setOwner(players.first());
|
||||
}
|
||||
}
|
||||
|
||||
QList<ServerPlayer *> Room::getPlayers() const
|
||||
{
|
||||
return players;
|
||||
return players;
|
||||
}
|
||||
|
||||
QList<ServerPlayer *> Room::getOtherPlayers(ServerPlayer* expect) const
|
||||
{
|
||||
QList<ServerPlayer *> others = getPlayers();
|
||||
others.removeOne(expect);
|
||||
return others;
|
||||
QList<ServerPlayer *> others = getPlayers();
|
||||
others.removeOne(expect);
|
||||
return others;
|
||||
}
|
||||
|
||||
ServerPlayer *Room::findPlayer(int id) const
|
||||
{
|
||||
foreach (ServerPlayer *p, players) {
|
||||
if (p->getId() == id)
|
||||
return p;
|
||||
}
|
||||
return nullptr;
|
||||
foreach (ServerPlayer *p, players) {
|
||||
if (p->getId() == id)
|
||||
return p;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
int Room::getTimeout() const
|
||||
{
|
||||
return timeout;
|
||||
return timeout;
|
||||
}
|
||||
|
||||
void Room::setTimeout(int timeout)
|
||||
{
|
||||
this->timeout = timeout;
|
||||
this->timeout = timeout;
|
||||
}
|
||||
|
||||
bool Room::isStarted() const
|
||||
{
|
||||
return gameStarted;
|
||||
return gameStarted;
|
||||
}
|
||||
|
||||
void Room::doRequest(const QList<ServerPlayer *> targets, int timeout)
|
||||
{
|
||||
// TODO
|
||||
// TODO
|
||||
}
|
||||
|
||||
void Room::doNotify(const QList<ServerPlayer *> targets, int timeout)
|
||||
{
|
||||
// TODO
|
||||
// TODO
|
||||
}
|
||||
|
||||
void Room::doBroadcastNotify(const QList<ServerPlayer *> targets,
|
||||
const QString& command, const QString& jsonData)
|
||||
const QString& command, const QString& jsonData)
|
||||
{
|
||||
foreach (ServerPlayer *p, targets) {
|
||||
p->doNotify(command, jsonData);
|
||||
}
|
||||
foreach (ServerPlayer *p, targets) {
|
||||
p->doNotify(command, jsonData);
|
||||
}
|
||||
}
|
||||
|
||||
void Room::gameOver()
|
||||
{
|
||||
gameStarted = false;
|
||||
runned_players.clear();
|
||||
// clean not online players
|
||||
foreach (ServerPlayer *p, players) {
|
||||
if (p->getState() != Player::Online) {
|
||||
p->deleteLater();
|
||||
}
|
||||
gameStarted = false;
|
||||
runned_players.clear();
|
||||
// clean not online players
|
||||
foreach (ServerPlayer *p, players) {
|
||||
if (p->getState() != Player::Online) {
|
||||
p->deleteLater();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Room::lockLua(const QString &caller)
|
||||
{
|
||||
if (!gameStarted) return;
|
||||
lua_mutex.lock();
|
||||
if (!gameStarted) return;
|
||||
lua_mutex.lock();
|
||||
#ifdef QT_DEBUG
|
||||
//qDebug() << caller << "=> room->L is locked.";
|
||||
//qDebug() << caller << "=> room->L is locked.";
|
||||
#endif
|
||||
}
|
||||
|
||||
void Room::unlockLua(const QString &caller)
|
||||
{
|
||||
if (!gameStarted) return;
|
||||
lua_mutex.unlock();
|
||||
if (!gameStarted) return;
|
||||
lua_mutex.unlock();
|
||||
#ifdef QT_DEBUG
|
||||
//qDebug() << caller << "=> room->L is unlocked.";
|
||||
//qDebug() << caller << "=> room->L is unlocked.";
|
||||
#endif
|
||||
}
|
||||
|
||||
void Room::run()
|
||||
{
|
||||
gameStarted = true;
|
||||
lockLua(__FUNCTION__);
|
||||
roomStart();
|
||||
unlockLua(__FUNCTION__);
|
||||
gameStarted = true;
|
||||
lockLua(__FUNCTION__);
|
||||
roomStart();
|
||||
unlockLua(__FUNCTION__);
|
||||
}
|
||||
|
|
|
@ -5,86 +5,86 @@ class Server;
|
|||
class ServerPlayer;
|
||||
|
||||
class Room : public QThread {
|
||||
Q_OBJECT
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit Room(Server *m_server);
|
||||
~Room();
|
||||
explicit Room(Server *m_server);
|
||||
~Room();
|
||||
|
||||
// Property reader & setter
|
||||
// ==================================={
|
||||
Server *getServer() const;
|
||||
int getId() const;
|
||||
bool isLobby() const;
|
||||
QString getName() const;
|
||||
void setName(const QString &name);
|
||||
int getCapacity() const;
|
||||
void setCapacity(int capacity);
|
||||
bool isFull() const;
|
||||
bool isAbandoned() const;
|
||||
// Property reader & setter
|
||||
// ==================================={
|
||||
Server *getServer() const;
|
||||
int getId() const;
|
||||
bool isLobby() const;
|
||||
QString getName() const;
|
||||
void setName(const QString &name);
|
||||
int getCapacity() const;
|
||||
void setCapacity(int capacity);
|
||||
bool isFull() const;
|
||||
bool isAbandoned() const;
|
||||
|
||||
ServerPlayer *getOwner() const;
|
||||
void setOwner(ServerPlayer *owner);
|
||||
ServerPlayer *getOwner() const;
|
||||
void setOwner(ServerPlayer *owner);
|
||||
|
||||
void addPlayer(ServerPlayer *player);
|
||||
void addRobot(ServerPlayer *player);
|
||||
void removePlayer(ServerPlayer *player);
|
||||
QList<ServerPlayer*> getPlayers() const;
|
||||
QList<ServerPlayer *> getOtherPlayers(ServerPlayer *expect) const;
|
||||
ServerPlayer *findPlayer(int id) const;
|
||||
void addPlayer(ServerPlayer *player);
|
||||
void addRobot(ServerPlayer *player);
|
||||
void removePlayer(ServerPlayer *player);
|
||||
QList<ServerPlayer*> getPlayers() const;
|
||||
QList<ServerPlayer *> getOtherPlayers(ServerPlayer *expect) const;
|
||||
ServerPlayer *findPlayer(int id) const;
|
||||
|
||||
int getTimeout() const;
|
||||
void setTimeout(int timeout);
|
||||
int getTimeout() const;
|
||||
void setTimeout(int timeout);
|
||||
|
||||
bool isStarted() const;
|
||||
// ====================================}
|
||||
bool isStarted() const;
|
||||
// ====================================}
|
||||
|
||||
void doRequest(const QList<ServerPlayer *> targets, int timeout);
|
||||
void doNotify(const QList<ServerPlayer *> targets, int timeout);
|
||||
void doRequest(const QList<ServerPlayer *> targets, int timeout);
|
||||
void doNotify(const QList<ServerPlayer *> targets, int timeout);
|
||||
|
||||
void doBroadcastNotify(
|
||||
const QList<ServerPlayer *> targets,
|
||||
const QString &command,
|
||||
const QString &jsonData
|
||||
);
|
||||
void doBroadcastNotify(
|
||||
const QList<ServerPlayer *> targets,
|
||||
const QString &command,
|
||||
const QString &jsonData
|
||||
);
|
||||
|
||||
void gameOver();
|
||||
void gameOver();
|
||||
|
||||
void initLua();
|
||||
void callLua(const QString &command, const QString &jsonData);
|
||||
LuaFunction callback;
|
||||
void initLua();
|
||||
void callLua(const QString &command, const QString &jsonData);
|
||||
LuaFunction callback;
|
||||
|
||||
void roomStart();
|
||||
LuaFunction startGame;
|
||||
void roomStart();
|
||||
LuaFunction startGame;
|
||||
|
||||
void lockLua(const QString &caller);
|
||||
void unlockLua(const QString &caller);
|
||||
void lockLua(const QString &caller);
|
||||
void unlockLua(const QString &caller);
|
||||
|
||||
signals:
|
||||
void abandoned();
|
||||
void abandoned();
|
||||
|
||||
void playerAdded(ServerPlayer *player);
|
||||
void playerRemoved(ServerPlayer *player);
|
||||
void playerAdded(ServerPlayer *player);
|
||||
void playerRemoved(ServerPlayer *player);
|
||||
|
||||
protected:
|
||||
virtual void run();
|
||||
virtual void run();
|
||||
|
||||
private:
|
||||
Server *server;
|
||||
int id; // Lobby's id is 0
|
||||
QString name; // “阴间大乱斗”
|
||||
int capacity; // by default is 5, max is 8
|
||||
bool m_abandoned; // If room is empty, delete it
|
||||
Server *server;
|
||||
int id; // Lobby's id is 0
|
||||
QString name; // “阴间大乱斗”
|
||||
int capacity; // by default is 5, max is 8
|
||||
bool m_abandoned; // If room is empty, delete it
|
||||
|
||||
ServerPlayer *owner; // who created this room?
|
||||
QList<ServerPlayer *> players;
|
||||
QList<int> runned_players;
|
||||
int robot_id;
|
||||
bool gameStarted;
|
||||
ServerPlayer *owner; // who created this room?
|
||||
QList<ServerPlayer *> players;
|
||||
QList<int> runned_players;
|
||||
int robot_id;
|
||||
bool gameStarted;
|
||||
|
||||
int timeout;
|
||||
int timeout;
|
||||
|
||||
lua_State *L;
|
||||
QMutex lua_mutex;
|
||||
lua_State *L;
|
||||
QMutex lua_mutex;
|
||||
};
|
||||
|
||||
#endif // _ROOM_H
|
||||
|
|
|
@ -9,253 +9,253 @@
|
|||
Server *ServerInstance;
|
||||
|
||||
Server::Server(QObject* parent)
|
||||
: QObject(parent)
|
||||
: QObject(parent)
|
||||
{
|
||||
ServerInstance = this;
|
||||
db = OpenDatabase();
|
||||
server = new ServerSocket();
|
||||
server->setParent(this);
|
||||
connect(server, &ServerSocket::new_connection,
|
||||
this, &Server::processNewConnection);
|
||||
ServerInstance = this;
|
||||
db = OpenDatabase();
|
||||
server = new ServerSocket();
|
||||
server->setParent(this);
|
||||
connect(server, &ServerSocket::new_connection,
|
||||
this, &Server::processNewConnection);
|
||||
|
||||
// create lobby
|
||||
nextRoomId = 0;
|
||||
createRoom(nullptr, "Lobby", INT32_MAX);
|
||||
connect(lobby(), &Room::playerAdded, this, &Server::updateRoomList);
|
||||
connect(lobby(), &Room::playerRemoved, this, &Server::updateRoomList);
|
||||
// create lobby
|
||||
nextRoomId = 0;
|
||||
createRoom(nullptr, "Lobby", INT32_MAX);
|
||||
connect(lobby(), &Room::playerAdded, this, &Server::updateRoomList);
|
||||
connect(lobby(), &Room::playerRemoved, this, &Server::updateRoomList);
|
||||
}
|
||||
|
||||
Server::~Server()
|
||||
{
|
||||
ServerInstance = nullptr;
|
||||
m_lobby->deleteLater();
|
||||
sqlite3_close(db);
|
||||
ServerInstance = nullptr;
|
||||
m_lobby->deleteLater();
|
||||
sqlite3_close(db);
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
Room *room = new Room(this);
|
||||
connect(room, &Room::abandoned, this, &Server::onRoomAbandoned);
|
||||
if (room->isLobby())
|
||||
m_lobby = room;
|
||||
else
|
||||
rooms.insert(room->getId(), room);
|
||||
Room *room = new Room(this);
|
||||
connect(room, &Room::abandoned, this, &Server::onRoomAbandoned);
|
||||
if (room->isLobby())
|
||||
m_lobby = room;
|
||||
else
|
||||
rooms.insert(room->getId(), room);
|
||||
|
||||
room->setName(name);
|
||||
room->setCapacity(capacity);
|
||||
room->addPlayer(owner);
|
||||
if (!room->isLobby()) room->setOwner(owner);
|
||||
room->setName(name);
|
||||
room->setCapacity(capacity);
|
||||
room->addPlayer(owner);
|
||||
if (!room->isLobby()) room->setOwner(owner);
|
||||
}
|
||||
|
||||
Room *Server::findRoom(int id) const
|
||||
{
|
||||
return rooms.value(id);
|
||||
return rooms.value(id);
|
||||
}
|
||||
|
||||
Room *Server::lobby() const
|
||||
{
|
||||
return m_lobby;
|
||||
return m_lobby;
|
||||
}
|
||||
|
||||
ServerPlayer *Server::findPlayer(int id) const
|
||||
{
|
||||
return players.value(id);
|
||||
return players.value(id);
|
||||
}
|
||||
|
||||
void Server::removePlayer(int id) {
|
||||
players.remove(id);
|
||||
players.remove(id);
|
||||
}
|
||||
|
||||
void Server::updateRoomList()
|
||||
{
|
||||
QJsonArray arr;
|
||||
foreach (Room *room, rooms) {
|
||||
QJsonArray obj;
|
||||
obj << room->getId(); // roomId
|
||||
obj << room->getName(); // roomName
|
||||
obj << "Role"; // gameMode
|
||||
obj << room->getPlayers().count(); // playerNum
|
||||
obj << room->getCapacity(); // capacity
|
||||
arr << obj;
|
||||
}
|
||||
lobby()->doBroadcastNotify(
|
||||
lobby()->getPlayers(),
|
||||
"UpdateRoomList",
|
||||
QJsonDocument(arr).toJson()
|
||||
);
|
||||
QJsonArray arr;
|
||||
foreach (Room *room, rooms) {
|
||||
QJsonArray obj;
|
||||
obj << room->getId(); // roomId
|
||||
obj << room->getName(); // roomName
|
||||
obj << "Role"; // gameMode
|
||||
obj << room->getPlayers().count(); // playerNum
|
||||
obj << room->getCapacity(); // capacity
|
||||
arr << obj;
|
||||
}
|
||||
lobby()->doBroadcastNotify(
|
||||
lobby()->getPlayers(),
|
||||
"UpdateRoomList",
|
||||
QJsonDocument(arr).toJson()
|
||||
);
|
||||
}
|
||||
|
||||
sqlite3 *Server::getDatabase() {
|
||||
return db;
|
||||
return db;
|
||||
}
|
||||
|
||||
void Server::processNewConnection(ClientSocket* client)
|
||||
{
|
||||
qDebug() << client->peerAddress() << "connected";
|
||||
// version check, file check, ban IP, reconnect, etc
|
||||
qDebug() << client->peerAddress() << "connected";
|
||||
// version check, file check, ban IP, reconnect, etc
|
||||
|
||||
connect(client, &ClientSocket::disconnected, this, [client](){
|
||||
qDebug() << client->peerAddress() << "disconnected";
|
||||
});
|
||||
connect(client, &ClientSocket::disconnected, this, [client](){
|
||||
qDebug() << client->peerAddress() << "disconnected";
|
||||
});
|
||||
|
||||
// network delay test
|
||||
QJsonArray body;
|
||||
body << -2;
|
||||
body << (Router::TYPE_NOTIFICATION | Router::SRC_SERVER | Router::DEST_CLIENT);
|
||||
body << "NetworkDelayTest";
|
||||
body << "[]";
|
||||
client->send(QJsonDocument(body).toJson(QJsonDocument::Compact));
|
||||
// Note: the client should send a setup string next
|
||||
connect(client, &ClientSocket::message_got, this, &Server::processRequest);
|
||||
client->timerSignup.start(30000);
|
||||
// network delay test
|
||||
QJsonArray body;
|
||||
body << -2;
|
||||
body << (Router::TYPE_NOTIFICATION | Router::SRC_SERVER | Router::DEST_CLIENT);
|
||||
body << "NetworkDelayTest";
|
||||
body << "[]";
|
||||
client->send(QJsonDocument(body).toJson(QJsonDocument::Compact));
|
||||
// Note: the client should send a setup string next
|
||||
connect(client, &ClientSocket::message_got, this, &Server::processRequest);
|
||||
client->timerSignup.start(30000);
|
||||
}
|
||||
|
||||
void Server::processRequest(const QByteArray& msg)
|
||||
{
|
||||
ClientSocket *client = qobject_cast<ClientSocket *>(sender());
|
||||
client->disconnect(this, SLOT(processRequest(const QByteArray &)));
|
||||
client->timerSignup.stop();
|
||||
ClientSocket *client = qobject_cast<ClientSocket *>(sender());
|
||||
client->disconnect(this, SLOT(processRequest(const QByteArray &)));
|
||||
client->timerSignup.stop();
|
||||
|
||||
bool valid = true;
|
||||
QJsonDocument doc = QJsonDocument::fromJson(msg);
|
||||
if (doc.isNull() || !doc.isArray()) {
|
||||
valid = false;
|
||||
} else {
|
||||
if (doc.array().size() != 4
|
||||
|| doc[0] != -2
|
||||
|| doc[1] != (Router::TYPE_NOTIFICATION | Router::SRC_CLIENT | Router::DEST_SERVER)
|
||||
|| doc[2] != "Setup"
|
||||
)
|
||||
valid = false;
|
||||
else
|
||||
valid = (QJsonDocument::fromJson(doc[3].toString().toUtf8()).array().size() == 2);
|
||||
}
|
||||
bool valid = true;
|
||||
QJsonDocument doc = QJsonDocument::fromJson(msg);
|
||||
if (doc.isNull() || !doc.isArray()) {
|
||||
valid = false;
|
||||
} else {
|
||||
if (doc.array().size() != 4
|
||||
|| doc[0] != -2
|
||||
|| doc[1] != (Router::TYPE_NOTIFICATION | Router::SRC_CLIENT | Router::DEST_SERVER)
|
||||
|| doc[2] != "Setup"
|
||||
)
|
||||
valid = false;
|
||||
else
|
||||
valid = (QJsonDocument::fromJson(doc[3].toString().toUtf8()).array().size() == 2);
|
||||
}
|
||||
|
||||
if (!valid) {
|
||||
qDebug() << "Invalid setup string:" << msg;
|
||||
QJsonArray body;
|
||||
body << -2;
|
||||
body << (Router::TYPE_NOTIFICATION | Router::SRC_SERVER | Router::DEST_CLIENT);
|
||||
body << "ErrorMsg";
|
||||
body << "INVALID SETUP STRING";
|
||||
client->send(QJsonDocument(body).toJson(QJsonDocument::Compact));
|
||||
client->disconnectFromHost();
|
||||
return;
|
||||
}
|
||||
if (!valid) {
|
||||
qDebug() << "Invalid setup string:" << msg;
|
||||
QJsonArray body;
|
||||
body << -2;
|
||||
body << (Router::TYPE_NOTIFICATION | Router::SRC_SERVER | Router::DEST_CLIENT);
|
||||
body << "ErrorMsg";
|
||||
body << "INVALID SETUP STRING";
|
||||
client->send(QJsonDocument(body).toJson(QJsonDocument::Compact));
|
||||
client->disconnectFromHost();
|
||||
return;
|
||||
}
|
||||
|
||||
QJsonArray arr = QJsonDocument::fromJson(doc[3].toString().toUtf8()).array();
|
||||
handleNameAndPassword(client, arr[0].toString(), arr[1].toString());
|
||||
QJsonArray arr = QJsonDocument::fromJson(doc[3].toString().toUtf8()).array();
|
||||
handleNameAndPassword(client, arr[0].toString(), arr[1].toString());
|
||||
}
|
||||
|
||||
void Server::handleNameAndPassword(ClientSocket *client, const QString& name, const QString& password)
|
||||
{
|
||||
// First check the name and password
|
||||
// Matches a string that does not contain special characters
|
||||
QRegExp nameExp("[^\\0000-\\0057\\0072-\\0100\\0133-\\0140\\0173-\\0177]+");
|
||||
QByteArray passwordHash = QCryptographicHash::hash(password.toLatin1(), QCryptographicHash::Sha256).toHex();
|
||||
bool passed = false;
|
||||
QString error_msg;
|
||||
QJsonObject result;
|
||||
// First check the name and password
|
||||
// Matches a string that does not contain special characters
|
||||
QRegExp nameExp("[^\\0000-\\0057\\0072-\\0100\\0133-\\0140\\0173-\\0177]+");
|
||||
QByteArray passwordHash = QCryptographicHash::hash(password.toLatin1(), QCryptographicHash::Sha256).toHex();
|
||||
bool passed = false;
|
||||
QString error_msg;
|
||||
QJsonObject result;
|
||||
|
||||
if (nameExp.exactMatch(name)) {
|
||||
// Then we check the database,
|
||||
QString sql_find = QString("SELECT * FROM userinfo \
|
||||
WHERE name='%1';").arg(name);
|
||||
result = SelectFromDatabase(db, sql_find);
|
||||
QJsonArray arr = result["password"].toArray();
|
||||
if (arr.isEmpty()) {
|
||||
// not present in database, register
|
||||
QString sql_reg = QString("INSERT INTO userinfo (name,password,\
|
||||
avatar,lastLoginIp,banned) VALUES ('%1','%2','%3','%4',%5);")
|
||||
.arg(name)
|
||||
.arg(QString(passwordHash))
|
||||
.arg("liubei")
|
||||
.arg(client->peerAddress())
|
||||
.arg("FALSE");
|
||||
ExecSQL(db, sql_reg);
|
||||
result = SelectFromDatabase(db, sql_find); // refresh result
|
||||
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";
|
||||
}
|
||||
}
|
||||
if (nameExp.exactMatch(name)) {
|
||||
// Then we check the database,
|
||||
QString sql_find = QString("SELECT * FROM userinfo \
|
||||
WHERE name='%1';").arg(name);
|
||||
result = SelectFromDatabase(db, sql_find);
|
||||
QJsonArray arr = result["password"].toArray();
|
||||
if (arr.isEmpty()) {
|
||||
// not present in database, register
|
||||
QString sql_reg = QString("INSERT INTO userinfo (name,password,\
|
||||
avatar,lastLoginIp,banned) VALUES ('%1','%2','%3','%4',%5);")
|
||||
.arg(name)
|
||||
.arg(QString(passwordHash))
|
||||
.arg("liubei")
|
||||
.arg(client->peerAddress())
|
||||
.arg("FALSE");
|
||||
ExecSQL(db, sql_reg);
|
||||
result = SelectFromDatabase(db, sql_find); // refresh result
|
||||
passed = true;
|
||||
} 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) {
|
||||
// create new ServerPlayer and setup
|
||||
ServerPlayer *player = new ServerPlayer(lobby());
|
||||
player->setSocket(client);
|
||||
client->disconnect(this);
|
||||
connect(player, &ServerPlayer::disconnected, this, &Server::onUserDisconnected);
|
||||
connect(player, &Player::stateChanged, this, &Server::onUserStateChanged);
|
||||
player->setScreenName(name);
|
||||
player->setAvatar(result["avatar"].toArray()[0].toString());
|
||||
player->setId(result["id"].toArray()[0].toString().toInt());
|
||||
players.insert(player->getId(), player);
|
||||
if (passed) {
|
||||
// create new ServerPlayer and setup
|
||||
ServerPlayer *player = new ServerPlayer(lobby());
|
||||
player->setSocket(client);
|
||||
client->disconnect(this);
|
||||
connect(player, &ServerPlayer::disconnected, this, &Server::onUserDisconnected);
|
||||
connect(player, &Player::stateChanged, this, &Server::onUserStateChanged);
|
||||
player->setScreenName(name);
|
||||
player->setAvatar(result["avatar"].toArray()[0].toString());
|
||||
player->setId(result["id"].toArray()[0].toString().toInt());
|
||||
players.insert(player->getId(), player);
|
||||
|
||||
// tell the lobby player's basic property
|
||||
QJsonArray arr;
|
||||
arr << player->getId();
|
||||
arr << player->getScreenName();
|
||||
arr << player->getAvatar();
|
||||
player->doNotify("Setup", QJsonDocument(arr).toJson());
|
||||
// tell the lobby player's basic property
|
||||
QJsonArray arr;
|
||||
arr << player->getId();
|
||||
arr << player->getScreenName();
|
||||
arr << player->getAvatar();
|
||||
player->doNotify("Setup", QJsonDocument(arr).toJson());
|
||||
|
||||
lobby()->addPlayer(player);
|
||||
} else {
|
||||
qDebug() << client->peerAddress() << "lost connection:" << error_msg;
|
||||
QJsonArray body;
|
||||
body << -2;
|
||||
body << (Router::TYPE_NOTIFICATION | Router::SRC_SERVER | Router::DEST_CLIENT);
|
||||
body << "ErrorMsg";
|
||||
body << error_msg;
|
||||
client->send(QJsonDocument(body).toJson(QJsonDocument::Compact));
|
||||
client->disconnectFromHost();
|
||||
return;
|
||||
}
|
||||
lobby()->addPlayer(player);
|
||||
} else {
|
||||
qDebug() << client->peerAddress() << "lost connection:" << error_msg;
|
||||
QJsonArray body;
|
||||
body << -2;
|
||||
body << (Router::TYPE_NOTIFICATION | Router::SRC_SERVER | Router::DEST_CLIENT);
|
||||
body << "ErrorMsg";
|
||||
body << error_msg;
|
||||
client->send(QJsonDocument(body).toJson(QJsonDocument::Compact));
|
||||
client->disconnectFromHost();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void Server::onRoomAbandoned()
|
||||
{
|
||||
Room *room = qobject_cast<Room *>(sender());
|
||||
room->gameOver();
|
||||
rooms.remove(room->getId());
|
||||
updateRoomList();
|
||||
room->deleteLater();
|
||||
Room *room = qobject_cast<Room *>(sender());
|
||||
room->gameOver();
|
||||
rooms.remove(room->getId());
|
||||
updateRoomList();
|
||||
room->deleteLater();
|
||||
}
|
||||
|
||||
void Server::onUserDisconnected()
|
||||
{
|
||||
ServerPlayer *player = qobject_cast<ServerPlayer *>(sender());
|
||||
qDebug() << "Player" << player->getId() << "disconnected";
|
||||
Room *room = player->getRoom();
|
||||
if (room->isStarted()) {
|
||||
player->setState(Player::Offline);
|
||||
// TODO: add a robot
|
||||
} else {
|
||||
player->deleteLater();
|
||||
}
|
||||
ServerPlayer *player = qobject_cast<ServerPlayer *>(sender());
|
||||
qDebug() << "Player" << player->getId() << "disconnected";
|
||||
Room *room = player->getRoom();
|
||||
if (room->isStarted()) {
|
||||
player->setState(Player::Offline);
|
||||
// TODO: add a robot
|
||||
} else {
|
||||
player->deleteLater();
|
||||
}
|
||||
}
|
||||
|
||||
void Server::onUserStateChanged()
|
||||
{
|
||||
ServerPlayer *player = qobject_cast<ServerPlayer *>(sender());
|
||||
QJsonArray arr;
|
||||
arr << player->getId();
|
||||
arr << player->getStateString();
|
||||
player->getRoom()->callLua("PlayerStateChanged", QJsonDocument(arr).toJson());
|
||||
ServerPlayer *player = qobject_cast<ServerPlayer *>(sender());
|
||||
QJsonArray arr;
|
||||
arr << player->getId();
|
||||
arr << player->getStateString();
|
||||
player->getRoom()->callLua("PlayerStateChanged", QJsonDocument(arr).toJson());
|
||||
}
|
||||
|
|
|
@ -8,49 +8,49 @@ class ServerPlayer;
|
|||
#include "room.h"
|
||||
|
||||
class Server : public QObject {
|
||||
Q_OBJECT
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit Server(QObject *parent = nullptr);
|
||||
~Server();
|
||||
explicit Server(QObject *parent = nullptr);
|
||||
~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);
|
||||
Room *findRoom(int id) const;
|
||||
Room *lobby() const;
|
||||
void createRoom(ServerPlayer *owner, const QString &name, int capacity);
|
||||
Room *findRoom(int id) const;
|
||||
Room *lobby() const;
|
||||
|
||||
ServerPlayer *findPlayer(int id) const;
|
||||
void removePlayer(int id);
|
||||
ServerPlayer *findPlayer(int id) const;
|
||||
void removePlayer(int id);
|
||||
|
||||
void updateRoomList();
|
||||
void updateRoomList();
|
||||
|
||||
sqlite3 *getDatabase();
|
||||
sqlite3 *getDatabase();
|
||||
|
||||
signals:
|
||||
void roomCreated(Room *room);
|
||||
void playerAdded(ServerPlayer *player);
|
||||
void playerRemoved(ServerPlayer *player);
|
||||
void roomCreated(Room *room);
|
||||
void playerAdded(ServerPlayer *player);
|
||||
void playerRemoved(ServerPlayer *player);
|
||||
|
||||
public slots:
|
||||
void processNewConnection(ClientSocket *client);
|
||||
void processRequest(const QByteArray &msg);
|
||||
void processNewConnection(ClientSocket *client);
|
||||
void processRequest(const QByteArray &msg);
|
||||
|
||||
void onRoomAbandoned();
|
||||
void onUserDisconnected();
|
||||
void onUserStateChanged();
|
||||
void onRoomAbandoned();
|
||||
void onUserDisconnected();
|
||||
void onUserStateChanged();
|
||||
|
||||
private:
|
||||
ServerSocket *server;
|
||||
Room *m_lobby;
|
||||
QMap<int, Room *> rooms;
|
||||
int nextRoomId;
|
||||
friend Room::Room(Server *server);
|
||||
QHash<int, ServerPlayer *> players;
|
||||
ServerSocket *server;
|
||||
Room *m_lobby;
|
||||
QMap<int, Room *> rooms;
|
||||
int nextRoomId;
|
||||
friend Room::Room(Server *server);
|
||||
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;
|
||||
|
|
|
@ -6,111 +6,111 @@
|
|||
|
||||
ServerPlayer::ServerPlayer(Room *room)
|
||||
{
|
||||
socket = nullptr;
|
||||
router = new Router(this, socket, Router::TYPE_SERVER);
|
||||
setState(Player::Online);
|
||||
this->room = room;
|
||||
server = room->getServer();
|
||||
socket = nullptr;
|
||||
router = new Router(this, socket, Router::TYPE_SERVER);
|
||||
setState(Player::Online);
|
||||
this->room = room;
|
||||
server = room->getServer();
|
||||
}
|
||||
|
||||
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);
|
||||
if (room != nullptr) {
|
||||
// now we are in lobby, so quit lobby
|
||||
room->removePlayer(this);
|
||||
}
|
||||
server->removePlayer(getId());
|
||||
router->deleteLater();
|
||||
}
|
||||
server->removePlayer(getId());
|
||||
router->deleteLater();
|
||||
}
|
||||
|
||||
void ServerPlayer::setSocket(ClientSocket *socket)
|
||||
{
|
||||
if (this->socket != nullptr) {
|
||||
this->socket->disconnect(this);
|
||||
disconnect(this->socket);
|
||||
this->socket->deleteLater();
|
||||
}
|
||||
if (this->socket != nullptr) {
|
||||
this->socket->disconnect(this);
|
||||
disconnect(this->socket);
|
||||
this->socket->deleteLater();
|
||||
}
|
||||
|
||||
this->socket = nullptr;
|
||||
if (socket != nullptr) {
|
||||
connect(socket, &ClientSocket::disconnected, this, &ServerPlayer::disconnected);
|
||||
this->socket = socket;
|
||||
}
|
||||
this->socket = nullptr;
|
||||
if (socket != nullptr) {
|
||||
connect(socket, &ClientSocket::disconnected, this, &ServerPlayer::disconnected);
|
||||
this->socket = socket;
|
||||
}
|
||||
|
||||
router->setSocket(socket);
|
||||
router->setSocket(socket);
|
||||
}
|
||||
|
||||
Server *ServerPlayer::getServer() const
|
||||
{
|
||||
return server;
|
||||
return server;
|
||||
}
|
||||
|
||||
Room *ServerPlayer::getRoom() const
|
||||
{
|
||||
return room;
|
||||
return room;
|
||||
}
|
||||
|
||||
void ServerPlayer::setRoom(Room* room)
|
||||
{
|
||||
this->room = room;
|
||||
this->room = room;
|
||||
}
|
||||
|
||||
void ServerPlayer::speak(const QString& message)
|
||||
{
|
||||
;
|
||||
;
|
||||
}
|
||||
|
||||
void ServerPlayer::doRequest(const QString& command, const QString& jsonData, int timeout)
|
||||
{
|
||||
if (getState() != Player::Online) return;
|
||||
int type = Router::TYPE_REQUEST | Router::SRC_SERVER | Router::DEST_CLIENT;
|
||||
router->request(type, command, jsonData, timeout);
|
||||
if (getState() != Player::Online) return;
|
||||
int type = Router::TYPE_REQUEST | Router::SRC_SERVER | Router::DEST_CLIENT;
|
||||
router->request(type, command, jsonData, timeout);
|
||||
}
|
||||
|
||||
void ServerPlayer::abortRequest()
|
||||
{
|
||||
router->abortRequest();
|
||||
router->abortRequest();
|
||||
}
|
||||
|
||||
QString ServerPlayer::waitForReply()
|
||||
{
|
||||
room->unlockLua(__FUNCTION__);
|
||||
QString ret;
|
||||
if (getState() != Player::Online) {
|
||||
QThread::sleep(1);
|
||||
ret = "";
|
||||
} else {
|
||||
ret = router->waitForReply();
|
||||
}
|
||||
room->lockLua(__FUNCTION__);
|
||||
return ret;
|
||||
room->unlockLua(__FUNCTION__);
|
||||
QString ret;
|
||||
if (getState() != Player::Online) {
|
||||
QThread::sleep(1);
|
||||
ret = "";
|
||||
} else {
|
||||
ret = router->waitForReply();
|
||||
}
|
||||
room->lockLua(__FUNCTION__);
|
||||
return ret;
|
||||
}
|
||||
|
||||
QString ServerPlayer::waitForReply(int timeout)
|
||||
{
|
||||
room->unlockLua(__FUNCTION__);
|
||||
QString ret;
|
||||
if (getState() != Player::Online) {
|
||||
QThread::sleep(1);
|
||||
ret = "";
|
||||
} else {
|
||||
ret = router->waitForReply(timeout);
|
||||
}
|
||||
room->lockLua(__FUNCTION__);
|
||||
return ret;
|
||||
room->unlockLua(__FUNCTION__);
|
||||
QString ret;
|
||||
if (getState() != Player::Online) {
|
||||
QThread::sleep(1);
|
||||
ret = "";
|
||||
} else {
|
||||
ret = router->waitForReply(timeout);
|
||||
}
|
||||
room->lockLua(__FUNCTION__);
|
||||
return ret;
|
||||
}
|
||||
|
||||
void ServerPlayer::doNotify(const QString& command, const QString& jsonData)
|
||||
{
|
||||
if (getState() != Player::Online) return;
|
||||
int type = Router::TYPE_NOTIFICATION | Router::SRC_SERVER | Router::DEST_CLIENT;
|
||||
router->notify(type, command, jsonData);
|
||||
if (getState() != Player::Online) return;
|
||||
int type = Router::TYPE_NOTIFICATION | Router::SRC_SERVER | Router::DEST_CLIENT;
|
||||
router->notify(type, command, jsonData);
|
||||
}
|
||||
|
||||
void ServerPlayer::prepareForRequest(const QString& command, const QString& data)
|
||||
{
|
||||
requestCommand = command;
|
||||
requestData = data;
|
||||
requestCommand = command;
|
||||
requestData = data;
|
||||
}
|
||||
|
|
|
@ -9,40 +9,40 @@ class Server;
|
|||
class Room;
|
||||
|
||||
class ServerPlayer : public Player {
|
||||
Q_OBJECT
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit ServerPlayer(Room *room);
|
||||
~ServerPlayer();
|
||||
explicit ServerPlayer(Room *room);
|
||||
~ServerPlayer();
|
||||
|
||||
void setSocket(ClientSocket *socket);
|
||||
void setSocket(ClientSocket *socket);
|
||||
|
||||
Server *getServer() const;
|
||||
Room *getRoom() const;
|
||||
void setRoom(Room *room);
|
||||
Server *getServer() const;
|
||||
Room *getRoom() const;
|
||||
void setRoom(Room *room);
|
||||
|
||||
void speak(const QString &message);
|
||||
void speak(const QString &message);
|
||||
|
||||
void doRequest(const QString &command,
|
||||
const QString &jsonData, int timeout = -1);
|
||||
void abortRequest();
|
||||
QString waitForReply(int timeout);
|
||||
QString waitForReply();
|
||||
void doNotify(const QString &command, const QString &jsonData);
|
||||
void doRequest(const QString &command,
|
||||
const QString &jsonData, int timeout = -1);
|
||||
void abortRequest();
|
||||
QString waitForReply(int timeout);
|
||||
QString waitForReply();
|
||||
void doNotify(const QString &command, const QString &jsonData);
|
||||
|
||||
void prepareForRequest(const QString &command,
|
||||
const QString &data);
|
||||
void prepareForRequest(const QString &command,
|
||||
const QString &data);
|
||||
|
||||
signals:
|
||||
void disconnected();
|
||||
|
||||
void disconnected();
|
||||
|
||||
private:
|
||||
ClientSocket *socket; // socket for communicating with client
|
||||
Router *router;
|
||||
Server *server;
|
||||
Room *room; // Room that player is in, maybe lobby
|
||||
ClientSocket *socket; // socket for communicating with client
|
||||
Router *router;
|
||||
Server *server;
|
||||
Room *room; // Room that player is in, maybe lobby
|
||||
|
||||
QString requestCommand;
|
||||
QString requestData;
|
||||
QString requestCommand;
|
||||
QString requestData;
|
||||
};
|
||||
|
||||
#endif // _SERVERPLAYER_H
|
||||
|
|
|
@ -2,13 +2,13 @@
|
|||
%nodefaultdtor QmlBackend;
|
||||
class QmlBackend : public QObject {
|
||||
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 QStringList ls(const QString &dir);
|
||||
static QString pwd();
|
||||
static bool exists(const QString &file);
|
||||
static bool isDir(const QString &file);
|
||||
static void cd(const QString &path);
|
||||
static QStringList ls(const QString &dir);
|
||||
static QString pwd();
|
||||
static bool exists(const QString &file);
|
||||
static bool isDir(const QString &file);
|
||||
};
|
||||
|
||||
extern QmlBackend *Backend;
|
||||
|
@ -17,13 +17,13 @@ extern QmlBackend *Backend;
|
|||
%nodefaultdtor Client;
|
||||
class Client : public QObject {
|
||||
public:
|
||||
void replyToServer(const QString &command, const QString &json_data);
|
||||
void notifyServer(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);
|
||||
|
||||
LuaFunction callback;
|
||||
LuaFunction callback;
|
||||
|
||||
ClientPlayer *addPlayer(int id, const QString &name, const QString &avatar);
|
||||
void removePlayer(int id);
|
||||
ClientPlayer *addPlayer(int id, const QString &name, const QString &avatar);
|
||||
void removePlayer(int id);
|
||||
};
|
||||
|
||||
extern Client *ClientInstance;
|
||||
|
@ -31,24 +31,24 @@ extern Client *ClientInstance;
|
|||
%{
|
||||
void Client::callLua(const QString& command, const QString& json_data)
|
||||
{
|
||||
Q_ASSERT(callback);
|
||||
Q_ASSERT(callback);
|
||||
|
||||
lua_getglobal(L, "debug");
|
||||
lua_getfield(L, -1, "traceback");
|
||||
lua_replace(L, -2);
|
||||
lua_getglobal(L, "debug");
|
||||
lua_getfield(L, -1, "traceback");
|
||||
lua_replace(L, -2);
|
||||
|
||||
lua_rawgeti(L, LUA_REGISTRYINDEX, callback);
|
||||
SWIG_NewPointerObj(L, this, SWIGTYPE_p_Client, 0);
|
||||
lua_pushstring(L, command.toUtf8());
|
||||
lua_pushstring(L, json_data.toUtf8());
|
||||
lua_rawgeti(L, LUA_REGISTRYINDEX, callback);
|
||||
SWIG_NewPointerObj(L, this, SWIGTYPE_p_Client, 0);
|
||||
lua_pushstring(L, command.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) {
|
||||
const char *error_msg = lua_tostring(L, -1);
|
||||
qDebug() << error_msg;
|
||||
lua_pop(L, 2);
|
||||
}
|
||||
lua_pop(L, 1);
|
||||
if (error) {
|
||||
const char *error_msg = lua_tostring(L, -1);
|
||||
qDebug() << error_msg;
|
||||
lua_pop(L, 2);
|
||||
}
|
||||
lua_pop(L, 1);
|
||||
}
|
||||
%}
|
||||
|
|
|
@ -7,10 +7,10 @@
|
|||
%typemap(in) LuaFunction
|
||||
%{
|
||||
if (lua_isfunction(L, $input)) {
|
||||
lua_pushvalue(L, $input);
|
||||
$1 = luaL_ref(L, LUA_REGISTRYINDEX);
|
||||
lua_pushvalue(L, $input);
|
||||
$1 = luaL_ref(L, LUA_REGISTRYINDEX);
|
||||
} else {
|
||||
$1 = 0;
|
||||
$1 = 0;
|
||||
}
|
||||
%}
|
||||
|
||||
|
@ -35,8 +35,8 @@ SWIG_arg ++;
|
|||
|
||||
%typemap(in, checkfn = "lua_isstring") QString const &
|
||||
%{
|
||||
$1_str = QString::fromUtf8(lua_tostring(L, $input));
|
||||
$1 = &$1_str;
|
||||
$1_str = QString::fromUtf8(lua_tostring(L, $input));
|
||||
$1 = &$1_str;
|
||||
%}
|
||||
|
||||
%typemap(out) QString const &
|
||||
|
@ -48,10 +48,10 @@ SWIG_arg ++;
|
|||
%typemap(in, checkfn = "lua_istable") QStringList
|
||||
%{
|
||||
for (size_t i = 0; i < lua_rawlen(L, $input); ++i) {
|
||||
lua_rawgeti(L, $input, i + 1);
|
||||
const char *elem = luaL_checkstring(L, -1);
|
||||
$1 << QString::fromUtf8(QByteArray(elem));
|
||||
lua_pop(L, 1);
|
||||
lua_rawgeti(L, $input, i + 1);
|
||||
const char *elem = luaL_checkstring(L, -1);
|
||||
$1 << QString::fromUtf8(QByteArray(elem));
|
||||
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);
|
||||
|
||||
for (int i = 0; i < $1.length(); i++) {
|
||||
QString str = $1.at(i);
|
||||
lua_pushstring(L, str.toUtf8().constData());
|
||||
lua_rawseti(L, -2, i + 1);
|
||||
QString str = $1.at(i);
|
||||
lua_pushstring(L, str.toUtf8().constData());
|
||||
lua_rawseti(L, -2, i + 1);
|
||||
}
|
||||
|
||||
SWIG_arg++;
|
||||
|
@ -70,7 +70,7 @@ SWIG_arg++;
|
|||
|
||||
%typemap(typecheck) QStringList
|
||||
%{
|
||||
$1 = lua_istable(L, $input) ? 1 : 0;
|
||||
$1 = lua_istable(L, $input) ? 1 : 0;
|
||||
%}
|
||||
|
||||
|
||||
|
|
|
@ -2,29 +2,29 @@
|
|||
%nodefaultdtor Player;
|
||||
class Player : public QObject {
|
||||
public:
|
||||
enum State{
|
||||
Invalid,
|
||||
Online,
|
||||
Trust,
|
||||
Offline
|
||||
};
|
||||
enum State{
|
||||
Invalid,
|
||||
Online,
|
||||
Trust,
|
||||
Offline
|
||||
};
|
||||
|
||||
int getId() const;
|
||||
void setId(int id);
|
||||
int getId() const;
|
||||
void setId(int id);
|
||||
|
||||
QString getScreenName() const;
|
||||
void setScreenName(const QString &name);
|
||||
QString getScreenName() const;
|
||||
void setScreenName(const QString &name);
|
||||
|
||||
QString getAvatar() const;
|
||||
void setAvatar(const QString &avatar);
|
||||
QString getAvatar() const;
|
||||
void setAvatar(const QString &avatar);
|
||||
|
||||
State getState() const;
|
||||
QString getStateString() const;
|
||||
void setState(State state);
|
||||
void setStateString(const QString &state);
|
||||
State getState() const;
|
||||
QString getStateString() const;
|
||||
void setState(State state);
|
||||
void setStateString(const QString &state);
|
||||
|
||||
bool isReady() const;
|
||||
void setReady(bool ready);
|
||||
bool isReady() const;
|
||||
void setReady(bool ready);
|
||||
};
|
||||
|
||||
%nodefaultctor ClientPlayer;
|
||||
|
@ -39,17 +39,17 @@ extern ClientPlayer *Self;
|
|||
%nodefaultdtor ServerPlayer;
|
||||
class ServerPlayer : public Player {
|
||||
public:
|
||||
Server *getServer() const;
|
||||
Room *getRoom() const;
|
||||
void setRoom(Room *room);
|
||||
Server *getServer() const;
|
||||
Room *getRoom() const;
|
||||
void setRoom(Room *room);
|
||||
|
||||
void speak(const QString &message);
|
||||
void speak(const QString &message);
|
||||
|
||||
void doRequest(const QString &command,
|
||||
const QString &json_data, int timeout);
|
||||
QString waitForReply();
|
||||
QString waitForReply(int timeout);
|
||||
void doNotify(const QString &command, const QString &json_data);
|
||||
void doRequest(const QString &command,
|
||||
const QString &json_data, int timeout);
|
||||
QString waitForReply();
|
||||
QString waitForReply(int timeout);
|
||||
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>
|
||||
class QList {
|
||||
public:
|
||||
QList();
|
||||
~QList();
|
||||
int length() const;
|
||||
void append(const T &elem);
|
||||
void prepend(const T &elem);
|
||||
bool isEmpty() const;
|
||||
bool contains(const T &value) const;
|
||||
T first() const;
|
||||
T last() const;
|
||||
void removeAt(int i);
|
||||
int removeAll(const T &value);
|
||||
bool removeOne(const T &value);
|
||||
QList<T> mid(int pos, int length = -1) const;
|
||||
int indexOf(const T &value, int from = 0);
|
||||
void replace(int i, const T &value);
|
||||
void swapItemsAt(int i, int j);
|
||||
QList();
|
||||
~QList();
|
||||
int length() const;
|
||||
void append(const T &elem);
|
||||
void prepend(const T &elem);
|
||||
bool isEmpty() const;
|
||||
bool contains(const T &value) const;
|
||||
T first() const;
|
||||
T last() const;
|
||||
void removeAt(int i);
|
||||
int removeAll(const T &value);
|
||||
bool removeOne(const T &value);
|
||||
QList<T> mid(int pos, int length = -1) const;
|
||||
int indexOf(const T &value, int from = 0);
|
||||
void replace(int i, const T &value);
|
||||
void swapItemsAt(int i, int j);
|
||||
};
|
||||
|
||||
%extend QList {
|
||||
T at(int i) const
|
||||
{
|
||||
return $self->value(i);
|
||||
}
|
||||
T at(int i) const
|
||||
{
|
||||
return $self->value(i);
|
||||
}
|
||||
}
|
||||
|
||||
%template(SPlayerList) QList<ServerPlayer *>;
|
||||
|
@ -39,10 +39,10 @@ public:
|
|||
%{
|
||||
#include <sys/time.h>
|
||||
static int GetMicroSecond(lua_State *L) {
|
||||
struct timeval tv;
|
||||
gettimeofday(&tv, nullptr);
|
||||
long microsecond = tv.tv_sec * 1000000 + tv.tv_usec;
|
||||
lua_pushnumber(L, microsecond);
|
||||
return 1;
|
||||
}
|
||||
struct timeval tv;
|
||||
gettimeofday(&tv, nullptr);
|
||||
long microsecond = tv.tv_sec * 1000000 + tv.tv_usec;
|
||||
lua_pushnumber(L, microsecond);
|
||||
return 1;
|
||||
}
|
||||
%}
|
||||
|
|
|
@ -2,12 +2,12 @@
|
|||
%nodefaultdtor Server;
|
||||
class Server : public QObject {
|
||||
public:
|
||||
Room *lobby() const;
|
||||
void createRoom(ServerPlayer *owner, const QString &name, int capacity);
|
||||
Room *findRoom(int id) const;
|
||||
ServerPlayer *findPlayer(int id) const;
|
||||
Room *lobby() const;
|
||||
void createRoom(ServerPlayer *owner, const QString &name, int capacity);
|
||||
Room *findRoom(int id) const;
|
||||
ServerPlayer *findPlayer(int id) const;
|
||||
|
||||
sqlite3 *getDatabase();
|
||||
sqlite3 *getDatabase();
|
||||
};
|
||||
|
||||
extern Server *ServerInstance;
|
||||
|
@ -16,104 +16,104 @@ extern Server *ServerInstance;
|
|||
%nodefaultdtor Room;
|
||||
class Room : public QThread {
|
||||
public:
|
||||
// Property reader & setter
|
||||
// ==================================={
|
||||
Server *getServer() const;
|
||||
int getId() const;
|
||||
bool isLobby() const;
|
||||
QString getName() const;
|
||||
void setName(const QString &name);
|
||||
int getCapacity() const;
|
||||
void setCapacity(int capacity);
|
||||
bool isFull() const;
|
||||
bool isAbandoned() const;
|
||||
// Property reader & setter
|
||||
// ==================================={
|
||||
Server *getServer() const;
|
||||
int getId() const;
|
||||
bool isLobby() const;
|
||||
QString getName() const;
|
||||
void setName(const QString &name);
|
||||
int getCapacity() const;
|
||||
void setCapacity(int capacity);
|
||||
bool isFull() const;
|
||||
bool isAbandoned() const;
|
||||
|
||||
ServerPlayer *getOwner() const;
|
||||
void setOwner(ServerPlayer *owner);
|
||||
ServerPlayer *getOwner() const;
|
||||
void setOwner(ServerPlayer *owner);
|
||||
|
||||
void addPlayer(ServerPlayer *player);
|
||||
void addRobot(ServerPlayer *player);
|
||||
void removePlayer(ServerPlayer *player);
|
||||
QList<ServerPlayer *> getPlayers() const;
|
||||
ServerPlayer *findPlayer(int id) const;
|
||||
void addPlayer(ServerPlayer *player);
|
||||
void addRobot(ServerPlayer *player);
|
||||
void removePlayer(ServerPlayer *player);
|
||||
QList<ServerPlayer *> getPlayers() 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 doNotify(const QList<ServerPlayer *> targets, int timeout);
|
||||
void doBroadcastNotify(
|
||||
const QList<ServerPlayer *> targets,
|
||||
const QString &command,
|
||||
const QString &jsonData
|
||||
);
|
||||
|
||||
void gameOver();
|
||||
void doRequest(const QList<ServerPlayer *> targets, int timeout);
|
||||
void doNotify(const QList<ServerPlayer *> targets, int timeout);
|
||||
void doBroadcastNotify(
|
||||
const QList<ServerPlayer *> targets,
|
||||
const QString &command,
|
||||
const QString &jsonData
|
||||
);
|
||||
|
||||
void gameOver();
|
||||
|
||||
LuaFunction callback;
|
||||
LuaFunction startGame;
|
||||
LuaFunction callback;
|
||||
LuaFunction startGame;
|
||||
};
|
||||
|
||||
%{
|
||||
void Room::initLua()
|
||||
{
|
||||
lua_getglobal(L, "debug");
|
||||
lua_getfield(L, -1, "traceback");
|
||||
lua_replace(L, -2);
|
||||
lua_getglobal(L, "CreateRoom");
|
||||
SWIG_NewPointerObj(L, this, SWIGTYPE_p_Room, 0);
|
||||
int error = lua_pcall(L, 1, 0, -2);
|
||||
lua_pop(L, 1);
|
||||
if (error) {
|
||||
const char *error_msg = lua_tostring(L, -1);
|
||||
qDebug() << error_msg;
|
||||
}
|
||||
lua_getglobal(L, "debug");
|
||||
lua_getfield(L, -1, "traceback");
|
||||
lua_replace(L, -2);
|
||||
lua_getglobal(L, "CreateRoom");
|
||||
SWIG_NewPointerObj(L, this, SWIGTYPE_p_Room, 0);
|
||||
int error = lua_pcall(L, 1, 0, -2);
|
||||
lua_pop(L, 1);
|
||||
if (error) {
|
||||
const char *error_msg = lua_tostring(L, -1);
|
||||
qDebug() << error_msg;
|
||||
}
|
||||
}
|
||||
|
||||
void Room::callLua(const QString& command, const QString& json_data)
|
||||
{
|
||||
Q_ASSERT(callback);
|
||||
Q_ASSERT(callback);
|
||||
|
||||
lua_getglobal(L, "debug");
|
||||
lua_getfield(L, -1, "traceback");
|
||||
lua_replace(L, -2);
|
||||
lua_getglobal(L, "debug");
|
||||
lua_getfield(L, -1, "traceback");
|
||||
lua_replace(L, -2);
|
||||
|
||||
lua_rawgeti(L, LUA_REGISTRYINDEX, callback);
|
||||
SWIG_NewPointerObj(L, this, SWIGTYPE_p_Room, 0);
|
||||
lua_pushstring(L, command.toUtf8());
|
||||
lua_pushstring(L, json_data.toUtf8());
|
||||
lua_rawgeti(L, LUA_REGISTRYINDEX, callback);
|
||||
SWIG_NewPointerObj(L, this, SWIGTYPE_p_Room, 0);
|
||||
lua_pushstring(L, command.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) {
|
||||
const char *error_msg = lua_tostring(L, -1);
|
||||
qDebug() << error_msg;
|
||||
lua_pop(L, 2);
|
||||
}
|
||||
lua_pop(L, 1);
|
||||
if (error) {
|
||||
const char *error_msg = lua_tostring(L, -1);
|
||||
qDebug() << error_msg;
|
||||
lua_pop(L, 2);
|
||||
}
|
||||
lua_pop(L, 1);
|
||||
}
|
||||
|
||||
void Room::roomStart() {
|
||||
Q_ASSERT(startGame);
|
||||
Q_ASSERT(startGame);
|
||||
|
||||
lua_getglobal(L, "debug");
|
||||
lua_getfield(L, -1, "traceback");
|
||||
lua_replace(L, -2);
|
||||
lua_getglobal(L, "debug");
|
||||
lua_getfield(L, -1, "traceback");
|
||||
lua_replace(L, -2);
|
||||
|
||||
lua_rawgeti(L, LUA_REGISTRYINDEX, startGame);
|
||||
SWIG_NewPointerObj(L, this, SWIGTYPE_p_Room, 0);
|
||||
lua_rawgeti(L, LUA_REGISTRYINDEX, startGame);
|
||||
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) {
|
||||
const char *error_msg = lua_tostring(L, -1);
|
||||
qDebug() << error_msg;
|
||||
lua_pop(L, 2);
|
||||
}
|
||||
lua_pop(L, 1);
|
||||
if (error) {
|
||||
const char *error_msg = lua_tostring(L, -1);
|
||||
qDebug() << error_msg;
|
||||
lua_pop(L, 2);
|
||||
}
|
||||
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