mirror of
https://github.com/Qsgs-Fans/FreeKill.git
synced 2024-11-16 11:42:45 +08:00
Use skill (#25)
* distance & snatch * distance & snatch - clean * fix: client.alive_players * room:askForCardChosen(todo: snatch) * add skill to players(uncomplete) * ui of skill btn * expand pile(not completed) * Use card2 (#23) * snatch (todo: owner_map) * owner_map * remove too many snatch * update * call active skill's functions * use zhiheng * Qt6 (#24) * use qt6 * android compiling * remove version number of qml import * correct anti-sql injection; update deprecated code * add fkparse as submodule * link fkparse, and write simple functions * adjust ui * adjust layout for photos; fix qml warning * android problem fix (partially) * move ico * update copy_assets * update logic for ok/cancel btn
This commit is contained in:
parent
7da5fcfa2c
commit
162b3af505
3
.gitmodules
vendored
Normal file
3
.gitmodules
vendored
Normal file
|
@ -0,0 +1,3 @@
|
|||
[submodule "fkparse"]
|
||||
path = fkparse
|
||||
url = git@github.com:Notify-ctrl/fkparse
|
|
@ -2,7 +2,10 @@ cmake_minimum_required(VERSION 3.16)
|
|||
|
||||
project(FreeKill VERSION 0.0.1)
|
||||
|
||||
find_package(Qt5 REQUIRED COMPONENTS
|
||||
include_directories(fkparse/src)
|
||||
add_subdirectory(fkparse)
|
||||
|
||||
find_package(Qt6 REQUIRED COMPONENTS
|
||||
Gui
|
||||
Qml
|
||||
Network
|
||||
|
@ -15,7 +18,7 @@ find_package(SQLite3)
|
|||
set(CMAKE_AUTOMOC ON)
|
||||
set(CMAKE_CXX_STANDARD 11)
|
||||
set(CMAKE_CXX_STANDARD_REQUIRED True)
|
||||
set(REQUIRED_QT_VERSION "5.15.2")
|
||||
set(REQUIRED_QT_VERSION "6.3")
|
||||
|
||||
include_directories(include/lua)
|
||||
include_directories(include/sqlite3)
|
||||
|
@ -26,4 +29,15 @@ include_directories(src/network)
|
|||
include_directories(src/server)
|
||||
include_directories(src/ui)
|
||||
|
||||
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
|
||||
)
|
||||
|
||||
qt_add_executable(FreeKill)
|
||||
add_subdirectory(src)
|
||||
|
|
2
android/.gitignore
vendored
Normal file
2
android/.gitignore
vendored
Normal file
|
@ -0,0 +1,2 @@
|
|||
assets/
|
||||
res/
|
51
android/AndroidManifest.xml
Normal file
51
android/AndroidManifest.xml
Normal file
|
@ -0,0 +1,51 @@
|
|||
<?xml version="1.0"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="org.notify.FreeKill"
|
||||
android:installLocation="auto"
|
||||
android:versionCode="1"
|
||||
android:versionName="1.0">
|
||||
<uses-permission android:name="android.permission.INTERNET" />
|
||||
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
|
||||
|
||||
<supports-screens
|
||||
android:anyDensity="true"
|
||||
android:largeScreens="true"
|
||||
android:normalScreens="true"
|
||||
android:smallScreens="true" />
|
||||
<application
|
||||
android:name="org.qtproject.qt.android.bindings.QtApplication"
|
||||
android:hardwareAccelerated="true"
|
||||
android:label="FreeKill"
|
||||
android:icon="@mipmap/icon"
|
||||
|
||||
android:requestLegacyExternalStorage="true"
|
||||
android:allowNativeHeapPointerTagging="false"
|
||||
android:allowBackup="true"
|
||||
android:fullBackupOnly="false">
|
||||
<activity
|
||||
android:name="org.qtproject.qt.android.bindings.QtActivity"
|
||||
android:configChanges="orientation|uiMode|screenLayout|screenSize|smallestScreenSize|layoutDirection|locale|fontScale|keyboard|keyboardHidden|navigation|mcc|mnc|density"
|
||||
android:label="FreeKill"
|
||||
android:launchMode="singleTop"
|
||||
android:screenOrientation="sensorLandscape"
|
||||
android:theme="@android:style/Theme.NoTitleBar.Fullscreen"
|
||||
android:exported="true">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
<category android:name="android.intent.category.LAUNCHER" />
|
||||
</intent-filter>
|
||||
|
||||
<meta-data
|
||||
android:name="android.app.lib_name"
|
||||
android:value="FreeKill" />
|
||||
|
||||
<meta-data
|
||||
android:name="android.app.arguments"
|
||||
android:value="" />
|
||||
|
||||
<meta-data
|
||||
android:name="android.app.extract_android_style"
|
||||
android:value="minimal" />
|
||||
</activity>
|
||||
</application>
|
||||
</manifest>
|
45
android/copy_assets.sh
Executable file
45
android/copy_assets.sh
Executable file
|
@ -0,0 +1,45 @@
|
|||
#!/bin/sh
|
||||
|
||||
if [ ! -e res/mipmap ]; then
|
||||
mkdir -p res/mipmap
|
||||
fi
|
||||
cp ../image/icon.png res/mipmap
|
||||
|
||||
if [ ! -e assets/res ]; then
|
||||
mkdir -p assets/res
|
||||
fi
|
||||
|
||||
cp -r ../fonts assets/res
|
||||
cp -r ../image assets/res
|
||||
cp -r ../lua assets/res
|
||||
cp -r ../packages assets/res
|
||||
cp -r ../qml assets/res
|
||||
cp -r ../server assets/res
|
||||
rm assets/res/server/users.db
|
||||
cp ../LICENSE assets/res
|
||||
|
||||
# Due to Qt Android's bug, we need make sure every directory has a subfile (not subdir)
|
||||
function fixDir() {
|
||||
cd $1
|
||||
hasSubfile=false
|
||||
for f in $(ls); do
|
||||
if [ -f $f ]; then
|
||||
hasSubfile=true
|
||||
break
|
||||
fi
|
||||
done
|
||||
|
||||
if ! $hasSubfile; then
|
||||
echo "辣鸡Qt" > bug.txt
|
||||
fi
|
||||
|
||||
for f in $(ls); do
|
||||
if [ -d $f ]; then
|
||||
fixDir $f
|
||||
fi
|
||||
done
|
||||
cd ..
|
||||
}
|
||||
|
||||
fixDir assets/res
|
||||
|
1
fkparse
Submodule
1
fkparse
Submodule
|
@ -0,0 +1 @@
|
|||
Subproject commit bbf45faf7dd67fca4bedf535c14a8037990a7399
|
BIN
image/button/skill/active/disabled.png
Normal file
BIN
image/button/skill/active/disabled.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 4.8 KiB |
BIN
image/button/skill/active/normal.png
Normal file
BIN
image/button/skill/active/normal.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 5.9 KiB |
BIN
image/button/skill/active/pressed.png
Normal file
BIN
image/button/skill/active/pressed.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 8.5 KiB |
BIN
image/icon.png
Normal file
BIN
image/icon.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 11 KiB |
BIN
lib/android/liblua54.so
Normal file
BIN
lib/android/liblua54.so
Normal file
Binary file not shown.
BIN
lib/android/libsqlite3.so
Normal file
BIN
lib/android/libsqlite3.so
Normal file
Binary file not shown.
|
@ -1,6 +1,9 @@
|
|||
---@class Client
|
||||
---@field client fk.Client
|
||||
---@field players ClientPlayer[]
|
||||
---@field alive_players ClientPlayer[]
|
||||
---@field current ClientPlayer
|
||||
---@field discard_pile integer[]
|
||||
Client = class('Client')
|
||||
|
||||
-- load client classes
|
||||
|
@ -23,17 +26,42 @@ function Client:initialize()
|
|||
end
|
||||
|
||||
self.players = {} -- ClientPlayer[]
|
||||
self.alive_players = {}
|
||||
self.discard_pile = {}
|
||||
end
|
||||
|
||||
---@param id integer
|
||||
---@return ClientPlayer
|
||||
function Client:findPlayer(id)
|
||||
function Client:getPlayerById(id)
|
||||
for _, p in ipairs(self.players) do
|
||||
if p.player:getId() == id then return p end
|
||||
if p.id == id then return p end
|
||||
end
|
||||
return nil
|
||||
end
|
||||
|
||||
function Client:moveCards(moves)
|
||||
for _, move in ipairs(moves) do
|
||||
if move.from and move.fromArea then
|
||||
local from = self:getPlayerById(move.from)
|
||||
if from.id ~= Self.id and move.fromArea == Card.PlayerHand then
|
||||
for i = 1, #move.ids do
|
||||
table.remove(from.player_cards[Player.Hand])
|
||||
end
|
||||
else
|
||||
from:removeCards(move.fromArea, move.ids)
|
||||
end
|
||||
elseif move.fromArea == Card.DiscardPile then
|
||||
table.removeOne(self.discard_pile, move.ids[1])
|
||||
end
|
||||
|
||||
if move.to and move.toArea then
|
||||
self:getPlayerById(move.to):addCards(move.toArea, move.ids)
|
||||
elseif move.toArea == Card.DiscardPile then
|
||||
table.insert(self.discard_pile, move.ids[1])
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
fk.client_callback["Setup"] = function(jsonData)
|
||||
-- jsonData: [ int id, string screenName, string avatar ]
|
||||
local data = json.decode(jsonData)
|
||||
|
@ -47,6 +75,8 @@ end
|
|||
|
||||
fk.client_callback["EnterRoom"] = function(jsonData)
|
||||
ClientInstance.players = {Self}
|
||||
ClientInstance.alive_players = {Self}
|
||||
ClientInstance.discard_pile = {}
|
||||
ClientInstance:notifyUI("EnterRoom", jsonData)
|
||||
end
|
||||
|
||||
|
@ -56,7 +86,9 @@ fk.client_callback["AddPlayer"] = function(jsonData)
|
|||
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))
|
||||
local p = ClientPlayer:new(player)
|
||||
table.insert(ClientInstance.players, p)
|
||||
table.insert(ClientInstance.alive_players, p)
|
||||
ClientInstance:notifyUI("AddPlayer", jsonData)
|
||||
end
|
||||
|
||||
|
@ -67,6 +99,7 @@ fk.client_callback["RemovePlayer"] = function(jsonData)
|
|||
for _, p in ipairs(ClientInstance.players) do
|
||||
if p.player:getId() == id then
|
||||
table.removeOne(ClientInstance.players, p)
|
||||
table.removeOne(ClientInstance.alive_players, p)
|
||||
break
|
||||
end
|
||||
end
|
||||
|
@ -80,7 +113,9 @@ fk.client_callback["ArrangeSeats"] = function(jsonData)
|
|||
local players = {}
|
||||
|
||||
for i = 1, n do
|
||||
table.insert(players, ClientInstance:findPlayer(data[i]))
|
||||
local p = ClientInstance:getPlayerById(data[i])
|
||||
p.seat = i
|
||||
table.insert(players, p)
|
||||
end
|
||||
ClientInstance.players = players
|
||||
|
||||
|
@ -91,10 +126,31 @@ 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:getPlayerById(id)[name] = value
|
||||
ClientInstance:notifyUI("PropertyUpdate", jsonData)
|
||||
end
|
||||
|
||||
fk.client_callback["AskForCardChosen"] = function(jsonData)
|
||||
-- jsonData: [ int target_id, string flag, int reason ]
|
||||
local data = json.decode(jsonData)
|
||||
local id, flag, reason = data[1], data[2], data[3]
|
||||
local target = ClientInstance:getPlayerById(id)
|
||||
local hand = target.player_cards[Player.Hand]
|
||||
local equip = target.player_cards[Player.Equip]
|
||||
local judge = target.player_cards[Player.Judge]
|
||||
if not string.find(flag, "h") then
|
||||
hand = {}
|
||||
end
|
||||
if not string.find(flag, "e") then
|
||||
equip = {}
|
||||
end
|
||||
if not string.find(flag, "j") then
|
||||
judge = {}
|
||||
end
|
||||
local ui_data = {hand, equip, judge, reason}
|
||||
ClientInstance:notifyUI("AskForCardChosen", json.encode(ui_data))
|
||||
end
|
||||
|
||||
--- separated moves to many moves(one card per move)
|
||||
---@param moves CardsMoveStruct[]
|
||||
local function separateMoves(moves)
|
||||
|
@ -113,13 +169,15 @@ local function separateMoves(moves)
|
|||
return ret
|
||||
end
|
||||
|
||||
--- merge separated moves (one fromArea per move)
|
||||
--- merge separated moves that information is the same
|
||||
local function mergeMoves(moves)
|
||||
local ret = {}
|
||||
local temp = {}
|
||||
for _, move in ipairs(moves) do
|
||||
if temp[move.fromArea] == nil then
|
||||
temp[move.fromArea] = {
|
||||
local info = string.format("%q,%q,%q,%q",
|
||||
move.from, move.to, move.fromArea, move.toArea)
|
||||
if temp[info] == nil then
|
||||
temp[info] = {
|
||||
ids = {},
|
||||
from = move.from,
|
||||
to = move.to,
|
||||
|
@ -127,7 +185,7 @@ local function mergeMoves(moves)
|
|||
toArea = move.toArea
|
||||
}
|
||||
end
|
||||
table.insert(temp[move.fromArea].ids, move.ids[1])
|
||||
table.insert(temp[info].ids, move.ids[1])
|
||||
end
|
||||
for _, v in pairs(temp) do
|
||||
table.insert(ret, v)
|
||||
|
@ -139,10 +197,35 @@ fk.client_callback["MoveCards"] = function(jsonData)
|
|||
-- jsonData: CardsMoveStruct[]
|
||||
local raw_moves = json.decode(jsonData)
|
||||
local separated = separateMoves(raw_moves)
|
||||
ClientInstance:moveCards(separated)
|
||||
local merged = mergeMoves(separated)
|
||||
ClientInstance:notifyUI("MoveCards", json.encode(merged))
|
||||
end
|
||||
|
||||
fk.client_callback["LoseSkill"] = function(jsonData)
|
||||
-- jsonData: [ int player_id, string skill_name ]
|
||||
local data = json.decode(jsonData)
|
||||
local id, skill_name = data[1], data[2]
|
||||
local target = ClientInstance:getPlayerById(id)
|
||||
local skill = Fk.skills[skill_name]
|
||||
target:loseSkill(skill)
|
||||
if skill.visible then
|
||||
ClientInstance:notifyUI("LoseSkill", jsonData)
|
||||
end
|
||||
end
|
||||
|
||||
fk.client_callback["AddSkill"] = function(jsonData)
|
||||
-- jsonData: [ int player_id, string skill_name ]
|
||||
local data = json.decode(jsonData)
|
||||
local id, skill_name = data[1], data[2]
|
||||
local target = ClientInstance:getPlayerById(id)
|
||||
local skill = Fk.skills[skill_name]
|
||||
target:addSkill(skill)
|
||||
if skill.visible then
|
||||
ClientInstance:notifyUI("AddSkill", jsonData)
|
||||
end
|
||||
end
|
||||
|
||||
-- Create ClientInstance (used by Lua)
|
||||
ClientInstance = Client:new()
|
||||
dofile "lua/client/client_util.lua"
|
||||
|
|
|
@ -87,7 +87,7 @@ function CanUseCard(card, player)
|
|||
error()
|
||||
end
|
||||
|
||||
local ret = c.skill:canUse(ClientInstance:findPlayer(player))
|
||||
local ret = c.skill:canUse(ClientInstance:getPlayerById(player))
|
||||
return json.encode(ret)
|
||||
end
|
||||
|
||||
|
@ -102,7 +102,8 @@ function CanUseCardToTarget(card, to_select, selected)
|
|||
c = Fk:getCardById(card)
|
||||
selected_cards = {card}
|
||||
else
|
||||
error()
|
||||
local t = json.decode(card)
|
||||
return ActiveTargetFilter(t.skill, to_select, selected, t.subcards)
|
||||
end
|
||||
|
||||
local ret = c.skill:targetFilter(to_select, selected, selected_cards)
|
||||
|
@ -137,13 +138,70 @@ function CardFeasible(card, selected_targets)
|
|||
c = Fk:getCardById(card)
|
||||
selected_cards = {card}
|
||||
else
|
||||
error()
|
||||
local t = json.decode(card)
|
||||
return ActiveFeasible(t.skill, selected_targets, t.subcards)
|
||||
end
|
||||
|
||||
local ret = c.skill:feasible(selected_cards, selected_targets)
|
||||
local ret = c.skill:feasible(selected_targets, selected_cards)
|
||||
return json.encode(ret)
|
||||
end
|
||||
|
||||
-- Handle skills
|
||||
|
||||
function GetSkillData(skill_name)
|
||||
local skill = Fk.skills[skill_name]
|
||||
local freq = "notactive"
|
||||
if skill:isInstanceOf(ActiveSkill) then
|
||||
freq = "active"
|
||||
end
|
||||
return json.encode{
|
||||
skill = Fk:translate(skill_name),
|
||||
orig_skill = skill_name,
|
||||
freq = freq
|
||||
}
|
||||
end
|
||||
|
||||
function ActiveCanUse(skill_name)
|
||||
local skill = Fk.skills[skill_name]
|
||||
local ret = false
|
||||
if skill and skill:isInstanceOf(ActiveSkill) then
|
||||
ret = skill:canUse(Self)
|
||||
end
|
||||
return json.encode(ret)
|
||||
end
|
||||
|
||||
function ActiveCardFilter(skill_name, to_select, selected, selected_targets)
|
||||
local skill = Fk.skills[skill_name]
|
||||
local ret = false
|
||||
if skill and skill:isInstanceOf(ActiveSkill) then
|
||||
ret = skill:cardFilter(to_select, selected, selected_targets)
|
||||
end
|
||||
return json.encode(ret)
|
||||
end
|
||||
|
||||
function ActiveTargetFilter(skill_name, to_select, selected, selected_cards)
|
||||
local skill = Fk.skills[skill_name]
|
||||
local ret = false
|
||||
if skill and skill:isInstanceOf(ActiveSkill) then
|
||||
ret = skill:targetFilter(to_select, selected, selected_cards)
|
||||
end
|
||||
return json.encode(ret)
|
||||
end
|
||||
|
||||
function ActiveFeasible(skill_name, selected, selected_cards)
|
||||
local skill = Fk.skills[skill_name]
|
||||
local ret = false
|
||||
if skill and skill:isInstanceOf(ActiveSkill) then
|
||||
ret = skill:feasible(selected, selected_cards)
|
||||
end
|
||||
return json.encode(ret)
|
||||
end
|
||||
|
||||
-- ViewAsSkill (Todo)
|
||||
function CanViewAs(skill_name, card_ids)
|
||||
return "true"
|
||||
end
|
||||
|
||||
Fk:loadTranslationTable{
|
||||
-- Lobby
|
||||
["Room List"] = "房间列表",
|
||||
|
@ -190,4 +248,11 @@ Fk:loadTranslationTable{
|
|||
["AskForGeneral"] = "选择武将",
|
||||
["AskForChoice"] = "选择",
|
||||
["PlayCard"] = "出牌",
|
||||
|
||||
["AskForCardChosen"] = "选牌",
|
||||
["#AskForChooseCard"] = "%1:请选择其一张卡牌",
|
||||
["$ChooseCard"] = "请选择一张卡牌",
|
||||
["$Hand"] = "手牌区",
|
||||
["$Equip"] = "装备区",
|
||||
["$Judge"] = "判定区",
|
||||
}
|
||||
|
|
|
@ -1,13 +1,30 @@
|
|||
---@class ClientPlayer
|
||||
---@class ClientPlayer: Player
|
||||
---@field player fk.Player
|
||||
---@field handcardNum integer
|
||||
---@field known_cards integer[]
|
||||
---@field global_known_cards integer[]
|
||||
local ClientPlayer = Player:subclass("ClientPlayer")
|
||||
|
||||
function ClientPlayer:initialize(cp)
|
||||
Player.initialize(self)
|
||||
self.id = cp:getId()
|
||||
self.player = cp
|
||||
self.handcardNum = 0
|
||||
self.known_cards = {}
|
||||
self.known_cards = {} -- you know he/she have this card, but not shown
|
||||
self.global_known_cards = {} -- card that visible to all players
|
||||
end
|
||||
|
||||
---@param skill Skill
|
||||
function ClientPlayer:hasSkill(skill)
|
||||
return table.contains(self.player_skills, skill)
|
||||
end
|
||||
|
||||
---@param skill Skill
|
||||
function ClientPlayer:addSkill(skill)
|
||||
table.insert(self.player_skills, skill)
|
||||
end
|
||||
|
||||
---@param skill Skill
|
||||
function ClientPlayer:loseSkill(skill)
|
||||
table.removeOne(self.player_skills, skill)
|
||||
end
|
||||
|
||||
return ClientPlayer
|
||||
|
|
|
@ -209,4 +209,11 @@ function Engine:getCardById(id)
|
|||
return self.cards[id]
|
||||
end
|
||||
|
||||
function Engine:currentRoom()
|
||||
if ClientInstance then
|
||||
return ClientInstance
|
||||
end
|
||||
return RoomInstance
|
||||
end
|
||||
|
||||
return Engine
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
---@field dead boolean
|
||||
---@field state string
|
||||
---@field player_skills Skill[]
|
||||
---@field derivative_skills table<Skill, Skill[]>
|
||||
---@field flag string[]
|
||||
---@field tag table<string, any>
|
||||
---@field mark table<string, integer>
|
||||
|
@ -57,6 +58,7 @@ function Player:initialize()
|
|||
self.state = ""
|
||||
|
||||
self.player_skills = {}
|
||||
self.derivative_skills = {}
|
||||
self.flag = {}
|
||||
self.tag = {}
|
||||
self.mark = {}
|
||||
|
@ -236,6 +238,20 @@ function Player:getAttackRange()
|
|||
return math.max(baseAttackRange, 0)
|
||||
end
|
||||
|
||||
---@param other Player
|
||||
function Player:distanceTo(other)
|
||||
local right = math.abs(self.seat - other.seat)
|
||||
local left = #Fk:currentRoom().alive_players - right
|
||||
local ret = math.min(left, right)
|
||||
-- TODO: corrent distance here using skills
|
||||
return math.max(ret, 1)
|
||||
end
|
||||
|
||||
---@param other Player
|
||||
function Player:inMyAttackRange(other)
|
||||
return self ~= other and self:distanceTo(other) <= self:getAttackRange()
|
||||
end
|
||||
|
||||
function Player:addCardUseHistory(cardName, num)
|
||||
assert(type(num) == "number" and num ~= 0)
|
||||
|
||||
|
@ -249,4 +265,126 @@ function Player:resetCardUseHistory(cardName)
|
|||
end
|
||||
end
|
||||
|
||||
function Player:isKongcheng()
|
||||
return #self:getCardIds(Player.Hand) == 0
|
||||
end
|
||||
|
||||
function Player:isNude()
|
||||
return #self:getCardIds{Player.Hand, Player.Equip} == 0
|
||||
end
|
||||
|
||||
function Player:isAllNude()
|
||||
return #self:getCardIds() == 0
|
||||
end
|
||||
|
||||
---@param skill string | Skill
|
||||
---@return Skill
|
||||
local function getActualSkill(skill)
|
||||
if type(skill) == "string" then
|
||||
skill = Fk.skills[skill]
|
||||
end
|
||||
assert(skill:isInstanceOf(Skill))
|
||||
return skill
|
||||
end
|
||||
|
||||
---@param skill string | Skill
|
||||
function Player:hasEquipSkill(skill)
|
||||
skill = getActualSkill(skill)
|
||||
local equips = self.player_cards[Player.Equip]
|
||||
for _, id in ipairs(equips) do
|
||||
local card = Fk:getCardById(id)
|
||||
if card.skill == skill then
|
||||
return true
|
||||
end
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
---@param skill string | Skill
|
||||
function Player:hasSkill(skill)
|
||||
skill = getActualSkill(skill)
|
||||
|
||||
if self:hasEquipSkill(skill) then
|
||||
return true
|
||||
end
|
||||
|
||||
if table.contains(self.player_skills, skill) then
|
||||
return true
|
||||
end
|
||||
|
||||
for _, v in pairs(self.derivative_skills) do
|
||||
if table.contains(v, skill) then
|
||||
return true
|
||||
end
|
||||
end
|
||||
|
||||
return false
|
||||
end
|
||||
|
||||
---@param skill string | Skill
|
||||
---@param source_skill string | Skill | nil
|
||||
---@return Skill[] @ got skills that Player didn't have at start
|
||||
function Player:addSkill(skill, source_skill)
|
||||
skill = getActualSkill(skill)
|
||||
|
||||
local toget = table.clone(skill.related_skills)
|
||||
table.insert(toget, skill)
|
||||
local ret = {}
|
||||
for _, s in ipairs(toget) do
|
||||
if not self:hasSkill(s) then
|
||||
table.insert(ret, s)
|
||||
end
|
||||
end
|
||||
|
||||
if source_skill then
|
||||
source_skill = getActualSkill(source_skill)
|
||||
if not self.derivative_skills[source_skill] then
|
||||
self.derivative_skills[source_skill] = {}
|
||||
end
|
||||
table.insertIfNeed(self.derivative_skills[source_skill], skill)
|
||||
else
|
||||
table.insertIfNeed(self.player_skills, skill)
|
||||
end
|
||||
|
||||
-- add related skills
|
||||
if not self.derivative_skills[skill] then
|
||||
self.derivative_skills[skill] = {}
|
||||
end
|
||||
for _, s in ipairs(skill.related_skills) do
|
||||
table.insertIfNeed(self.derivative_skills[skill], s)
|
||||
end
|
||||
|
||||
return ret
|
||||
end
|
||||
|
||||
---@param skill string | Skill
|
||||
---@param source_skill string | Skill | nil
|
||||
---@return Skill[] @ lost skills that the Player doesn't have anymore
|
||||
function Player:loseSkill(skill, source_skill)
|
||||
skill = getActualSkill(skill)
|
||||
|
||||
if source_skill then
|
||||
source_skill = getActualSkill(source_skill)
|
||||
if not self.derivative_skills[source_skill] then
|
||||
self.derivative_skills[source_skill] = {}
|
||||
end
|
||||
table.removeOne(self.derivative_skills[source_skill], skill)
|
||||
else
|
||||
table.removeOne(self.player_skills, skill)
|
||||
end
|
||||
|
||||
-- clear derivative skills of this skill as well
|
||||
local tolose = self.derivative_skills[skill]
|
||||
table.insert(tolose, skill)
|
||||
self.derivative_skills[skill] = nil
|
||||
|
||||
local ret = {} ---@type Skill[]
|
||||
for _, s in ipairs(tolose) do
|
||||
if not self:hasSkill(s) then
|
||||
table.insert(ret, s)
|
||||
end
|
||||
end
|
||||
return ret
|
||||
end
|
||||
|
||||
return Player
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
---@field name string
|
||||
---@field frequency Frequency
|
||||
---@field visible boolean
|
||||
---@field related_skills Skill[]
|
||||
local Skill = class("Skill")
|
||||
|
||||
---@alias Frequency integer
|
||||
|
@ -17,6 +18,12 @@ function Skill:initialize(name, frequency)
|
|||
self.name = name
|
||||
self.frequency = frequency
|
||||
self.visible = true
|
||||
self.related_skills = {}
|
||||
end
|
||||
|
||||
---@param skill Skill
|
||||
function Skill:addRelatedSkill(skill)
|
||||
table.insert(self.related_skills, skill)
|
||||
end
|
||||
|
||||
return Skill
|
||||
|
|
|
@ -38,9 +38,9 @@ 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)
|
||||
---@param selected integer[] @ ids of selected players
|
||||
---@param selected_cards integer[] @ ids of selected cards
|
||||
function ActiveSkill:feasible(selected, selected_cards)
|
||||
return true
|
||||
end
|
||||
|
||||
|
@ -51,7 +51,7 @@ end
|
|||
function ActiveSkill:onUse(room, cardUseEvent) end
|
||||
|
||||
---@param room Room
|
||||
---@param cardEffectEvent CardEffectEvent
|
||||
---@param cardEffectEvent CardEffectEvent | SkillEffectEvent
|
||||
function ActiveSkill:onEffect(room, cardEffectEvent) end
|
||||
|
||||
return ActiveSkill
|
||||
|
|
|
@ -66,6 +66,29 @@ function table.clone(self)
|
|||
return ret
|
||||
end
|
||||
|
||||
-- if table does not contain the element, we insert it
|
||||
function table:insertIfNeed(element)
|
||||
if not table.contains(self, element) then
|
||||
table.insert(self, element)
|
||||
end
|
||||
end
|
||||
|
||||
---@param delimiter string
|
||||
---@return string[]
|
||||
function string:split(delimiter)
|
||||
if #self == 0 then return {} end
|
||||
local result = {}
|
||||
local from = 1
|
||||
local delim_from, delim_to = string.find(self, delimiter, from)
|
||||
while delim_from do
|
||||
table.insert(result, string.sub(self, from, delim_from - 1))
|
||||
from = delim_to + 1
|
||||
delim_from, delim_to = string.find(self, delimiter, from)
|
||||
end
|
||||
table.insert(result, string.sub(self, from))
|
||||
return result
|
||||
end
|
||||
|
||||
---@class Sql
|
||||
Sql = {
|
||||
---@param filename string
|
||||
|
|
|
@ -87,7 +87,7 @@ end
|
|||
---@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 feasible fun(self: ActiveSkill, selected: integer[], selected_cards: integer[]): boolean
|
||||
---@field on_use fun(self: ActiveSkill, room: Room, cardUseEvent: CardUseStruct): boolean
|
||||
---@field on_effect fun(self: ActiveSkill, room: Room, cardEffectEvent: CardEffectEvent): boolean
|
||||
|
||||
|
|
|
@ -133,7 +133,11 @@ function GameLogic:prepareForStart()
|
|||
table.shuffle(allCardIds)
|
||||
room.draw_pile = allCardIds
|
||||
for _, id in ipairs(room.draw_pile) do
|
||||
self.room:setCardArea(id, Card.DrawPile)
|
||||
self.room:setCardArea(id, Card.DrawPile, nil)
|
||||
end
|
||||
|
||||
for _, p in ipairs(room.alive_players) do
|
||||
room:handleAddLoseSkills(p, "zhiheng")
|
||||
end
|
||||
|
||||
self:addTriggerSkill(GameRule)
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
---@field processing_area integer[]
|
||||
---@field void integer[]
|
||||
---@field card_place table<integer, CardArea>
|
||||
---@field owner_map table<integer, integer>
|
||||
local Room = class("Room")
|
||||
|
||||
-- load classes used by the game
|
||||
|
@ -32,6 +33,7 @@ function Room:initialize(_room)
|
|||
end
|
||||
|
||||
self.room.startGame = function(_self)
|
||||
Room.initialize(self, _room) -- clear old data
|
||||
self:run()
|
||||
end
|
||||
|
||||
|
@ -46,6 +48,7 @@ function Room:initialize(_room)
|
|||
self.processing_area = {}
|
||||
self.void = {}
|
||||
self.card_place = {}
|
||||
self.owner_map = {}
|
||||
end
|
||||
|
||||
-- When this function returns, the Room(C++) thread stopped.
|
||||
|
@ -81,7 +84,7 @@ end
|
|||
|
||||
---@param command string
|
||||
---@param jsonData string
|
||||
---@param players ServerPlayer[] @ default all players
|
||||
---@param players ServerPlayer[] | nil @ default all players
|
||||
function Room:doBroadcastNotify(command, jsonData, players)
|
||||
players = players or self.players
|
||||
local tolist = fk.SPlayerList()
|
||||
|
@ -177,7 +180,7 @@ function Room:shuffleDrawPile()
|
|||
|
||||
table.insertTable(self.draw_pile, self.discard_pile)
|
||||
for _, id in ipairs(self.discard_pile) do
|
||||
self:setCardArea(id, Card.DrawPile)
|
||||
self:setCardArea(id, Card.DrawPile, nil)
|
||||
end
|
||||
self.discard_pile = {}
|
||||
table.shuffle(self.draw_pile)
|
||||
|
@ -208,8 +211,10 @@ end
|
|||
|
||||
---@param cardId integer
|
||||
---@param cardArea CardArea
|
||||
function Room:setCardArea(cardId, cardArea)
|
||||
---@param integer owner
|
||||
function Room:setCardArea(cardId, cardArea, owner)
|
||||
self.card_place[cardId] = cardArea
|
||||
self.owner_map[cardId] = owner
|
||||
end
|
||||
|
||||
---@param cardId integer
|
||||
|
@ -345,7 +350,7 @@ function Room:moveCards(...)
|
|||
|
||||
table.insert(toAreaIds, toAreaIds == Card.DrawPile and 1 or #toAreaIds + 1, info.cardId)
|
||||
end
|
||||
self:setCardArea(info.cardId, data.toArea)
|
||||
self:setCardArea(info.cardId, data.toArea, data.to)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -354,6 +359,22 @@ function Room:moveCards(...)
|
|||
return true
|
||||
end
|
||||
|
||||
---@param player integer
|
||||
---@param cid integer
|
||||
---@param unhide boolean
|
||||
---@param reason CardMoveReason
|
||||
function Room:obtainCard(player, cid, unhide, reason)
|
||||
self:moveCards({
|
||||
ids = {cid},
|
||||
from = self.owner_map[cid],
|
||||
to = player,
|
||||
toArea = Card.PlayerHand,
|
||||
moveReason = reason or fk.ReasonJustMove,
|
||||
proposer = player,
|
||||
moveVisible = unhide or false,
|
||||
})
|
||||
end
|
||||
|
||||
---@param player ServerPlayer
|
||||
---@param num integer
|
||||
---@param skillName string
|
||||
|
@ -504,6 +525,31 @@ function Room:askForGeneral(player, generals)
|
|||
return defaultChoice
|
||||
end
|
||||
|
||||
---@param chooser ServerPlayer
|
||||
---@param target ServerPlayer
|
||||
---@param flag string @ "hej", h for handcard, e for equip, j for judge
|
||||
---@param reason string
|
||||
function Room:askForCardChosen(chooser, target, flag, reason)
|
||||
local command = "AskForCardChosen"
|
||||
self:notifyMoveFocus(chooser, command)
|
||||
local data = {target.id, flag, reason}
|
||||
local result = self:doRequest(chooser, command, json.encode(data))
|
||||
|
||||
if result == "" then
|
||||
-- FIXME: generate a random card according to flag
|
||||
result = -1
|
||||
else
|
||||
result = tonumber(result)
|
||||
end
|
||||
|
||||
if result == -1 then
|
||||
local handcards = target.player_cards[Player.Hand]
|
||||
result = handcards[math.random(1, #handcards)]
|
||||
end
|
||||
|
||||
return result
|
||||
end
|
||||
|
||||
function Room:gameOver()
|
||||
self.game_finished = true
|
||||
-- dosomething
|
||||
|
@ -954,6 +1000,60 @@ function Room:useCard(cardUseEvent)
|
|||
end
|
||||
end
|
||||
|
||||
---@param player ServerPlayer
|
||||
---@param skill_names string[] | string
|
||||
---@param source_skill string | Skill | nil
|
||||
function Room:handleAddLoseSkills(player, skill_names, source_skill)
|
||||
if type(skill_names) == "string" then
|
||||
skill_names = skill_names:split("|")
|
||||
end
|
||||
|
||||
if #skill_names == 0 then return end
|
||||
local losts = {} ---@type boolean[]
|
||||
local triggers = {} ---@type Skill[]
|
||||
for _, skill in ipairs(skill_names) do
|
||||
if string.sub(skill, 1, 1) == "-" then
|
||||
local actual_skill = string.sub(skill, 2, #skill)
|
||||
if player:hasSkill(actual_skill) then
|
||||
local lost_skills = player:loseSkill(actual_skill, source_skill)
|
||||
for _, s in ipairs(lost_skills) do
|
||||
self:doBroadcastNotify("LoseSkill", json.encode{
|
||||
player.id,
|
||||
s.name
|
||||
})
|
||||
-- TODO: send a log here
|
||||
table.insert(losts, true)
|
||||
table.insert(triggers, s)
|
||||
end
|
||||
end
|
||||
else
|
||||
local sk = Fk.skills[skill]
|
||||
if sk and not player:hasSkill(sk) then
|
||||
local got_skills = player:addSkill(sk)
|
||||
|
||||
for _, s in ipairs(got_skills) do
|
||||
-- TODO: limit skill mark
|
||||
|
||||
self:doBroadcastNotify("AddSkill", json.encode{
|
||||
player.id,
|
||||
s.name
|
||||
})
|
||||
-- TODO: send log
|
||||
table.insert(losts, false)
|
||||
table.insert(triggers, s)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if #triggers > 0 then
|
||||
for i = 1, #triggers do
|
||||
local event = losts[i] and fk.EventLoseSkill or fk.EventAcquireSkill
|
||||
self.logic:trigger(event, player, triggers[i])
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
fk.room_callback["QuitRoom"] = function(jsonData)
|
||||
-- jsonData: [ int uid ]
|
||||
local data = json.decode(jsonData)
|
||||
|
|
|
@ -65,11 +65,6 @@ function ServerPlayer:waitForReply(timeout)
|
|||
return result
|
||||
end
|
||||
|
||||
---@param skill Skill
|
||||
function ServerPlayer:hasSkill(skill)
|
||||
return table.contains(self.player_skills, skill)
|
||||
end
|
||||
|
||||
function ServerPlayer:isAlive()
|
||||
return self.dead == false
|
||||
end
|
||||
|
|
|
@ -13,8 +13,9 @@
|
|||
---@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 SkillEffectEvent { from: integer, tos: integer[], cards: integer[] }
|
||||
|
||||
---@alias MoveReason integer
|
||||
---@alias CardMoveReason integer
|
||||
|
||||
fk.ReasonJustMove = 1
|
||||
fk.ReasonDraw = 2
|
||||
|
|
|
@ -42,7 +42,7 @@ GameRule = fk.CreateTriggerSkill{
|
|||
room:notifyMoveCards(room.players, {move_to_notify})
|
||||
|
||||
for _, id in ipairs(cardIds) do
|
||||
room:setCardArea(id, Card.PlayerHand)
|
||||
room:setCardArea(id, Card.PlayerHand, player.id)
|
||||
end
|
||||
|
||||
room.logic:trigger(fk.AfterDrawInitialCards, player, data)
|
||||
|
@ -91,14 +91,25 @@ GameRule = fk.CreateTriggerSkill{
|
|||
local data = json.decode(result)
|
||||
local card = data.card
|
||||
local targets = data.targets
|
||||
local use = {} ---@type CardUseStruct
|
||||
use.from = player.id
|
||||
use.tos = {}
|
||||
for _, target in ipairs(targets) do
|
||||
table.insert(use.tos, { target })
|
||||
if type(card) == "string" then
|
||||
local card_data = json.decode(card)
|
||||
local skill = Fk.skills[card_data.skill]
|
||||
local selected_cards = card_data.subcards
|
||||
skill:onEffect(room, {
|
||||
from = player.id,
|
||||
cards = selected_cards,
|
||||
tos = targets,
|
||||
})
|
||||
else
|
||||
local use = {} ---@type CardUseStruct
|
||||
use.from = player.id
|
||||
use.tos = {}
|
||||
for _, target in ipairs(targets) do
|
||||
table.insert(use.tos, { target })
|
||||
end
|
||||
use.cardId = card
|
||||
room:useCard(use)
|
||||
end
|
||||
use.cardId = card
|
||||
room:useCard(use)
|
||||
end
|
||||
end,
|
||||
[Player.Discard] = function()
|
||||
|
|
|
@ -94,10 +94,21 @@ Fk:loadTranslationTable{
|
|||
["huangyueying"] = "黄月英",
|
||||
}
|
||||
|
||||
local zhiheng = fk.CreateActiveSkill{
|
||||
name = "zhiheng",
|
||||
feasible = function(self, selected, selected_cards)
|
||||
return #selected == 0 and #selected_cards > 0
|
||||
end,
|
||||
on_effect = function(self, room, effect)
|
||||
room:drawCards(room:getPlayerById(effect.from), #effect.cards, "zhiheng")
|
||||
end
|
||||
}
|
||||
local sunquan = General:new(extension, "sunquan", "wu", 4)
|
||||
sunquan:addSkill(zhiheng)
|
||||
extension:addGeneral(sunquan)
|
||||
Fk:loadTranslationTable{
|
||||
["sunquan"] = "孙权",
|
||||
["zhiheng"] = "制衡",
|
||||
}
|
||||
|
||||
local ganning = General:new(extension, "ganning", "wu", 4)
|
||||
|
|
|
@ -118,10 +118,36 @@ extension:addCards({
|
|||
dismantlement:clone(Card.Heart, 12),
|
||||
})
|
||||
|
||||
local snatchSkill = fk.CreateActiveSkill{
|
||||
name = "snatch_skill",
|
||||
target_filter = function(self, to_select, selected)
|
||||
if #selected == 0 then
|
||||
local player = Fk:currentRoom():getPlayerById(to_select)
|
||||
return Self ~= player and Self:distanceTo(player) <= 1
|
||||
and not player:isAllNude()
|
||||
end
|
||||
end,
|
||||
feasible = function(self, selected)
|
||||
return #selected == 1
|
||||
end,
|
||||
on_effect = function(self, room, effect)
|
||||
local to = TargetGroup:getRealTargets(effect.tos)[1]
|
||||
local from = effect.from
|
||||
local cid = room:askForCardChosen(
|
||||
room:getPlayerById(from),
|
||||
room:getPlayerById(to),
|
||||
"hej",
|
||||
"snatch"
|
||||
)
|
||||
|
||||
room:obtainCard(from, cid)
|
||||
end
|
||||
}
|
||||
local snatch = fk.CreateTrickCard{
|
||||
name = "snatch",
|
||||
suit = Card.Spade,
|
||||
number = 3,
|
||||
skill = snatchSkill,
|
||||
}
|
||||
Fk:loadTranslationTable{
|
||||
["snatch"] = "顺手牵羊",
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import QtQuick 2.15
|
||||
import QtQuick
|
||||
|
||||
QtObject {
|
||||
// Client configuration
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import QtQuick 2.15
|
||||
import QtQuick.Layouts 1.15
|
||||
import QtQuick.Controls 2.0
|
||||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
import QtQuick.Controls
|
||||
import "RoomElement"
|
||||
|
||||
Item {
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import QtQuick 2.15
|
||||
import QtQuick.Layouts 1.15
|
||||
import QtQuick.Controls 2.0
|
||||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
import QtQuick.Controls
|
||||
import "RoomElement"
|
||||
|
||||
Item {
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
import QtQuick 2.15
|
||||
import QtQuick.Controls 2.0
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
|
||||
Item {
|
||||
id: root
|
||||
scale: 2
|
||||
|
||||
Frame {
|
||||
id: join_server
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import QtQuick 2.15
|
||||
import QtQuick.Controls 2.0
|
||||
import QtQuick.Window 2.0
|
||||
import QtQuick.Layouts 1.15
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Window
|
||||
import QtQuick.Layouts
|
||||
import "Logic.js" as Logic
|
||||
|
||||
Item {
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import QtQuick 2.15
|
||||
import QtQuick
|
||||
|
||||
Item {
|
||||
property bool enabled: true
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import QtQuick 2.15
|
||||
import QtQuick.Controls 2.0
|
||||
import QtQuick.Layouts 1.15
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Layouts
|
||||
import "RoomElement"
|
||||
import "RoomLogic.js" as Logic
|
||||
|
||||
|
@ -42,6 +42,15 @@ Item {
|
|||
ClientInstance.notifyServer("AddRobot", "[]");
|
||||
}
|
||||
}
|
||||
Button {
|
||||
text: "test"
|
||||
onClicked: dashboard.expandPile("_equip");
|
||||
}
|
||||
Button {
|
||||
text: "test2"
|
||||
x: 60
|
||||
onClicked: dashboard.retractPile("_equip");
|
||||
}
|
||||
|
||||
states: [
|
||||
State { name: "notactive" }, // Normal status
|
||||
|
@ -61,6 +70,7 @@ Item {
|
|||
endPhaseButton.visible = false;
|
||||
|
||||
dashboard.disableAllCards();
|
||||
dashboard.disableSkills();
|
||||
if (dashboard.pending_skill !== "")
|
||||
dashboard.stopPending();
|
||||
selected_targets = [];
|
||||
|
@ -77,6 +87,7 @@ Item {
|
|||
ScriptAction {
|
||||
script: {
|
||||
dashboard.enableCards();
|
||||
dashboard.enableSkills();
|
||||
progress.visible = true;
|
||||
okCancel.visible = true;
|
||||
endPhaseButton.visible = true;
|
||||
|
@ -88,6 +99,8 @@ Item {
|
|||
from: "*"; to: "responding"
|
||||
ScriptAction {
|
||||
script: {
|
||||
dashboard.enableCards();
|
||||
dashboard.enableSkills();
|
||||
progress.visible = true;
|
||||
okCancel.visible = true;
|
||||
}
|
||||
|
@ -98,6 +111,8 @@ Item {
|
|||
from: "*"; to: "replying"
|
||||
ScriptAction {
|
||||
script: {
|
||||
dashboard.disableAllCards();
|
||||
dashboard.disableSkills();
|
||||
progress.visible = true;
|
||||
}
|
||||
}
|
||||
|
@ -122,7 +137,7 @@ Item {
|
|||
Item {
|
||||
id: roomArea
|
||||
width: roomScene.width
|
||||
height: roomScene.height - dashboard.height
|
||||
height: roomScene.height - dashboard.height + 20
|
||||
|
||||
Repeater {
|
||||
id: photos
|
||||
|
@ -164,7 +179,7 @@ Item {
|
|||
width: parent.width * 0.6
|
||||
height: 150
|
||||
x: parent.width * 0.2
|
||||
y: parent.height * 0.6
|
||||
y: parent.height * 0.6 + 20
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -193,7 +208,7 @@ Item {
|
|||
Logic.updateSelectedTargets(self.playerid, selected);
|
||||
}
|
||||
|
||||
onCardSelected: {
|
||||
onCardSelected: function(card) {
|
||||
Logic.enableTargets(card);
|
||||
}
|
||||
}
|
||||
|
@ -201,7 +216,7 @@ Item {
|
|||
Item {
|
||||
id: controls
|
||||
anchors.bottom: dashboard.top
|
||||
anchors.bottomMargin: -40
|
||||
anchors.bottomMargin: -60
|
||||
width: roomScene.width
|
||||
|
||||
Text {
|
||||
|
@ -268,6 +283,7 @@ Item {
|
|||
|
||||
Loader {
|
||||
id: popupBox
|
||||
z: 999
|
||||
onSourceChanged: {
|
||||
if (item === null)
|
||||
return;
|
||||
|
@ -290,6 +306,15 @@ Item {
|
|||
}
|
||||
}
|
||||
|
||||
function activateSkill(skill_name, pressed) {
|
||||
if (pressed) {
|
||||
dashboard.startPending(skill_name);
|
||||
cancelButton.enabled = true;
|
||||
} else {
|
||||
Logic.doCancelButton();
|
||||
}
|
||||
}
|
||||
|
||||
Component.onCompleted: {
|
||||
toast.show(Backend.translate("$EnterRoom"));
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import QtQuick 2.15
|
||||
import QtQuick
|
||||
|
||||
// CardArea stores CardItem.
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import QtQuick 2.15
|
||||
import QtGraphicalEffects 1.0
|
||||
import QtQuick
|
||||
import Qt5Compat.GraphicalEffects
|
||||
import "../skin-bank.js" as SkinBank
|
||||
|
||||
/* Layout of card:
|
||||
|
@ -122,7 +122,7 @@ Item {
|
|||
glow.color: "black"
|
||||
glow.spread: 1
|
||||
glow.radius: 1
|
||||
glow.samples: 12
|
||||
//glow.samples: 12
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
|
@ -138,7 +138,7 @@ Item {
|
|||
drag.axis: Drag.XAndYAxis
|
||||
hoverEnabled: true
|
||||
|
||||
onReleased: {
|
||||
onReleased: function(mouse) {
|
||||
root.isClicked = mouse.isClick;
|
||||
parent.released();
|
||||
if (autoBack)
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import QtQuick 2.15
|
||||
import QtQuick
|
||||
import ".."
|
||||
|
||||
GraphicsBox {
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import QtQuick 2.15
|
||||
import QtQuick
|
||||
import ".."
|
||||
import "../skin-bank.js" as SkinBank
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import QtQuick 2.15
|
||||
import QtQuick.Layouts 1.1
|
||||
import QtGraphicalEffects 1.0
|
||||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
import Qt5Compat.GraphicalEffects
|
||||
|
||||
RowLayout {
|
||||
id: root
|
||||
|
@ -19,26 +19,40 @@ RowLayout {
|
|||
property var pendings: [] // int[], store cid
|
||||
property int selected_card: -1
|
||||
|
||||
property alias skillButtons: skillPanel.skill_buttons
|
||||
|
||||
property var expanded_piles: ({}) // name -> int[]
|
||||
|
||||
signal cardSelected(var card)
|
||||
|
||||
Item {
|
||||
width: 40
|
||||
}
|
||||
Item { width: 5 }
|
||||
|
||||
HandcardArea {
|
||||
id: handcardAreaItem
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredHeight: 130
|
||||
Layout.alignment: Qt.AlignVCenter
|
||||
Layout.alignment: Qt.AlignBottom
|
||||
Layout.bottomMargin: 24
|
||||
onWidthChanged: updateCardPosition(true);
|
||||
}
|
||||
|
||||
SkillArea {
|
||||
Layout.fillHeight: true
|
||||
Layout.fillWidth: true
|
||||
Layout.maximumWidth: width
|
||||
Layout.maximumHeight: height
|
||||
Layout.alignment: Qt.AlignBottom
|
||||
Layout.bottomMargin: 32
|
||||
Layout.rightMargin: -16
|
||||
id: skillPanel
|
||||
}
|
||||
|
||||
Photo {
|
||||
id: selfPhoto
|
||||
Layout.rightMargin: -175 / 8 + (roomArea.width - 175 * 0.75 * 7) / 8
|
||||
handcards: handcardAreaItem.length
|
||||
}
|
||||
|
||||
Item { width: 5 }
|
||||
|
||||
Connections {
|
||||
target: handcardAreaItem
|
||||
function onCardSelected(cardId, selected) {
|
||||
|
@ -54,6 +68,49 @@ RowLayout {
|
|||
handcardAreaItem.unselectAll(expectId);
|
||||
}
|
||||
|
||||
function expandPile(pile) {
|
||||
let expanded_pile_names = Object.keys(expanded_piles);
|
||||
if (expanded_pile_names.indexOf(pile) !== -1)
|
||||
return;
|
||||
|
||||
let component = Qt.createComponent("CardItem.qml");
|
||||
let parentPos = roomScene.mapFromItem(selfPhoto, 0, 0);
|
||||
|
||||
// FIXME: only expand equip area here. modify this if need true pile
|
||||
expanded_piles[pile] = [];
|
||||
if (pile === "_equip") {
|
||||
let equips = selfPhoto.equipArea.getAllCards();
|
||||
equips.forEach(data => {
|
||||
data.x = parentPos.x;
|
||||
data.y = parentPos.y;
|
||||
let card = component.createObject(roomScene, data);
|
||||
handcardAreaItem.add(card);
|
||||
})
|
||||
handcardAreaItem.updateCardPosition();
|
||||
}
|
||||
}
|
||||
|
||||
function retractPile(pile) {
|
||||
let expanded_pile_names = Object.keys(expanded_piles);
|
||||
if (expanded_pile_names.indexOf(pile) === -1)
|
||||
return;
|
||||
|
||||
let parentPos = roomScene.mapFromItem(selfPhoto, 0, 0);
|
||||
|
||||
delete expanded_piles[pile];
|
||||
if (pile === "_equip") {
|
||||
let equips = selfPhoto.equipArea.getAllCards();
|
||||
equips.forEach(data => {
|
||||
let card = handcardAreaItem.remove([data.cid])[0];
|
||||
card.origX = parentPos.x;
|
||||
card.origY = parentPos.y;
|
||||
card.destroyOnStop();
|
||||
card.goBack(true);
|
||||
})
|
||||
handcardAreaItem.updateCardPosition();
|
||||
}
|
||||
}
|
||||
|
||||
function enableCards() {
|
||||
// TODO: expand pile
|
||||
let ids = [], cards = handcardAreaItem.cards;
|
||||
|
@ -62,6 +119,9 @@ RowLayout {
|
|||
ids.push(cards[i].cid);
|
||||
}
|
||||
handcardAreaItem.enableCards(ids)
|
||||
if (pending_skill === "") {
|
||||
cancelButton.enabled = false;
|
||||
}
|
||||
}
|
||||
|
||||
function selectCard(cardId, selected) {
|
||||
|
@ -100,23 +160,21 @@ RowLayout {
|
|||
if (pending_skill === "") return;
|
||||
|
||||
let enabled_cards = [];
|
||||
let targets = roomScene.selected_targets;
|
||||
|
||||
handcardAreaItem.cards.forEach(function(card) {
|
||||
if (card.selected || Router.vs_view_filter(pending_skill, pendings, card.cid))
|
||||
handcardAreaItem.cards.forEach((card) => {
|
||||
if (card.selected || JSON.parse(Backend.callLuaFunction(
|
||||
"ActiveCardFilter",
|
||||
[pending_skill, card.cid, pendings, targets]
|
||||
)))
|
||||
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)) {
|
||||
if (JSON.parse(Backend.callLuaFunction(
|
||||
"CanViewAs",
|
||||
[pending_skill, pendings]
|
||||
))) {
|
||||
pending_card = {
|
||||
skill: pending_skill,
|
||||
subcards: pendings
|
||||
|
@ -141,8 +199,8 @@ RowLayout {
|
|||
}
|
||||
|
||||
function deactivateSkillButton() {
|
||||
for (let i = 0; i < headSkills.length; i++) {
|
||||
headSkillButtons.itemAt(i).pressed = false;
|
||||
for (let i = 0; i < skillButtons.count; i++) {
|
||||
skillButtons.itemAt(i).pressed = false;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -152,17 +210,31 @@ RowLayout {
|
|||
|
||||
// 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;
|
||||
}
|
||||
}
|
||||
// TODO: equipment
|
||||
|
||||
pendings = [];
|
||||
handcardAreaItem.adjustCards();
|
||||
handcardAreaItem.unselectAll();
|
||||
cardSelected(-1);
|
||||
}
|
||||
|
||||
function addSkill(skill_name) {
|
||||
skillPanel.addSkill(skill_name);
|
||||
}
|
||||
|
||||
function loseSkill(skill_name) {
|
||||
skillPanel.loseSkill(skill_name);
|
||||
}
|
||||
|
||||
function enableSkills() {
|
||||
for (let i = 0; i < skillButtons.count; i++) {
|
||||
let item = skillButtons.itemAt(i);
|
||||
item.enabled = JSON.parse(Backend.callLuaFunction("ActiveCanUse", [item.orig]));
|
||||
}
|
||||
}
|
||||
|
||||
function disableSkills() {
|
||||
for (let i = 0; i < skillButtons.count; i++)
|
||||
skillButtons.itemAt(i).enabled = false;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import QtQuick 2.15
|
||||
import QtQuick
|
||||
import "../skin-bank.js" as SkinBank
|
||||
|
||||
/* Layout of general card:
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import QtQuick 2.15
|
||||
import QtGraphicalEffects 1.0
|
||||
import QtQuick
|
||||
import Qt5Compat.GraphicalEffects
|
||||
|
||||
Item {
|
||||
property alias text: textItem.text
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import QtQuick 2.15
|
||||
import QtGraphicalEffects 1.0
|
||||
import QtQuick
|
||||
import Qt5Compat.GraphicalEffects
|
||||
|
||||
Item {
|
||||
property alias title: titleItem
|
||||
|
@ -22,7 +22,7 @@ Item {
|
|||
anchors.fill: background
|
||||
color: "#B0000000"
|
||||
radius: 5
|
||||
samples: 12
|
||||
//samples: 12
|
||||
spread: 0.2
|
||||
horizontalOffset: 5
|
||||
verticalOffset: 4
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import QtQuick 2.15
|
||||
import QtQuick
|
||||
import "../../util.js" as Utility
|
||||
|
||||
Item {
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import QtQuick 2.15
|
||||
import QtQuick
|
||||
|
||||
Item {
|
||||
property point start: Qt.point(0, 0)
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import QtQuick 2.15
|
||||
import QtQuick
|
||||
|
||||
Item {
|
||||
property var cards: []
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import QtQuick 2.15
|
||||
import QtGraphicalEffects 1.15
|
||||
import QtQuick.Controls 2.15
|
||||
import QtQuick
|
||||
import Qt5Compat.GraphicalEffects
|
||||
import QtQuick.Controls
|
||||
import "PhotoElement"
|
||||
import "../skin-bank.js" as SkinBank
|
||||
|
||||
|
@ -8,7 +8,7 @@ Item {
|
|||
id: root
|
||||
width: 175
|
||||
height: 233
|
||||
scale: 0.8
|
||||
scale: 0.75
|
||||
property int playerid
|
||||
property string general: ""
|
||||
property string screenName: ""
|
||||
|
@ -282,7 +282,7 @@ Item {
|
|||
glow.color: "brown"
|
||||
glow.spread: 0.2
|
||||
glow.radius: 8
|
||||
glow.samples: 12
|
||||
//glow.samples: 12
|
||||
}
|
||||
|
||||
SequentialAnimation {
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
|
||||
import QtQuick 2.15
|
||||
import QtQuick
|
||||
import ".."
|
||||
import "../../skin-bank.js" as SkinBank
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import QtQuick 2.15
|
||||
import QtQuick
|
||||
import ".."
|
||||
import "../../skin-bank.js" as SkinBank
|
||||
|
||||
|
@ -116,5 +116,9 @@ Column {
|
|||
{
|
||||
area.updateCardPosition(animated);
|
||||
}
|
||||
|
||||
function getAllCards() {
|
||||
return area.cards;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import QtQuick 2.15
|
||||
import QtQuick
|
||||
import ".."
|
||||
import "../../../util.js" as Utility
|
||||
import "../../skin-bank.js" as SkinBank
|
||||
|
@ -40,20 +40,16 @@ Item {
|
|||
glow.color: "black"
|
||||
glow.spread: 0.75
|
||||
glow.radius: 2
|
||||
glow.samples: 4
|
||||
//glow.samples: 4
|
||||
x: parent.width - 24
|
||||
y: 1
|
||||
}
|
||||
|
||||
GlowText {
|
||||
Text {
|
||||
id: textItem
|
||||
font.family: fontLibian.name
|
||||
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
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import QtQuick 2.15
|
||||
import QtQuick
|
||||
import ".."
|
||||
|
||||
Column {
|
||||
|
@ -36,7 +36,7 @@ Column {
|
|||
glow.color: "#3E3F47"
|
||||
glow.spread: 0.8
|
||||
glow.radius: 8
|
||||
glow.samples: 12
|
||||
//glow.samples: 12
|
||||
}
|
||||
|
||||
GlowText {
|
||||
|
@ -51,7 +51,7 @@ Column {
|
|||
glow.color: hpItem.glow.color
|
||||
glow.spread: hpItem.glow.spread
|
||||
glow.radius: hpItem.glow.radius
|
||||
glow.samples: hpItem.glow.samples
|
||||
//glow.samples: hpItem.glow.samples
|
||||
}
|
||||
|
||||
GlowText {
|
||||
|
@ -65,7 +65,7 @@ Column {
|
|||
glow.color: hpItem.glow.color
|
||||
glow.spread: hpItem.glow.spread
|
||||
glow.radius: hpItem.glow.radius
|
||||
glow.samples: hpItem.glow.samples
|
||||
//glow.samples: hpItem.glow.samples
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import QtQuick 2.15
|
||||
import QtQuick
|
||||
import "../../skin-bank.js" as SkinBank
|
||||
|
||||
Image {
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
|
||||
import QtQuick 2.15
|
||||
import QtQuick
|
||||
import "../../skin-bank.js" as SkinBank
|
||||
|
||||
Image {
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import QtQuick 2.15
|
||||
import Qt.labs.folderlistmodel 2.15
|
||||
import QtQuick
|
||||
import Qt.labs.folderlistmodel
|
||||
import "../skin-bank.js" as SkinBank
|
||||
|
||||
Item {
|
||||
|
|
188
qml/Pages/RoomElement/PlayerCardBox.qml
Normal file
188
qml/Pages/RoomElement/PlayerCardBox.qml
Normal file
|
@ -0,0 +1,188 @@
|
|||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
|
||||
|
||||
GraphicsBox {
|
||||
signal cardSelected(int cid)
|
||||
|
||||
id: root
|
||||
title.text: Backend.translate("$ChooseCard")
|
||||
//@to-do: Adjust the UI design in case there are more than 7 cards
|
||||
width: 70 + Math.min(7, Math.max(1, handcards.count, equips.count, delayedTricks.count)) * 100
|
||||
height: 50 + (handcards.count > 0 ? 150 : 0) + (equips.count > 0 ? 150 : 0) + (delayedTricks.count > 0 ? 150 : 0)
|
||||
|
||||
ListModel {
|
||||
id: handcards
|
||||
}
|
||||
|
||||
ListModel {
|
||||
id: equips
|
||||
}
|
||||
|
||||
ListModel {
|
||||
id: delayedTricks
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
anchors.fill: parent
|
||||
anchors.topMargin: 40
|
||||
anchors.leftMargin: 20
|
||||
anchors.rightMargin: 20
|
||||
anchors.bottomMargin: 20
|
||||
|
||||
Row {
|
||||
height: 130
|
||||
spacing: 15
|
||||
visible: handcards.count > 0
|
||||
|
||||
Rectangle {
|
||||
border.color: "#A6967A"
|
||||
radius: 5
|
||||
color: "transparent"
|
||||
width: 18
|
||||
height: parent.height
|
||||
|
||||
Text {
|
||||
color: "#E4D5A0"
|
||||
text: Backend.translate("$Hand")
|
||||
anchors.fill: parent
|
||||
wrapMode: Text.WrapAnywhere
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
font.pixelSize: 15
|
||||
}
|
||||
}
|
||||
|
||||
Row {
|
||||
spacing: 7
|
||||
Repeater {
|
||||
model: handcards
|
||||
|
||||
CardItem {
|
||||
name: "card-back"
|
||||
cid: -1
|
||||
suit: ""
|
||||
number: 0
|
||||
autoBack: false
|
||||
selectable: true
|
||||
onClicked: root.cardSelected(cid);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Row {
|
||||
height: 130
|
||||
spacing: 15
|
||||
visible: equips.count > 0
|
||||
|
||||
Rectangle {
|
||||
border.color: "#A6967A"
|
||||
radius: 5
|
||||
color: "transparent"
|
||||
width: 18
|
||||
height: parent.height
|
||||
|
||||
Text {
|
||||
color: "#E4D5A0"
|
||||
text: Backend.translate("$Equip")
|
||||
anchors.fill: parent
|
||||
wrapMode: Text.WrapAnywhere
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
font.pixelSize: 15
|
||||
}
|
||||
}
|
||||
|
||||
Row {
|
||||
spacing: 7
|
||||
Repeater {
|
||||
model: equips
|
||||
|
||||
CardItem {
|
||||
cid: model.cid
|
||||
name: model.name
|
||||
suit: model.suit
|
||||
number: model.number
|
||||
autoBack: false
|
||||
selectable: true
|
||||
onClicked: root.cardSelected(cid);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Row {
|
||||
height: 130
|
||||
spacing: 15
|
||||
visible: delayedTricks.count > 0
|
||||
|
||||
Rectangle {
|
||||
border.color: "#A6967A"
|
||||
radius: 5
|
||||
color: "transparent"
|
||||
width: 18
|
||||
height: parent.height
|
||||
|
||||
Text {
|
||||
color: "#E4D5A0"
|
||||
text: Backend.translate("$Judge")
|
||||
anchors.fill: parent
|
||||
wrapMode: Text.WrapAnywhere
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
font.pixelSize: 15
|
||||
}
|
||||
}
|
||||
|
||||
Row {
|
||||
spacing: 7
|
||||
Repeater {
|
||||
model: delayedTricks
|
||||
|
||||
CardItem {
|
||||
cid: model.cid
|
||||
name: model.name
|
||||
suit: model.suit
|
||||
number: model.number
|
||||
autoBack: false
|
||||
selectable: true
|
||||
onClicked: root.cardSelected(cid);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
onCardSelected: finished();
|
||||
|
||||
function addHandcards(cards)
|
||||
{
|
||||
if (cards instanceof Array) {
|
||||
for (var i = 0; i < cards.length; i++)
|
||||
handcards.append(cards[i]);
|
||||
} else {
|
||||
handcards.append(cards);
|
||||
}
|
||||
}
|
||||
|
||||
function addEquips(cards)
|
||||
{
|
||||
if (cards instanceof Array) {
|
||||
for (var i = 0; i < cards.length; i++)
|
||||
equips.append(cards[i]);
|
||||
} else {
|
||||
equips.append(cards);
|
||||
}
|
||||
}
|
||||
|
||||
function addDelayedTricks(cards)
|
||||
{
|
||||
if (cards instanceof Array) {
|
||||
for (var i = 0; i < cards.length; i++)
|
||||
delayedTricks.append(cards[i]);
|
||||
} else {
|
||||
delayedTricks.append(cards);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,5 +1,95 @@
|
|||
import QtQuick 2.15
|
||||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
|
||||
Flickable {
|
||||
id: root
|
||||
property alias skill_buttons: skill_buttons
|
||||
|
||||
clip: true
|
||||
contentWidth: panel.width
|
||||
contentHeight: panel.height
|
||||
width: panel.width
|
||||
height: Math.min(180, panel.height)
|
||||
flickableDirection: Flickable.AutoFlickIfNeeded
|
||||
|
||||
ListModel {
|
||||
id: active_skills
|
||||
}
|
||||
|
||||
ListModel {
|
||||
id: not_active_skills
|
||||
}
|
||||
|
||||
Item {
|
||||
id: panel
|
||||
width: Math.max(grid1.width, grid2.width)
|
||||
height: grid1.height + grid2.height
|
||||
Grid {
|
||||
id: grid1
|
||||
columns: 2
|
||||
columnSpacing: 2
|
||||
rowSpacing: 2
|
||||
Repeater {
|
||||
id: skill_buttons
|
||||
model: active_skills
|
||||
onItemAdded: parent.forceLayout()
|
||||
SkillButton {
|
||||
skill: model.skill
|
||||
type: "active"
|
||||
enabled: false
|
||||
orig: model.orig_skill
|
||||
|
||||
onPressedChanged: {
|
||||
if (enabled)
|
||||
roomScene.activateSkill(orig, pressed);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Grid {
|
||||
id: grid2
|
||||
anchors.top: grid1.bottom
|
||||
anchors.topMargin: 2
|
||||
columns: 3
|
||||
columnSpacing: 2
|
||||
rowSpacing: 2
|
||||
Repeater {
|
||||
model: not_active_skills
|
||||
onItemAdded: parent.forceLayout()
|
||||
SkillButton {
|
||||
skill: model.skill
|
||||
orig: model.orig_skill
|
||||
type: "notactive"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function addSkill(skill_name) {
|
||||
let data = JSON.parse(Backend.callLuaFunction(
|
||||
"GetSkillData",
|
||||
[skill_name]
|
||||
));
|
||||
if (data.freq = "active") {
|
||||
active_skills.append(data);
|
||||
} else {
|
||||
not_active_skills.append(data);
|
||||
}
|
||||
}
|
||||
|
||||
function loseSkill(skill_name) {
|
||||
for (let i = 0; i < active_skills.count; i++) {
|
||||
let item = active_skills.at(i);
|
||||
if (item.skill == skill_name) {
|
||||
active_skills.remove(i);
|
||||
}
|
||||
}
|
||||
for (let i = 0; i < not_active_skills.count; i++) {
|
||||
let item = not_active_skills.at(i);
|
||||
if (item.skill == skill_name) {
|
||||
not_active_skills.remove(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
60
qml/Pages/RoomElement/SkillButton.qml
Normal file
60
qml/Pages/RoomElement/SkillButton.qml
Normal file
|
@ -0,0 +1,60 @@
|
|||
import QtQuick
|
||||
import Qt5Compat.GraphicalEffects
|
||||
|
||||
Item {
|
||||
id: root
|
||||
property alias skill: skill.text
|
||||
property string type: "active"
|
||||
property string orig: ""
|
||||
property bool pressed: false
|
||||
|
||||
onEnabledChanged: {
|
||||
if (!enabled)
|
||||
pressed = false;
|
||||
}
|
||||
|
||||
width: type === "active" ? 120 * 0.66 : 72 * 0.66
|
||||
height: type === "active" ? 55 * 0.66 : 36 * 0.66
|
||||
|
||||
Image {
|
||||
x: -13 - 120 * 0.166
|
||||
y: -6 - 55 * 0.166
|
||||
scale: 0.66
|
||||
source: type !== "active" ? ""
|
||||
: AppPath + "/image/button/skill/active/"
|
||||
+ (enabled ? (pressed ? "pressed" : "normal") : "disabled")
|
||||
}
|
||||
|
||||
Text {
|
||||
anchors.centerIn: parent
|
||||
id: skill
|
||||
font.family: fontLi2.name
|
||||
font.pixelSize: 36 * 0.66
|
||||
visible: false
|
||||
}
|
||||
|
||||
Glow {
|
||||
id: glowItem
|
||||
source: skill
|
||||
anchors.fill: skill
|
||||
radius: 6
|
||||
//samples: 8
|
||||
color: "grey"
|
||||
}
|
||||
|
||||
LinearGradient {
|
||||
anchors.fill: skill
|
||||
source: skill
|
||||
gradient: Gradient {
|
||||
GradientStop { position: 0; color: "#FFE07C" }
|
||||
GradientStop { position: 1; color: "#B79A5F" }
|
||||
}
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
id: mouseArea
|
||||
anchors.fill: parent
|
||||
enabled: root.type === "active" && root.enabled
|
||||
onClicked: parent.pressed = !parent.pressed;
|
||||
}
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
import QtQuick 2.15
|
||||
import QtQuick
|
||||
|
||||
Item {
|
||||
property var discardedCards: []
|
||||
|
|
|
@ -19,21 +19,24 @@ function arrangePhotos() {
|
|||
* +---------------+
|
||||
*/
|
||||
|
||||
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 * 0.75;
|
||||
// Padding is negative, because photos are scaled.
|
||||
const roomAreaPadding = -16;
|
||||
const verticalPadding = -175 / 8;
|
||||
const horizontalSpacing = 32;
|
||||
let verticalSpacing = (roomArea.width - photoWidth * 7) / 8;
|
||||
|
||||
// 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 },
|
||||
let startX = verticalPadding + verticalSpacing;
|
||||
let padding = photoWidth + verticalSpacing;
|
||||
let regions = [
|
||||
{ x: startX + padding * 6, y: roomAreaPadding + horizontalSpacing * 3 },
|
||||
{ x: startX + padding * 5, y: roomAreaPadding + horizontalSpacing },
|
||||
{ x: startX + padding * 4, y: roomAreaPadding },
|
||||
{ x: startX + padding * 3, y: roomAreaPadding },
|
||||
{ x: startX + padding * 2, y: roomAreaPadding },
|
||||
{ x: startX + padding, y: roomAreaPadding + horizontalSpacing },
|
||||
{ x: startX, y: roomAreaPadding + horizontalSpacing * 3 },
|
||||
];
|
||||
|
||||
const regularSeatIndex = [
|
||||
|
@ -61,7 +64,7 @@ function arrangePhotos() {
|
|||
}
|
||||
|
||||
function doOkButton() {
|
||||
if (roomScene.state == "playing") {
|
||||
if (roomScene.state == "playing" || roomScene.state == "responding") {
|
||||
replyToServer(JSON.stringify(
|
||||
{
|
||||
card: dashboard.getSelectedCard(),
|
||||
|
@ -74,6 +77,20 @@ function doOkButton() {
|
|||
}
|
||||
|
||||
function doCancelButton() {
|
||||
if (roomScene.state == "playing") {
|
||||
dashboard.deactivateSkillButton();
|
||||
dashboard.unSelectAll();
|
||||
dashboard.stopPending();
|
||||
dashboard.enableCards();
|
||||
return;
|
||||
} else if (roomScene.state == "responding") {
|
||||
dashboard.deactivateSkillButton();
|
||||
dashboard.unSelectAll();
|
||||
dashboard.stopPending();
|
||||
replyToServer("");
|
||||
return;
|
||||
}
|
||||
|
||||
replyToServer("");
|
||||
}
|
||||
|
||||
|
@ -271,6 +288,7 @@ function enableTargets(card) { // card: int | { skill: string, subcards: int[] }
|
|||
function updateSelectedTargets(playerid, selected) {
|
||||
let i = 0;
|
||||
let card = dashboard.getSelectedCard();
|
||||
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))
|
||||
|
@ -282,19 +300,28 @@ function updateSelectedTargets(playerid, selected) {
|
|||
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;
|
||||
})
|
||||
if (candidate) {
|
||||
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]
|
||||
));
|
||||
okButton.enabled = JSON.parse(Backend.callLuaFunction(
|
||||
"CardFeasible", [card, selected_targets]
|
||||
));
|
||||
} else {
|
||||
all_photos.forEach(photo => {
|
||||
photo.state = "normal";
|
||||
photo.selected = false;
|
||||
});
|
||||
|
||||
okButton.enabled = false;
|
||||
}
|
||||
}
|
||||
|
||||
callbacks["RemovePlayer"] = function(jsonData) {
|
||||
|
@ -469,6 +496,47 @@ callbacks["AskForChoice"] = function(jsonData) {
|
|||
});
|
||||
}
|
||||
|
||||
callbacks["AskForCardChosen"] = function(jsonData) {
|
||||
// jsonData: [ int[] handcards, int[] equips, int[] delayedtricks,
|
||||
// string reason ]
|
||||
let data = JSON.parse(jsonData);
|
||||
let handcard_ids = data[0];
|
||||
let equip_ids = data[1];
|
||||
let delayedTrick_ids = data[2];
|
||||
let reason = data[3];
|
||||
let handcards = [];
|
||||
let equips = [];
|
||||
let delayedTricks = [];
|
||||
|
||||
handcard_ids.forEach(id => {
|
||||
let card_data = JSON.parse(Backend.callLuaFunction("GetCardData", [id]));
|
||||
handcards.push(card_data);
|
||||
});
|
||||
|
||||
equip_ids.forEach(id => {
|
||||
let card_data = JSON.parse(Backend.callLuaFunction("GetCardData", [id]));
|
||||
equips.push(card_data);
|
||||
});
|
||||
|
||||
delayedTrick_ids.forEach(id => {
|
||||
let card_data = JSON.parse(Backend.callLuaFunction("GetCardData", [id]));
|
||||
delayedTricks.push(card_data);
|
||||
});
|
||||
|
||||
roomScene.promptText = Backend.translate("#AskForChooseCard")
|
||||
.arg(Backend.translate(reason));
|
||||
roomScene.state = "replying";
|
||||
roomScene.popupBox.source = "RoomElement/PlayerCardBox.qml";
|
||||
let box = roomScene.popupBox.item;
|
||||
box.addHandcards(handcards);
|
||||
box.addEquips(equips);
|
||||
box.addDelayedTricks(delayedTricks);
|
||||
roomScene.popupBox.moveToCenter();
|
||||
box.cardSelected.connect(function(cid){
|
||||
replyToServer(cid);
|
||||
});
|
||||
}
|
||||
|
||||
callbacks["MoveCards"] = function(jsonData) {
|
||||
// jsonData: merged moves
|
||||
let moves = JSON.parse(jsonData);
|
||||
|
@ -481,5 +549,26 @@ callbacks["PlayCard"] = function(jsonData) {
|
|||
if (playerId == Self.id) {
|
||||
roomScene.promptText = Backend.translate("#PlayCard");
|
||||
roomScene.state = "playing";
|
||||
okButton.enabled = false;
|
||||
}
|
||||
}
|
||||
|
||||
callbacks["LoseSkill"] = function(jsonData) {
|
||||
// jsonData: [ int player_id, string skill_name ]
|
||||
let data = JSON.parse(jsonData);
|
||||
let id = data[0];
|
||||
let skill_name = data[1];
|
||||
if (id === Self.id) {
|
||||
dashboard.loseSkill(skill_name);
|
||||
}
|
||||
}
|
||||
|
||||
callbacks["AddSkill"] = function(jsonData) {
|
||||
// jsonData: [ int player_id, string skill_name ]
|
||||
let data = JSON.parse(jsonData);
|
||||
let id = data[0];
|
||||
let skill_name = data[1];
|
||||
if (id === Self.id) {
|
||||
dashboard.addSkill(skill_name);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import QtQuick 2.15
|
||||
import QtQuick
|
||||
|
||||
Rectangle {
|
||||
function show(text, duration) {
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import QtQuick 2.15
|
||||
import QtQuick
|
||||
|
||||
// copy from https://gist.github.com/jonmcclung/bae669101d17b103e94790341301c129
|
||||
// and modified some code
|
||||
|
|
21
qml/main.qml
21
qml/main.qml
|
@ -1,16 +1,24 @@
|
|||
import QtQuick 2.15
|
||||
import QtQuick.Controls 2.0
|
||||
import QtQuick.Window 2.15
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Window
|
||||
import "Logic.js" as Logic
|
||||
import "Pages"
|
||||
|
||||
Window {
|
||||
id: mainWindow
|
||||
visible: true
|
||||
width: 720
|
||||
height: 480
|
||||
width: 960
|
||||
height: 540
|
||||
property var callbacks: Logic.callbacks
|
||||
|
||||
Item {
|
||||
id: mainWindow
|
||||
width: (parent.width / parent.height < 960 / 540)
|
||||
? 960 : 540 * parent.width / parent.height
|
||||
height: (parent.width / parent.height > 960 / 540)
|
||||
? 540 : 960 * parent.height / parent.width
|
||||
scale: parent.width / width
|
||||
anchors.centerIn: parent
|
||||
|
||||
Image {
|
||||
source: AppPath + "/image/background"
|
||||
anchors.fill: parent
|
||||
|
@ -130,6 +138,7 @@ Window {
|
|||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
onClosing: {
|
||||
Backend.quitLobby();
|
||||
|
|
|
@ -31,21 +31,28 @@ set(freekill_HEADERS
|
|||
if (WIN32)
|
||||
set(LUA_LIB ${PROJECT_SOURCE_DIR}/lib/win/lua54.dll)
|
||||
set(SQLITE3_LIB ${PROJECT_SOURCE_DIR}/lib/win/sqlite3.dll)
|
||||
elseif (ANDROID)
|
||||
set(LUA_LIB ${PROJECT_SOURCE_DIR}/lib/android/liblua54.so)
|
||||
set(SQLITE3_LIB ${PROJECT_SOURCE_DIR}/lib/android/libsqlite3.so)
|
||||
set_target_properties(FreeKill PROPERTIES
|
||||
QT_ANDROID_PACKAGE_SOURCE_DIR ${PROJECT_SOURCE_DIR}/android
|
||||
QT_ANDROID_EXTRA_LIBS "${LUA_LIB};${SQLITE3_LIB}"
|
||||
)
|
||||
else ()
|
||||
set(LUA_LIB lua5.4)
|
||||
set(SQLITE3_LIB sqlite3)
|
||||
endif ()
|
||||
|
||||
source_group("Include" FILES ${freekill_HEADERS})
|
||||
add_executable(FreeKill ${freekill_SRCS})
|
||||
|
||||
target_sources(FreeKill PRIVATE ${freekill_SRCS})
|
||||
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
|
||||
target_link_libraries(FreeKill PRIVATE
|
||||
${LUA_LIB}
|
||||
${SQLITE3_LIB}
|
||||
fkparse
|
||||
Qt6::Qml
|
||||
Qt6::Gui
|
||||
Qt6::Network
|
||||
Qt6::Multimedia
|
||||
)
|
||||
|
|
34
src/main.cpp
34
src/main.cpp
|
@ -1,12 +1,46 @@
|
|||
#include "qmlbackend.h"
|
||||
#include "server.h"
|
||||
|
||||
#ifdef Q_OS_ANDROID
|
||||
static bool copyPath(const QString &srcFilePath, const QString &tgtFilePath)
|
||||
{
|
||||
QFileInfo srcFileInfo(srcFilePath);
|
||||
if (srcFileInfo.isDir()) {
|
||||
QDir targetDir(tgtFilePath);
|
||||
if (!targetDir.exists()) {
|
||||
targetDir.cdUp();
|
||||
if (!targetDir.mkdir(QFileInfo(tgtFilePath).fileName()))
|
||||
return false;
|
||||
}
|
||||
QDir sourceDir(srcFilePath);
|
||||
QStringList fileNames = sourceDir.entryList(QDir::Files | QDir::Dirs | QDir::NoDotAndDotDot | QDir::Hidden | QDir::System);
|
||||
foreach (const QString &fileName, fileNames) {
|
||||
const QString newSrcFilePath
|
||||
= srcFilePath + QLatin1Char('/') + fileName;
|
||||
const QString newTgtFilePath
|
||||
= tgtFilePath + QLatin1Char('/') + fileName;
|
||||
if (!copyPath(newSrcFilePath, newTgtFilePath))
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
QFile::remove(tgtFilePath);
|
||||
if (!QFile::copy(srcFilePath, tgtFilePath))
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
QCoreApplication *app;
|
||||
QCoreApplication::setApplicationName("FreeKill");
|
||||
QCoreApplication::setApplicationVersion("Alpha 0.0.1");
|
||||
|
||||
#ifdef Q_OS_ANDROID
|
||||
copyPath("assets:/res", QDir::currentPath());
|
||||
#endif
|
||||
|
||||
QCommandLineParser parser;
|
||||
parser.setApplicationDescription("FreeKill server");
|
||||
parser.addHelpOption();
|
||||
|
|
|
@ -165,13 +165,13 @@ void Server::handleNameAndPassword(ClientSocket *client, const QString& name, co
|
|||
{
|
||||
// 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]+");
|
||||
QRegularExpression nameExp("[\\000-\\057\\072-\\100\\133-\\140\\173-\\177]");
|
||||
QByteArray passwordHash = QCryptographicHash::hash(password.toLatin1(), QCryptographicHash::Sha256).toHex();
|
||||
bool passed = false;
|
||||
QString error_msg;
|
||||
QJsonObject result;
|
||||
|
||||
if (nameExp.exactMatch(name)) {
|
||||
if (!nameExp.match(name).hasMatch()) {
|
||||
// Then we check the database,
|
||||
QString sql_find = QString("SELECT * FROM userinfo \
|
||||
WHERE name='%1';").arg(name);
|
||||
|
|
|
@ -9,6 +9,8 @@ public:
|
|||
static QString pwd();
|
||||
static bool exists(const QString &file);
|
||||
static bool isDir(const QString &file);
|
||||
|
||||
void parseFkp(const QString &file);
|
||||
};
|
||||
|
||||
extern QmlBackend *Backend;
|
||||
|
|
|
@ -9,6 +9,13 @@ QmlBackend::QmlBackend(QObject* parent)
|
|||
{
|
||||
Backend = this;
|
||||
engine = nullptr;
|
||||
parser = fkp_new_parser();
|
||||
}
|
||||
|
||||
QmlBackend::~QmlBackend()
|
||||
{
|
||||
Backend = nullptr;
|
||||
fkp_close(parser);
|
||||
}
|
||||
|
||||
QQmlApplicationEngine *QmlBackend::getEngine() const
|
||||
|
@ -104,21 +111,21 @@ QString QmlBackend::translate(const QString &src) {
|
|||
|
||||
void QmlBackend::pushLuaValue(lua_State *L, QVariant v) {
|
||||
QVariantList list;
|
||||
switch(v.type()) {
|
||||
case QVariant::Bool:
|
||||
switch (v.typeId()) {
|
||||
case QMetaType::Bool:
|
||||
lua_pushboolean(L, v.toBool());
|
||||
break;
|
||||
case QVariant::Int:
|
||||
case QVariant::UInt:
|
||||
case QMetaType::Int:
|
||||
case QMetaType::UInt:
|
||||
lua_pushinteger(L, v.toInt());
|
||||
break;
|
||||
case QVariant::Double:
|
||||
case QMetaType::Double:
|
||||
lua_pushnumber(L, v.toDouble());
|
||||
break;
|
||||
case QVariant::String:
|
||||
case QMetaType::QString:
|
||||
lua_pushstring(L, v.toString().toUtf8().data());
|
||||
break;
|
||||
case QVariant::List:
|
||||
case QMetaType::QVariantList:
|
||||
lua_newtable(L);
|
||||
list = v.toList();
|
||||
for (int i = 1; i <= list.length(); i++) {
|
||||
|
@ -128,7 +135,7 @@ void QmlBackend::pushLuaValue(lua_State *L, QVariant v) {
|
|||
}
|
||||
break;
|
||||
default:
|
||||
qDebug() << "cannot handle QVariant type" << v.type();
|
||||
qDebug() << "cannot handle QVariant type" << v.typeId();
|
||||
lua_pushnil(L);
|
||||
break;
|
||||
}
|
||||
|
@ -154,3 +161,57 @@ QString QmlBackend::callLuaFunction(const QString &func_name,
|
|||
lua_pop(L, 1);
|
||||
return QString(result);
|
||||
}
|
||||
|
||||
void QmlBackend::parseFkp(const QString &fileName) {
|
||||
if (!QFile::exists(fileName)) {
|
||||
// errorEdit->setText(tr("File does not exist!"));
|
||||
return;
|
||||
}
|
||||
QString cwd = QDir::currentPath();
|
||||
|
||||
QStringList strlist = fileName.split('/');
|
||||
QString shortFileName = strlist.last();
|
||||
strlist.removeLast();
|
||||
QString path = strlist.join('/');
|
||||
QDir::setCurrent(path);
|
||||
|
||||
bool error = fkp_parse(
|
||||
parser,
|
||||
shortFileName.toUtf8().data(),
|
||||
FKP_QSAN_LUA
|
||||
);
|
||||
/* setError(error);
|
||||
|
||||
if (error) {
|
||||
QStringList tmplist = shortFileName.split('.');
|
||||
tmplist.removeLast();
|
||||
QString fName = tmplist.join('.') + "-error.txt";
|
||||
if (!QFile::exists(fName)) {
|
||||
errorEdit->setText(tr("Unknown compile error."));
|
||||
} else {
|
||||
QFile f(fName);
|
||||
f.open(QIODevice::ReadOnly);
|
||||
errorEdit->setText(f.readAll());
|
||||
f.remove();
|
||||
}
|
||||
} else {
|
||||
errorEdit->setText(tr("Successfully compiled chosen file."));
|
||||
}
|
||||
*/
|
||||
QDir::setCurrent(cwd);
|
||||
}
|
||||
|
||||
static void copyFkpHash2QHash(QHash<QString, QString> &dst, fkp_hash *from) {
|
||||
dst.clear();
|
||||
for (size_t i = 0; i < from->capacity; i++) {
|
||||
if (from->entries[i].key != NULL) {
|
||||
dst[from->entries[i].key] = QString((const char *)from->entries[i].value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void QmlBackend::readHashFromParser() {
|
||||
copyFkpHash2QHash(generals, parser->generals);
|
||||
copyFkpHash2QHash(skills, parser->skills);
|
||||
copyFkpHash2QHash(marks, parser->marks);
|
||||
}
|
||||
|
|
|
@ -1,10 +1,13 @@
|
|||
#ifndef _QMLBACKEND_H
|
||||
#define _QMLBACKEND_H
|
||||
|
||||
#include "fkparse.h"
|
||||
|
||||
class QmlBackend : public QObject {
|
||||
Q_OBJECT
|
||||
public:
|
||||
QmlBackend(QObject *parent = nullptr);
|
||||
~QmlBackend();
|
||||
|
||||
QQmlApplicationEngine *getEngine() const;
|
||||
void setEngine(QQmlApplicationEngine *engine);
|
||||
|
@ -29,14 +32,21 @@ public:
|
|||
Q_INVOKABLE QString translate(const QString &src);
|
||||
Q_INVOKABLE QString callLuaFunction(const QString &func_name,
|
||||
QVariantList params);
|
||||
// support fkp
|
||||
Q_INVOKABLE void parseFkp(const QString &filename);
|
||||
|
||||
signals:
|
||||
void notifyUI(const QString &command, const QString &jsonData);
|
||||
|
||||
private:
|
||||
QQmlApplicationEngine *engine;
|
||||
fkp_parser *parser;
|
||||
QHash<QString, QString> generals;
|
||||
QHash<QString, QString> skills;
|
||||
QHash<QString, QString> marks;
|
||||
|
||||
void pushLuaValue(lua_State *L, QVariant v);
|
||||
void readHashFromParser();
|
||||
};
|
||||
|
||||
extern QmlBackend *Backend;
|
||||
|
|
Loading…
Reference in New Issue
Block a user