Role & Seat (#6)
* standard generals * assignrole * arrangeSeat * fix bugs
This commit is contained in:
parent
3e4080f2ad
commit
58ea0ca80a
|
@ -1,4 +1,7 @@
|
|||
local Client = class('Client')
|
||||
Client = class('Client')
|
||||
|
||||
-- load client classes
|
||||
ClientPlayer = require "client/clientplayer"
|
||||
|
||||
freekill.client_callback = {}
|
||||
|
||||
|
@ -15,6 +18,8 @@ function Client:initialize()
|
|||
self:notifyUI(command, jsonData);
|
||||
end
|
||||
end
|
||||
|
||||
self.players = {} -- ClientPlayer[]
|
||||
end
|
||||
|
||||
freekill.client_callback["Setup"] = function(jsonData)
|
||||
|
@ -32,7 +37,42 @@ freekill.client_callback["AddPlayer"] = function(jsonData)
|
|||
-- 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]
|
||||
ClientInstance:notifyUI("AddPlayer", json.encode({ name, avatar }))
|
||||
local player = freekill.ClientInstance:addPlayer(id, name, avatar)
|
||||
table.insert(ClientInstance.players, ClientPlayer:new(player))
|
||||
ClientInstance:notifyUI("AddPlayer", jsonData)
|
||||
end
|
||||
|
||||
freekill.client_callback["RemovePlayer"] = function(jsonData)
|
||||
-- jsonData: [ int id ]
|
||||
local data = json.decode(jsonData)
|
||||
local id = data[1]
|
||||
freekill.ClientInstance:removePlayer(id)
|
||||
for _, p in ipairs(ClientInstance.players) do
|
||||
if p.player:getId() == id then
|
||||
table.removeOne(ClientInstance.players, p)
|
||||
break
|
||||
end
|
||||
end
|
||||
ClientInstance:notifyUI("RemovePlayer", jsonData)
|
||||
end
|
||||
|
||||
freekill.client_callback["ArrangeSeats"] = function(jsonData)
|
||||
local data = json.decode(jsonData)
|
||||
local n = #ClientInstance.players
|
||||
local players = {}
|
||||
local function findPlayer(id)
|
||||
for _, p in ipairs(ClientInstance.players) do
|
||||
if p.player:getId() == id then return p end
|
||||
end
|
||||
return nil
|
||||
end
|
||||
|
||||
for i = 1, n do
|
||||
table.insert(players, findPlayer(data[i]))
|
||||
end
|
||||
ClientInstance.players = players
|
||||
|
||||
ClientInstance:notifyUI("ArrangeSeats", jsonData)
|
||||
end
|
||||
|
||||
-- Create ClientInstance (used by Lua)
|
||||
|
|
|
@ -1 +1,7 @@
|
|||
local ClientPlayer = Player:subclass("ClientPlayer")
|
||||
|
||||
function ClientPlayer:initialize(cp)
|
||||
self.player = cp
|
||||
end
|
||||
|
||||
return ClientPlayer
|
||||
|
|
|
@ -1,24 +1,103 @@
|
|||
local Sanguosha = class("Engine")
|
||||
local Engine = class("Engine")
|
||||
|
||||
function Sanguosha:initialize()
|
||||
self.skills = {}
|
||||
self.generals = {}
|
||||
self.cards = {}
|
||||
function Engine:initialize()
|
||||
-- Engine should be singleton
|
||||
if Fk ~= nil then
|
||||
error("Engine has been initialized")
|
||||
return
|
||||
end
|
||||
|
||||
Fk = self
|
||||
|
||||
self.packages = {} -- name --> Package
|
||||
self.skills = {} -- name --> Skill
|
||||
self.related_skills = {} -- skillName --> relatedName
|
||||
self.generals = {} -- name --> General
|
||||
self.lords = {} -- lordName[]
|
||||
self.cards = {} -- Card[]
|
||||
self.translations = {} -- srcText --> translated
|
||||
|
||||
self:loadPackages()
|
||||
end
|
||||
|
||||
function Sanguosha:addSkill(skill)
|
||||
table.insert(self.skills, skill)
|
||||
-- Package pack
|
||||
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
|
||||
|
||||
-- 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 Sanguosha:addGeneral(general)
|
||||
table.insert(self.generals, general)
|
||||
function Engine:loadPackages()
|
||||
assert(FileIO.isDir("packages"))
|
||||
FileIO.cd("packages")
|
||||
for _, dir in ipairs(FileIO.ls()) do
|
||||
if FileIO.isDir(dir) then
|
||||
self:loadPackage(require(dir))
|
||||
end
|
||||
end
|
||||
FileIO.cd("..")
|
||||
end
|
||||
|
||||
function Sanguosha:addCard(card)
|
||||
table.insert(self.cards, cards)
|
||||
function Engine:loadTranslationTable(t)
|
||||
assert(type(t) == "table")
|
||||
for k, v in pairs(t) do
|
||||
self.translations[k] = v
|
||||
end
|
||||
end
|
||||
|
||||
function Sanguosha:getGeneralsRandomly(num, generalPool, except, filter)
|
||||
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
|
||||
end
|
||||
|
||||
function Engine:addSkills(skills)
|
||||
assert(type(skills) == "table")
|
||||
for _, skill in ipairs(skills) do
|
||||
self:addSkill(skill)
|
||||
end
|
||||
end
|
||||
|
||||
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
|
||||
end
|
||||
|
||||
function Engine:addGenerals(generals)
|
||||
assert(type(generals) == "table")
|
||||
for _, general in ipairs(generals) do
|
||||
self:addGeneral(general)
|
||||
end
|
||||
end
|
||||
|
||||
function Engine:addCard(card)
|
||||
assert(card.class:isSubclassOf(Card))
|
||||
table.insert(self.cards, card)
|
||||
end
|
||||
|
||||
function Engine:addCards(cards)
|
||||
assert(type(cards) == "table")
|
||||
for _, card in ipairs(cards) do
|
||||
self:addCard(card)
|
||||
end
|
||||
end
|
||||
|
||||
function Engine:getGeneralsRandomly(num, generalPool, except, filter)
|
||||
if filter then
|
||||
assert(type(filter) == "function")
|
||||
end
|
||||
|
@ -51,7 +130,7 @@ function Sanguosha:getGeneralsRandomly(num, generalPool, except, filter)
|
|||
return result
|
||||
end
|
||||
|
||||
function Sanguosha:getAllGenerals(except)
|
||||
function Engine:getAllGenerals(except)
|
||||
local result = {}
|
||||
for _, general in ipairs(self.generals) do
|
||||
if not (except and table.contains(except, general)) then
|
||||
|
@ -62,4 +141,4 @@ function Sanguosha:getAllGenerals(except)
|
|||
return result
|
||||
end
|
||||
|
||||
return Sanguosha
|
||||
return Engine
|
||||
|
|
|
@ -1,19 +1,29 @@
|
|||
General = class("General")
|
||||
|
||||
-- enum Gender
|
||||
General.Male = 0
|
||||
General.Female = 1
|
||||
|
||||
function General:initialize(package, name, kingdom, hp, maxHp, gender, initialHp)
|
||||
self.package = package
|
||||
self.name = name
|
||||
self.kingdom = kingdom
|
||||
self.hp = hp
|
||||
self.maxHp = maxHp
|
||||
self.gender = gender
|
||||
self.maxHp = maxHp or hp
|
||||
self.gender = gender or General.Male
|
||||
self.initialHp = initialHp or maxHp
|
||||
|
||||
self.skills = {}
|
||||
self.skills = {} -- Skill[]
|
||||
-- skill belongs other general, e.g. "mashu" of pangde
|
||||
self.other_skills = {} -- string[]
|
||||
end
|
||||
|
||||
function General:addSkill(skill)
|
||||
table.insert(self.skills, 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
|
||||
end
|
||||
|
||||
return General
|
||||
|
|
|
@ -0,0 +1,40 @@
|
|||
local Package = class("Package")
|
||||
|
||||
-- enum Type
|
||||
Package.GeneralPack = 0
|
||||
Package.CardPack = 1
|
||||
Package.SpecialPack = 2
|
||||
|
||||
-- string name, Type type
|
||||
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
|
||||
|
||||
self.generals = {}
|
||||
-- skill not belongs to any generals, like "jixi"
|
||||
self.extra_skills = {}
|
||||
-- table: string --> string
|
||||
self.related_skills = {}
|
||||
self.cards = {} --> Card[]
|
||||
end
|
||||
|
||||
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
|
||||
end
|
||||
return ret
|
||||
end
|
||||
|
||||
function Package:addGeneral(general)
|
||||
assert(general.class and general:isInstanceOf(General))
|
||||
table.insert(self.generals, general)
|
||||
end
|
||||
|
||||
return Package
|
|
@ -1,11 +1,19 @@
|
|||
local Player = class("Player")
|
||||
|
||||
function Player:initialize()
|
||||
self.hp = nil
|
||||
self.maxHp = nil
|
||||
self.general = nil
|
||||
self.hp = 0
|
||||
self.maxHp = 0
|
||||
self.kingdom = "qun"
|
||||
self.role = ""
|
||||
self.general = ""
|
||||
self.handcard_num = 0
|
||||
self.seat = 0
|
||||
self.phase = Player.PhaseNone
|
||||
self.faceup = true
|
||||
self.chained = false
|
||||
self.dying = false
|
||||
self.dead = false
|
||||
|
||||
self.playerSkills = {}
|
||||
end
|
||||
|
||||
|
|
|
@ -16,6 +16,13 @@ function table:contains(element)
|
|||
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
|
||||
end
|
||||
|
||||
function table:insertTable(list)
|
||||
for _, e in ipairs(list) do
|
||||
table.insert(self, e)
|
||||
|
@ -37,6 +44,41 @@ Sql = {
|
|||
end,
|
||||
}
|
||||
|
||||
FileIO = {
|
||||
pwd = freekill.QmlBackend_pwd,
|
||||
ls = function(filename)
|
||||
if filename == nil then
|
||||
return freekill.QmlBackend_ls(".")
|
||||
else
|
||||
return freekill.QmlBackend_ls(filename)
|
||||
end
|
||||
end,
|
||||
cd = freekill.QmlBackend_cd,
|
||||
exists = freekill.QmlBackend_exists,
|
||||
isDir = freekill.QmlBackend_isDir
|
||||
}
|
||||
|
||||
Stack = class("Stack")
|
||||
function Stack:initialize()
|
||||
self.t = {}
|
||||
self.p = 0
|
||||
end
|
||||
|
||||
function Stack:push(e)
|
||||
self.p = self.p + 1
|
||||
self.t[self.p] = e
|
||||
end
|
||||
|
||||
function Stack:isEmpty()
|
||||
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]
|
||||
end
|
||||
|
||||
function table:removeOne(element)
|
||||
if #self == 0 or type(self[1]) ~= type(element) then return false end
|
||||
|
||||
|
|
|
@ -2,13 +2,14 @@
|
|||
-- Load mods, init the engine, etc.
|
||||
|
||||
package.path = package.path .. ";./lua/lib/?.lua"
|
||||
.. ";./lua/core/?.lua"
|
||||
.. ";./lua/?.lua"
|
||||
|
||||
-- load libraries
|
||||
class = require "middleclass"
|
||||
json = require "json"
|
||||
require "sha256"
|
||||
Util = require "util"
|
||||
Util = require "core/util"
|
||||
math.randomseed(os.time())
|
||||
|
||||
DebugMode = true
|
||||
|
||||
|
@ -19,10 +20,12 @@ function pt(t)
|
|||
end
|
||||
|
||||
-- load core classes
|
||||
Sanguosha = require "engine"
|
||||
General = require "general"
|
||||
Card = require "card"
|
||||
Skill = require "skill"
|
||||
Player = require "player"
|
||||
Engine = require "core/engine"
|
||||
Package = require "core/package"
|
||||
General = require "core/general"
|
||||
Card = require "core/card"
|
||||
Skill = require "core/skill"
|
||||
Player = require "core/player"
|
||||
|
||||
-- load packages
|
||||
Fk = Engine:new()
|
||||
|
|
|
@ -1,23 +1,55 @@
|
|||
function logic()
|
||||
chooseGeneral()
|
||||
initSkillList()
|
||||
actionNormal()
|
||||
local GameLogic = class("GameLogic")
|
||||
|
||||
function GameLogic:initialize(room)
|
||||
self.room = room
|
||||
self.skill_table = {} -- TriggerEvent --> Skill[]
|
||||
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" },
|
||||
}
|
||||
end
|
||||
|
||||
function chooseGeneral()
|
||||
for _, p in ipairs(room:getPlayers()) do
|
||||
local g = p:askForGeneral()
|
||||
room:changeHero(p, g)
|
||||
function GameLogic:run()
|
||||
-- default logic
|
||||
table.shuffle(self.room.players)
|
||||
self:assignRoles()
|
||||
self.room:adjustSeats()
|
||||
|
||||
self:chooseGenerals()
|
||||
self:startGame()
|
||||
end
|
||||
|
||||
function GameLogic:assignRoles()
|
||||
local n = #self.room.players
|
||||
local roles = self.role_table[n]
|
||||
table.shuffle(roles)
|
||||
|
||||
for i = 1, n do
|
||||
local p = self.room.players[i]
|
||||
p.role = roles[i]
|
||||
if p.role == "lord" then
|
||||
self.room:broadcastProperty(p, "role")
|
||||
else
|
||||
self.room:notifyProperty(p, p, "role")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function actionNormal()
|
||||
local p = room:getLord()
|
||||
while true do
|
||||
room:setCurrent(p)
|
||||
act(room:getCurrent)
|
||||
p = p:getNextAlive()
|
||||
end
|
||||
function GameLogic:chooseGenerals()
|
||||
|
||||
end
|
||||
|
||||
function trigger() end
|
||||
function GameLogic:startGame()
|
||||
|
||||
end
|
||||
|
||||
return GameLogic
|
||||
|
|
|
@ -2,29 +2,68 @@ local Room = class("Room")
|
|||
|
||||
function Room:initialize(_room)
|
||||
self.room = _room
|
||||
self.players = {}
|
||||
self.players = {} -- ServerPlayer[]
|
||||
self.gameFinished = false
|
||||
end
|
||||
|
||||
-- When this function returns, the Room(C++) thread stopped.
|
||||
function Room:run()
|
||||
print 'Room is running!'
|
||||
-- First, create players(Lua) from ServerPlayer(C++)
|
||||
for _, p in freekill.qlist(self.room:getPlayers()) do
|
||||
local player = ServerPlayer:new(p)
|
||||
print(player:getId())
|
||||
table.insert(self.players, p)
|
||||
table.insert(self.players, player)
|
||||
self.server.players[player:getId()] = player
|
||||
end
|
||||
-- Second, assign role and adjust seats
|
||||
-- Then let's choose general and start the game!
|
||||
|
||||
self.logic = GameLogic:new(self)
|
||||
self.logic:run()
|
||||
end
|
||||
|
||||
function Room:startGame()
|
||||
while true do
|
||||
if self.gameFinished then break end
|
||||
function Room:broadcastProperty(player, property)
|
||||
for _, p in ipairs(self.players) do
|
||||
self:notifyProperty(p, player, property)
|
||||
end
|
||||
end
|
||||
|
||||
function Room:notifyProperty(p, player, property)
|
||||
p:doNotify("PropertyUpdate", json.encode{
|
||||
player:getId(),
|
||||
property,
|
||||
player[property],
|
||||
})
|
||||
end
|
||||
|
||||
function Room:doBroadcastNotify(command, jsonData)
|
||||
self.room:doBroadcastNotify(self.room:getPlayers(), command, jsonData)
|
||||
end
|
||||
|
||||
function Room:adjustSeats()
|
||||
local players = {}
|
||||
local p = 0
|
||||
|
||||
for i = 1, #self.players do
|
||||
if self.players[i].role == "lord" then
|
||||
p = i
|
||||
break
|
||||
end
|
||||
end
|
||||
for j = p, #self.players do
|
||||
table.insert(players, self.players[j])
|
||||
end
|
||||
for j = 1, p - 1 do
|
||||
table.insert(players, self.players[j])
|
||||
end
|
||||
|
||||
self.players = players
|
||||
|
||||
local player_circle = {}
|
||||
for i = 1, #self.players do
|
||||
self.players[i].seat = i
|
||||
table.insert(player_circle, self.players[i]:getId())
|
||||
end
|
||||
|
||||
self:doBroadcastNotify("ArrangeSeats", json.encode(player_circle))
|
||||
end
|
||||
|
||||
function Room:gameOver()
|
||||
self.gameFinished = true
|
||||
-- dosomething
|
||||
|
|
|
@ -1,7 +1,9 @@
|
|||
Server = class('Server')
|
||||
package.path = package.path .. ';./lua/server/?.lua'
|
||||
Room = require "room"
|
||||
ServerPlayer = require "serverplayer"
|
||||
|
||||
-- load server classes
|
||||
Room = require "server/room"
|
||||
GameLogic = require "server/gamelogic"
|
||||
ServerPlayer = require "server/serverplayer"
|
||||
|
||||
freekill.server_callback = {}
|
||||
|
||||
|
@ -30,8 +32,8 @@ function Server:initialize()
|
|||
table.removeOne(self.rooms, room)
|
||||
end
|
||||
|
||||
self.rooms = {}
|
||||
self.players = {}
|
||||
self.rooms = {} -- id --> Room(Started)
|
||||
self.players = {} -- id --> ServerPlayer
|
||||
end
|
||||
|
||||
freekill.server_callback["UpdateAvatar"] = function(jsonData)
|
||||
|
@ -41,7 +43,8 @@ freekill.server_callback["UpdateAvatar"] = function(jsonData)
|
|||
local sql = "UPDATE userinfo SET avatar='%s' WHERE id=%d;"
|
||||
Sql.exec(ServerInstance.db, string.format(sql, avatar, id))
|
||||
local player = freekill.ServerInstance:findPlayer(id)
|
||||
player:doNotify("UpdateAvatar", "[]")
|
||||
player:setAvatar(avatar)
|
||||
player:doNotify("UpdateAvatar", avatar)
|
||||
end
|
||||
|
||||
freekill.server_callback["UpdatePassword"] = function(jsonData)
|
||||
|
|
|
@ -9,4 +9,12 @@ function ServerPlayer:getId()
|
|||
return self.serverplayer:getId()
|
||||
end
|
||||
|
||||
function ServerPlayer:doNotify(command, jsonData)
|
||||
self.serverplayer:doNotify(command, jsonData)
|
||||
end
|
||||
|
||||
function ServerPlayer:doRequest(command, jsonData, timeout)
|
||||
self.serverplayer:doRequest(command, jsonData, timeout)
|
||||
end
|
||||
|
||||
return ServerPlayer
|
||||
|
|
|
@ -1 +1,161 @@
|
|||
|
||||
local extension = Package:new("standard")
|
||||
extension.metadata = require "standard/metadata"
|
||||
|
||||
Fk:loadTranslationTable{
|
||||
["wei"] = "魏",
|
||||
["shu"] = "蜀",
|
||||
["wu"] = "吴",
|
||||
["qun"] = "群",
|
||||
}
|
||||
|
||||
local caocao = General:new(extension, "caocao", "wei", 4)
|
||||
extension:addGeneral(caocao)
|
||||
Fk:loadTranslationTable{
|
||||
["caocao"] = "曹操",
|
||||
}
|
||||
|
||||
local simayi = General:new(extension, "simayi", "wei", 3)
|
||||
extension:addGeneral(simayi)
|
||||
Fk:loadTranslationTable{
|
||||
["simayi"] = "司马懿",
|
||||
}
|
||||
|
||||
local xiahoudun = General:new(extension, "xiahoudun", "wei", 4)
|
||||
extension:addGeneral(xiahoudun)
|
||||
Fk:loadTranslationTable{
|
||||
["xiahoudun"] = "夏侯惇",
|
||||
}
|
||||
|
||||
local zhangliao = General:new(extension, "zhangliao", "wei", 4)
|
||||
extension:addGeneral(zhangliao)
|
||||
Fk:loadTranslationTable{
|
||||
["zhangliao"] = "张辽",
|
||||
}
|
||||
|
||||
local xuchu = General:new(extension, "xuchu", "wei", 4)
|
||||
extension:addGeneral(xuchu)
|
||||
Fk:loadTranslationTable{
|
||||
["xuchu"] = "许褚",
|
||||
}
|
||||
|
||||
local guojia = General:new(extension, "guojia", "wei", 4)
|
||||
extension:addGeneral(guojia)
|
||||
Fk:loadTranslationTable{
|
||||
["guojia"] = "郭嘉",
|
||||
}
|
||||
|
||||
local zhenji = General:new(extension, "zhenji", "wei", 3)
|
||||
extension:addGeneral(zhenji)
|
||||
Fk:loadTranslationTable{
|
||||
["zhenji"] = "甄姬",
|
||||
}
|
||||
|
||||
local liubei = General:new(extension, "liubei", "shu", 4)
|
||||
extension:addGeneral(liubei)
|
||||
Fk:loadTranslationTable{
|
||||
["liubei"] = "刘备",
|
||||
}
|
||||
|
||||
local guanyu = General:new(extension, "guanyu", "shu", 4)
|
||||
extension:addGeneral(guanyu)
|
||||
Fk:loadTranslationTable{
|
||||
["guanyu"] = "关羽",
|
||||
}
|
||||
|
||||
local zhangfei = General:new(extension, "zhangfei", "shu", 4)
|
||||
extension:addGeneral(zhangfei)
|
||||
Fk:loadTranslationTable{
|
||||
["zhangfei"] = "张飞",
|
||||
}
|
||||
|
||||
local zhugeliang = General:new(extension, "zhugeliang", "shu", 3)
|
||||
extension:addGeneral(zhugeliang)
|
||||
Fk:loadTranslationTable{
|
||||
["zhugeliang"] = "诸葛亮",
|
||||
}
|
||||
|
||||
local zhaoyun = General:new(extension, "zhaoyun", "shu", 4)
|
||||
extension:addGeneral(zhaoyun)
|
||||
Fk:loadTranslationTable{
|
||||
["zhaoyun"] = "赵云",
|
||||
}
|
||||
|
||||
local machao = General:new(extension, "machao", "shu", 4)
|
||||
extension:addGeneral(machao)
|
||||
Fk:loadTranslationTable{
|
||||
["machao"] = "马超",
|
||||
}
|
||||
|
||||
local huangyueying = General:new(extension, "huangyueying", "shu", 3)
|
||||
extension:addGeneral(huangyueying)
|
||||
Fk:loadTranslationTable{
|
||||
["huangyueying"] = "黄月英",
|
||||
}
|
||||
|
||||
local sunquan = General:new(extension, "sunquan", "wu", 4)
|
||||
extension:addGeneral(sunquan)
|
||||
Fk:loadTranslationTable{
|
||||
["sunquan"] = "孙权",
|
||||
}
|
||||
|
||||
local ganning = General:new(extension, "ganning", "wu", 4)
|
||||
extension:addGeneral(ganning)
|
||||
Fk:loadTranslationTable{
|
||||
["ganning"] = "甘宁",
|
||||
}
|
||||
|
||||
local lvmeng = General:new(extension, "lvmeng", "wu", 4)
|
||||
extension:addGeneral(lvmeng)
|
||||
Fk:loadTranslationTable{
|
||||
["lvmeng"] = "吕蒙",
|
||||
}
|
||||
|
||||
local huanggai = General:new(extension, "huanggai", "wu", 4)
|
||||
extension:addGeneral(huanggai)
|
||||
Fk:loadTranslationTable{
|
||||
["huanggai"] = "黄盖",
|
||||
}
|
||||
|
||||
local zhouyu = General:new(extension, "zhouyu", "wu", 3)
|
||||
extension:addGeneral(zhouyu)
|
||||
Fk:loadTranslationTable{
|
||||
["zhouyu"] = "周瑜",
|
||||
}
|
||||
|
||||
local daqiao = General:new(extension, "daqiao", "wu", 3)
|
||||
extension:addGeneral(daqiao)
|
||||
Fk:loadTranslationTable{
|
||||
["daqiao"] = "大乔",
|
||||
}
|
||||
|
||||
local luxun = General:new(extension, "luxun", "wu", 3)
|
||||
extension:addGeneral(luxun)
|
||||
Fk:loadTranslationTable{
|
||||
["luxun"] = "陆逊",
|
||||
}
|
||||
|
||||
local sunshangxiang = General:new(extension, "sunshangxiang", "wu", 3)
|
||||
extension:addGeneral(sunshangxiang)
|
||||
Fk:loadTranslationTable{
|
||||
["sunshangxiang"] = "孙尚香",
|
||||
}
|
||||
|
||||
local huatuo = General:new(extension, "huatuo", "qun", 3)
|
||||
extension:addGeneral(huatuo)
|
||||
Fk:loadTranslationTable{
|
||||
["huatuo"] = "华佗",
|
||||
}
|
||||
|
||||
local lvbu = General:new(extension, "lvbu", "qun", 4)
|
||||
extension:addGeneral(lvbu)
|
||||
Fk:loadTranslationTable{
|
||||
["lvbu"] = "吕布",
|
||||
}
|
||||
|
||||
local diaochan = General:new(extension, "diaochan", "qun", 3)
|
||||
extension:addGeneral(diaochan)
|
||||
Fk:loadTranslationTable{
|
||||
["diaochan"] = "貂蝉",
|
||||
}
|
||||
|
||||
return extension
|
||||
|
|
|
@ -1 +0,0 @@
|
|||
|
|
@ -0,0 +1,14 @@
|
|||
return {
|
||||
name = "standard",
|
||||
author = "official",
|
||||
description = "",
|
||||
collaborators = {
|
||||
program = {},
|
||||
designer = {},
|
||||
cv = {},
|
||||
illustrator = {},
|
||||
},
|
||||
|
||||
dependencies = {},
|
||||
extra_files = {},
|
||||
}
|
|
@ -1,6 +1,6 @@
|
|||
callbacks["UpdateAvatar"] = function(jsonData) {
|
||||
mainWindow.busy = false;
|
||||
self.avatar = avatarName.text;
|
||||
Self.avatar = jsonData;
|
||||
toast.show("Update avatar done.");
|
||||
}
|
||||
|
||||
|
|
|
@ -7,7 +7,6 @@ import "RoomLogic.js" as Logic
|
|||
Item {
|
||||
id: roomScene
|
||||
|
||||
property var photoModel: []
|
||||
property int playerNum: 0
|
||||
property var dashboardModel
|
||||
|
||||
|
@ -23,6 +22,7 @@ Item {
|
|||
text: "quit"
|
||||
anchors.bottom: parent.bottom
|
||||
onClicked: {
|
||||
ClientInstance.clearPlayers();
|
||||
ClientInstance.notifyServer("QuitRoom", "[]");
|
||||
}
|
||||
}
|
||||
|
@ -60,6 +60,10 @@ Item {
|
|||
* +---------------------+
|
||||
*/
|
||||
|
||||
ListModel {
|
||||
id: photoModel
|
||||
}
|
||||
|
||||
Item {
|
||||
id: roomArea
|
||||
width: roomScene.width
|
||||
|
@ -69,20 +73,20 @@ Item {
|
|||
id: photos
|
||||
model: photoModel
|
||||
Photo {
|
||||
general: modelData.general
|
||||
screenName: modelData.screenName
|
||||
role: modelData.role
|
||||
kingdom: modelData.kingdom
|
||||
netstate: modelData.netstate
|
||||
maxHp: modelData.maxHp
|
||||
hp: modelData.hp
|
||||
seatNumber: modelData.seatNumber
|
||||
isDead: modelData.isDead
|
||||
dying: modelData.dying
|
||||
faceturned: modelData.faceturned
|
||||
chained: modelData.chained
|
||||
drank: modelData.drank
|
||||
isOwner: modelData.isOwner
|
||||
general: _general
|
||||
screenName: _screenName
|
||||
role: _role
|
||||
kingdom: _kingdom
|
||||
netstate: _netstate
|
||||
maxHp: _maxHp
|
||||
hp: _hp
|
||||
seatNumber: _seatNumber
|
||||
isDead: _isDead
|
||||
dying: _dying
|
||||
faceturned: _faceturned
|
||||
chained: _chained
|
||||
drank: _drank
|
||||
isOwner: _isOwner
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -129,6 +133,7 @@ Item {
|
|||
toast.show("Sucesessfully entered room.");
|
||||
|
||||
dashboardModel = {
|
||||
id: Self.id,
|
||||
general: Self.avatar,
|
||||
screenName: Self.screenName,
|
||||
role: "unknown",
|
||||
|
@ -149,24 +154,25 @@ Item {
|
|||
|
||||
let i;
|
||||
for (i = 1; i < playerNum; i++) {
|
||||
photoModel.push({
|
||||
general: "",
|
||||
screenName: "",
|
||||
role: "unknown",
|
||||
kingdom: "qun",
|
||||
netstate: "online",
|
||||
maxHp: 0,
|
||||
hp: 0,
|
||||
seatNumber: i + 1,
|
||||
isDead: false,
|
||||
dying: false,
|
||||
faceturned: false,
|
||||
chained: false,
|
||||
drank: false,
|
||||
isOwner: false
|
||||
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,
|
||||
_faceturned: false,
|
||||
_chained: false,
|
||||
_drank: false,
|
||||
_isOwner: false
|
||||
});
|
||||
}
|
||||
photoModel = photoModel; // Force the Repeater reload
|
||||
|
||||
Logic.arrangePhotos();
|
||||
}
|
||||
|
|
|
@ -24,6 +24,14 @@ Item {
|
|||
property bool drank: false
|
||||
property bool isOwner: false
|
||||
|
||||
Behavior on x {
|
||||
NumberAnimation { duration: 600; easing.type: Easing.InOutQuad }
|
||||
}
|
||||
|
||||
Behavior on y {
|
||||
NumberAnimation { duration: 600; easing.type: Easing.InOutQuad }
|
||||
}
|
||||
|
||||
Image {
|
||||
id: back
|
||||
source: SkinBank.PHOTO_BACK_DIR + root.kingdom
|
||||
|
@ -145,11 +153,42 @@ Item {
|
|||
anchors.rightMargin: -4
|
||||
}
|
||||
|
||||
Text {
|
||||
GlowText {
|
||||
id: seatNum
|
||||
visible: false // TODO
|
||||
property var seatChr: ["一", "二", "三", "四", "五", "六", "七"]
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.bottomMargin: -32
|
||||
property var seatChr: ["一", "二", "三", "四", "五", "六", "七", "八"]
|
||||
font.family: "FZLiShu II-S06S"
|
||||
text: seatChr[root.seatNumber - 1]
|
||||
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()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -42,65 +42,39 @@ function arrangePhotos() {
|
|||
if (!item)
|
||||
continue;
|
||||
|
||||
region = regions[seatIndex[i] - 1];
|
||||
region = regions[seatIndex[photoModel.get(i).index] - 1];
|
||||
item.x = region.x;
|
||||
item.y = region.y;
|
||||
}
|
||||
}
|
||||
|
||||
callbacks["AddPlayer"] = function(jsonData) {
|
||||
// jsonData: string screenName, string avatar
|
||||
for (let i = 0; i < photoModel.length; i++) {
|
||||
if (photoModel[i].screenName === "") {
|
||||
// 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 name = data[0];
|
||||
let avatar = data[1];
|
||||
photoModel[i] = {
|
||||
general: avatar,
|
||||
screenName: name,
|
||||
role: "unknown",
|
||||
kingdom: "qun",
|
||||
netstate: "online",
|
||||
maxHp: 0,
|
||||
hp: 0,
|
||||
seatNumber: i + 1,
|
||||
isDead: false,
|
||||
dying: false,
|
||||
faceturned: false,
|
||||
chained: false,
|
||||
drank: false,
|
||||
isOwner: false
|
||||
};
|
||||
photoModel = photoModel;
|
||||
arrangePhotos();
|
||||
let uid = data[0];
|
||||
let name = data[1];
|
||||
let avatar = data[2];
|
||||
item.id = uid;
|
||||
item._screenName = name;
|
||||
item._general = avatar;
|
||||
photos.itemAt(i).tremble();
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
callbacks["RemovePlayer"] = function(jsonData) {
|
||||
// jsonData: string screenName
|
||||
let name = JSON.parse(jsonData)[0];
|
||||
for (let i = 0; i < photoModel.length; i++) {
|
||||
if (photoModel[i].screenName === name) {
|
||||
photoModel[i] = {
|
||||
general: "",
|
||||
screenName: "",
|
||||
role: "unknown",
|
||||
kingdom: "qun",
|
||||
netstate: "online",
|
||||
maxHp: 0,
|
||||
hp: 0,
|
||||
seatNumber: i + 1,
|
||||
isDead: false,
|
||||
dying: false,
|
||||
faceturned: false,
|
||||
chained: false,
|
||||
drank: false,
|
||||
isOwner: false
|
||||
};
|
||||
photoModel = photoModel;
|
||||
arrangePhotos();
|
||||
// jsonData: int uid
|
||||
let uid = JSON.parse(jsonData)[0];
|
||||
for (let i = 0; i < photoModel.count; i++) {
|
||||
let item = photoModel.get(i);
|
||||
if (item.id === uid) {
|
||||
item.id = -1;
|
||||
item._screenName = "";
|
||||
item._general = "";
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
@ -112,3 +86,51 @@ callbacks["RoomOwner"] = function(jsonData) {
|
|||
toast.show(J)
|
||||
}
|
||||
*/
|
||||
|
||||
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];
|
||||
|
||||
if (Self.id === uid) {
|
||||
dashboardModel[property_name] = value;
|
||||
roomScene.dashboardModelChanged();
|
||||
return;
|
||||
}
|
||||
|
||||
for (let i = 0; i < photoModel.count; i++) {
|
||||
let item = photoModel.get(i);
|
||||
if (item.id === uid) {
|
||||
item["_" + property_name] = value;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
callbacks["ArrangeSeats"] = function(jsonData) {
|
||||
// jsonData: seat order
|
||||
let order = JSON.parse(jsonData);
|
||||
|
||||
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);
|
||||
|
||||
for (let i = 0; i < photoModel.count; i++) {
|
||||
let item = photoModel.get(i);
|
||||
item.index = photoOrder.indexOf(item.id);
|
||||
}
|
||||
|
||||
arrangePhotos();
|
||||
}
|
||||
|
|
|
@ -49,3 +49,22 @@ 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);
|
||||
}
|
||||
|
||||
ClientPlayer *Client::addPlayer(int id, const QString &name, const QString &avatar) {
|
||||
ClientPlayer *player = new ClientPlayer(id);
|
||||
player->setScreenName(name);
|
||||
player->setAvatar(avatar);
|
||||
|
||||
players[id] = player;
|
||||
return player;
|
||||
}
|
||||
|
||||
void Client::removePlayer(int id) {
|
||||
ClientPlayer *p = players[id];
|
||||
p->deleteLater();
|
||||
players[id] = nullptr;
|
||||
}
|
||||
|
||||
void Client::clearPlayers() {
|
||||
players.clear();
|
||||
}
|
||||
|
|
|
@ -18,6 +18,10 @@ public:
|
|||
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();
|
||||
|
||||
signals:
|
||||
void error_message(const QString &msg);
|
||||
|
||||
|
|
|
@ -4,10 +4,10 @@
|
|||
|
||||
Room::Room(Server* server)
|
||||
{
|
||||
static int roomId = 0;
|
||||
id = roomId;
|
||||
roomId++;
|
||||
id = server->nextRoomId;
|
||||
server->nextRoomId++;
|
||||
this->server = server;
|
||||
setParent(server);
|
||||
gameStarted = false;
|
||||
if (!isLobby()) {
|
||||
connect(this, &Room::playerAdded, server->lobby(), &Room::removePlayer);
|
||||
|
@ -81,7 +81,7 @@ void Room::setOwner(ServerPlayer *owner)
|
|||
|
||||
void Room::addPlayer(ServerPlayer *player)
|
||||
{
|
||||
if (!player) return;
|
||||
if (isFull() || !player) return;
|
||||
|
||||
QJsonArray jsonData;
|
||||
|
||||
|
@ -126,7 +126,7 @@ void Room::removePlayer(ServerPlayer *player)
|
|||
|
||||
// player->doNotify("QuitRoom", "[]");
|
||||
QJsonArray jsonData;
|
||||
jsonData << player->getScreenName();
|
||||
jsonData << player->getId();
|
||||
doBroadcastNotify(getPlayers(), "RemovePlayer", QJsonDocument(jsonData).toJson());
|
||||
|
||||
if (isAbandoned()) {
|
||||
|
|
|
@ -18,6 +18,7 @@ Server::Server(QObject* parent)
|
|||
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);
|
||||
|
@ -188,15 +189,17 @@ void Server::handleNameAndPassword(ClientSocket *client, const QString& name, co
|
|||
} else {
|
||||
// check if this username already login
|
||||
int id = result["id"].toArray()[0].toString().toInt();
|
||||
if (!players.value(id))
|
||||
if (!players.value(id)) {
|
||||
// check if password is the same
|
||||
passed = (passwordHash == arr[0].toString());
|
||||
if (!passed) error_msg = "username or password error";
|
||||
else {
|
||||
} else {
|
||||
// TODO: reconnect here
|
||||
error_msg = "others logged in with this name";
|
||||
}
|
||||
}
|
||||
} else {
|
||||
error_msg = "invalid user name";
|
||||
}
|
||||
|
||||
if (passed) {
|
||||
|
|
|
@ -3,9 +3,10 @@
|
|||
|
||||
class ServerSocket;
|
||||
class ClientSocket;
|
||||
class Room;
|
||||
class ServerPlayer;
|
||||
|
||||
#include "room.h"
|
||||
|
||||
class Server : public QObject {
|
||||
Q_OBJECT
|
||||
|
||||
|
@ -49,6 +50,8 @@ private:
|
|||
ServerSocket *server;
|
||||
Room *m_lobby;
|
||||
QMap<int, Room *> rooms;
|
||||
int nextRoomId;
|
||||
friend Room::Room(Server *server);
|
||||
QHash<int, ServerPlayer *> players;
|
||||
|
||||
sqlite3 *db;
|
||||
|
|
|
@ -3,6 +3,12 @@
|
|||
class QmlBackend : public QObject {
|
||||
public:
|
||||
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);
|
||||
};
|
||||
|
||||
extern QmlBackend *Backend;
|
||||
|
@ -15,6 +21,9 @@ public:
|
|||
void notifyServer(const QString &command, const QString &json_data);
|
||||
|
||||
LuaFunction callback;
|
||||
|
||||
ClientPlayer *addPlayer(int id, const QString &name, const QString &avatar);
|
||||
void removePlayer(int id);
|
||||
};
|
||||
|
||||
extern Client *ClientInstance;
|
||||
|
|
|
@ -42,3 +42,35 @@ SWIG_arg ++;
|
|||
%typemap(out) QString const &
|
||||
%{ lua_pushstring(L, $1.toUtf8()); SWIG_arg++; %}
|
||||
|
||||
// QStringList
|
||||
%naturalvar QStringList;
|
||||
|
||||
%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);
|
||||
}
|
||||
%}
|
||||
|
||||
%typemap(out) QStringList
|
||||
%{
|
||||
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);
|
||||
}
|
||||
|
||||
SWIG_arg++;
|
||||
%}
|
||||
|
||||
%typemap(typecheck) QStringList
|
||||
%{
|
||||
$1 = lua_istable(L, $input) ? 1 : 0;
|
||||
%}
|
||||
|
||||
|
||||
|
|
|
@ -64,3 +64,23 @@ void QmlBackend::quitLobby()
|
|||
void QmlBackend::emitNotifyUI(const QString &command, const QString &jsonData) {
|
||||
emit notifyUI(command, jsonData);
|
||||
}
|
||||
|
||||
void QmlBackend::cd(const QString &path) {
|
||||
QDir::setCurrent(path);
|
||||
}
|
||||
|
||||
QStringList QmlBackend::ls(const QString &dir) {
|
||||
return QDir(dir).entryList(QDir::Files | QDir::Dirs | QDir::NoDotAndDotDot);
|
||||
}
|
||||
|
||||
QString QmlBackend::pwd() {
|
||||
return QDir::currentPath();
|
||||
}
|
||||
|
||||
bool QmlBackend::exists(const QString &file) {
|
||||
return QFile::exists(file);
|
||||
}
|
||||
|
||||
bool QmlBackend::isDir(const QString &file) {
|
||||
return QFileInfo(file).isDir();
|
||||
}
|
||||
|
|
|
@ -18,6 +18,13 @@ public:
|
|||
// lua --> qml
|
||||
void emitNotifyUI(const QString &command, const QString &jsonData);
|
||||
|
||||
// File used by both Lua and Qml
|
||||
static Q_INVOKABLE void cd(const QString &path);
|
||||
static Q_INVOKABLE QStringList ls(const QString &dir = "");
|
||||
static Q_INVOKABLE QString pwd();
|
||||
static Q_INVOKABLE bool exists(const QString &file);
|
||||
static Q_INVOKABLE bool isDir(const QString &file);
|
||||
|
||||
signals:
|
||||
void notifyUI(const QString &command, const QString &jsonData);
|
||||
|
||||
|
|
Loading…
Reference in New Issue