diff --git a/lua/core/engine.lua b/lua/core/engine.lua index 0b2047b7..e7cbe4d5 100644 --- a/lua/core/engine.lua +++ b/lua/core/engine.lua @@ -117,7 +117,7 @@ end ---@param generalPool General[] ---@param except string[] ---@param filter function ----@return General[] generals +---@return General[] function Engine:getGeneralsRandomly(num, generalPool, except, filter) if filter then assert(type(filter) == "function") diff --git a/lua/core/package.lua b/lua/core/package.lua index df66fea9..1e64b2c1 100644 --- a/lua/core/package.lua +++ b/lua/core/package.lua @@ -25,7 +25,7 @@ function Package:initialize(name, _type) self.cards = {} end ----@return table skills +---@return Skill[] function Package:getSkills() local ret = {table.unpack(self.related_skills)} if self.type == Package.GeneralPack then diff --git a/lua/core/player.lua b/lua/core/player.lua index c8e690a2..c56723e7 100644 --- a/lua/core/player.lua +++ b/lua/core/player.lua @@ -13,6 +13,9 @@ ---@field dead boolean ---@field state string ---@field player_skills Skill[] +---@field flag string[] +---@field tag table +---@field mark table local Player = class("Player") ---@alias Phase integer @@ -43,6 +46,9 @@ function Player:initialize() self.state = "" self.player_skills = {} + self.flag = {} + self.tag = {} + self.mark = {} end ---@param general General @@ -56,8 +62,65 @@ function Player:setGeneral(general, setHp, addSkills) end if addSkills then - table.insertTable(self.playerSkills, general.skills) + table.insertTable(self.player_skills, general.skills) end end +---@param flag string +function Player:hasFlag(flag) + return table.contains(self.flag, flag) +end + +---@param flag string +function Player:setFlag(flag) + if flag == "." then + self:clearFlags() + return + end + if flag:sub(1, 1) == "-" then + flag = flag:sub(2, #flag) + table.removeOne(self.flag, flag) + return + end + if not self:hasFlag(flag) then + table.insert(self.flag, flag) + end +end + +function Player:clearFlags() + self.flag = {} +end + +function Player:addMark(mark, count) + count = count or 1 + local num = self.mark[mark] + num = num or 0 + self:setMark(mark, math.max(num + count, 0)) +end + +function Player:removeMark(mark, count) + count = count or 1 + local num = self.mark[mark] + num = num or 0 + self:setMark(mark, math.max(num - count, 0)) +end + +function Player:setMark(mark, count) + if self.mark[mark] ~= count then + self.mark[mark] = count + end +end + +function Player:getMark(mark) + return (self.mark[mark] or 0) +end + +function Player:getMarkNames() + local ret = {} + for k, _ in pairs(self.mark) do + table.insert(ret, k) + end + return ret +end + return Player diff --git a/lua/core/skill_type/trigger.lua b/lua/core/skill_type/trigger.lua index 6dd76167..55924ab8 100644 --- a/lua/core/skill_type/trigger.lua +++ b/lua/core/skill_type/trigger.lua @@ -17,25 +17,24 @@ end -- Default functions ---Determine whether a skill can refresh at this moment ----@param event Event # TriggerEvent ----@param target ServerPlayer # Player who triggered this event ----@param player ServerPlayer # Player who is operating ----@param data any # useful data of the event ----@return nil +---@param event Event @ TriggerEvent +---@param target ServerPlayer @ Player who triggered this event +---@param player ServerPlayer @ Player who is operating +---@param data any @ useful data of the event function TriggerSkill:canRefresh(event, target, player, data) return false end ---Refresh the skill (e.g. clear marks) ----@param event Event # TriggerEvent ----@param target ServerPlayer # Player who triggered this event ----@param player ServerPlayer # Player who is operating ----@param data any # useful data of the event +---@param event Event @ TriggerEvent +---@param target ServerPlayer @ Player who triggered this event +---@param player ServerPlayer @ Player who is operating +---@param data any @ useful data of the event function TriggerSkill:refresh(event, target, player, data) end ---Determine whether a skill can trigger at this moment ----@param event Event # TriggerEvent ----@param target ServerPlayer # Player who triggered this event ----@param player ServerPlayer # Player who is operating ----@param data any # useful data of the event +---@param event Event @ TriggerEvent +---@param target ServerPlayer @ Player who triggered this event +---@param player ServerPlayer @ Player who is operating +---@param data any @ useful data of the event ---@return boolean function TriggerSkill:triggerable(event, target, player, data) return target and (target == player) @@ -43,11 +42,11 @@ function TriggerSkill:triggerable(event, target, player, data) end ---Trigger this skill ----@param event Event # TriggerEvent ----@param target ServerPlayer # Player who triggered this event ----@param player ServerPlayer # Player who is operating ----@param data any # useful data of the event ----@return boolean # returns true if trigger is broken +---@param event Event @ TriggerEvent +---@param target ServerPlayer @ Player who triggered this event +---@param player ServerPlayer @ Player who is operating +---@param data any @ useful data of the event +---@return boolean @ returns true if trigger is broken function TriggerSkill:trigger(event, target, player, data) if player.room:askForSkillInvoke(player, self.name) then return self:use(event, target, player, data) @@ -56,10 +55,10 @@ function TriggerSkill:trigger(event, target, player, data) end ---Use this skill ----@param event Event # TriggerEvent ----@param target ServerPlayer # Player who triggered this event ----@param player ServerPlayer # Player who is operating ----@param data any # useful data of the event +---@param event Event @ TriggerEvent +---@param target ServerPlayer @ Player who triggered this event +---@param player ServerPlayer @ Player who is operating +---@param data any @ useful data of the event ---@return boolean function TriggerSkill:use(event, target, player, data) end diff --git a/lua/core/util.lua b/lua/core/util.lua index 0a6227f0..26178b56 100644 --- a/lua/core/util.lua +++ b/lua/core/util.lua @@ -71,7 +71,7 @@ Sql = { --- Execute a `SELECT` SQL statement. ---@param db fk.SQLite3 ---@param sql string - ---@return table data # { [columnName] --> result : string[] } + ---@return table @ { [columnName] --> result : string[] } exec_select = function(db, sql) return json.decode(fk.SelectFromDb(db, sql)) end, @@ -127,3 +127,10 @@ function CreateEnum(table, enum) print(string.format(enum_format, table, v, i)) end end + +function switch(param, case_table) + local case = case_table[param] + if case then return case() end + local def = case_table["default"] + return def and def() or nil +end diff --git a/lua/fk_ex.lua b/lua/fk_ex.lua index 56cd5563..8de23dd7 100644 --- a/lua/fk_ex.lua +++ b/lua/fk_ex.lua @@ -8,7 +8,22 @@ EquipCard = require "core.card_type.equip" dofile "lua/server/event.lua" TriggerSkill = require "core.skill_type.trigger" ----@param spec table +---@class CardSpec: Card + +---@class SkillSpec: Skill + +---@alias TrigFunc fun(self: TriggerSkill, event: Event, target: ServerPlayer, player: ServerPlayer):boolean +---@class TriggerSkillSpec: SkillSpec +---@field global boolean +---@field events Event | Event[] +---@field refresh_events Event | Event[] +---@field priority number | table +---@field on_trigger TrigFunc +---@field can_trigger TrigFunc +---@field on_refresh TrigFunc +---@field can_refresh TrigFunc + +---@param spec CardSpec ---@return BasicCard function fk.CreateBasicCard(spec) assert(type(spec.name) == "string" or type(spec.class_name) == "string") @@ -21,7 +36,7 @@ function fk.CreateBasicCard(spec) return card end ----@param spec table +---@param spec CardSpec ---@return TrickCard function fk.CreateTrickCard(spec) assert(type(spec.name) == "string" or type(spec.class_name) == "string") @@ -34,7 +49,7 @@ function fk.CreateTrickCard(spec) return card end ----@param spec table +---@param spec CardSpec ---@return EquipCard function fk.CreateEquipCard(spec) assert(type(spec.name) == "string" or type(spec.class_name) == "string") @@ -47,7 +62,7 @@ function fk.CreateEquipCard(spec) return card end ----@param spec table +---@param spec TriggerSkillSpec ---@return TriggerSkill function fk.CreateTriggerSkill(spec) assert(type(spec.name) == "string") @@ -81,6 +96,10 @@ function fk.CreateTriggerSkill(spec) skill.canRefresh = spec.can_refresh end + if spec.on_refresh then + skill.refresh = spec.on_refresh + end + if not spec.priority then if frequency == Skill.Wake then spec.priority = 3 @@ -91,7 +110,7 @@ function fk.CreateTriggerSkill(spec) end end if type(spec.priority) == "number" then - for _, event in ipairs(spec.events) do + for _, event in ipairs(skill.events) do skill.priority_table[event] = spec.priority end elseif type(spec.priority) == "table" then diff --git a/lua/server/event.lua b/lua/server/event.lua index 89dc027c..43ed0bc4 100644 --- a/lua/server/event.lua +++ b/lua/server/event.lua @@ -8,4 +8,31 @@ fk.EventPhaseProceeding = 5 fk.EventPhaseEnd = 6 fk.EventPhaseChanging = 7 fk.EventPhaseSkipping = 8 -fk.NumOfEvents = 9 + +fk.DrawNCards = 9 +fk.AfterDrawNCards = 10 +fk.DrawInitialCards = 11 +fk.AfterDrawInitialCards = 12 + +fk.PreHpRecover = 13 +fk.HpRecover = 14 +fk.PreHpLost = 15 +fk.HpLost = 16 +fk.HpChanged = 17 +fk.MaxHpChanged = 18 + +fk.EventLoseSkill = 19 +fk.EventAcquireSkill = 20 + +fk.StartJudge = 21 +fk.AskForRetrial = 22 +fk.FinishRetrial = 23 +fk.FinishJudge = 24 + +fk.PindianVerifying = 25 +fk.Pindian = 26 + +fk.TurnedOver = 27 +fk.ChainStateChanged = 28 + +fk.NumOfEvents = 29 diff --git a/lua/server/room.lua b/lua/server/room.lua index 4bb24f4d..ea729181 100644 --- a/lua/server/room.lua +++ b/lua/server/room.lua @@ -5,6 +5,7 @@ ---@field current ServerPlayer ---@field game_finished boolean ---@field timeout integer +---@field tag table local Room = class("Room") -- load classes used by the game @@ -34,6 +35,7 @@ function Room:initialize(_room) self.current = nil self.game_finished = false self.timeout = _room:getTimeout() + self.tag = {} end -- When this function returns, the Room(C++) thread stopped. @@ -70,7 +72,7 @@ end ---@param command string ---@param jsonData string ----@param players ServerPlayer[] # default all players +---@param players ServerPlayer[] @ default all players function Room:doBroadcastNotify(command, jsonData, players) players = players or self.players local tolist = fk.SPlayerList() @@ -83,7 +85,7 @@ end ---@param player ServerPlayer ---@param command string ---@param jsonData string ----@param wait boolean # default true +---@param wait boolean @ default true ---@return string | nil function Room:doRequest(player, command, jsonData, wait) if wait == nil then wait = true end diff --git a/lua/server/serverplayer.lua b/lua/server/serverplayer.lua index 988723ee..dd6cc3b6 100644 --- a/lua/server/serverplayer.lua +++ b/lua/server/serverplayer.lua @@ -49,8 +49,8 @@ end --- Wait for at most *timeout* seconds for reply from client. --- --- If *timeout* is negative or **nil**, the function will wait forever until get reply. ----@param timeout integer # seconds to wait ----@return string reply # JSON data +---@param timeout integer @ seconds to wait +---@return string @ JSON data function ServerPlayer:waitForReply(timeout) local result = "" if timeout == nil then @@ -85,4 +85,12 @@ function ServerPlayer:getNextAlive() return ret end +function ServerPlayer:turnOver() + self.faceup = not self.faceup + self.room:broadcastProperty(self, "faceup") + + -- TODO: log + self.room.logic:trigger(fk.TurnedOver, self) +end + return ServerPlayer diff --git a/lua/vscode/lib.lua b/lua/vscode/lib.lua index 054df111..31946b91 100644 --- a/lua/vscode/lib.lua +++ b/lua/vscode/lib.lua @@ -41,6 +41,6 @@ json = {} function json.encode(obj)end --- convert JSON string to lua types ----@param str string # JSON string to decode +---@param str string @ JSON string to decode ---@return table|number|string function json.decode(str)end diff --git a/lua/vscode/player.lua b/lua/vscode/player.lua index 3d535b66..584fbce3 100644 --- a/lua/vscode/player.lua +++ b/lua/vscode/player.lua @@ -53,8 +53,8 @@ function FServerPlayer:doRequest(command,jsonData,timeout)end --- Wait for at most *timeout* seconds for reply from client. --- --- If *timeout* is negative or **nil**, the function will wait forever until get reply. ----@param timeout integer # seconds to wait ----@return string reply # JSON data +---@param timeout integer @ seconds to wait +---@return string @ JSON data ---@overload fun() function FServerPlayer:waitForReply(timeout)end diff --git a/lua/vscode/server.lua b/lua/vscode/server.lua index e2d09ff0..f24db095 100644 --- a/lua/vscode/server.lua +++ b/lua/vscode/server.lua @@ -16,17 +16,17 @@ FRoom = {} function FServer:createRoom(owner,name,capacity)end ---@param id integer ----@return fk.Room room +---@return fk.Room function FServer:findRoom(id)end ----@return fk.Room room +---@return fk.Room function FServer:lobby()end ---@param id integer ----@return fk.ServerPlayer player +---@return fk.ServerPlayer function FServer:findPlayer(id)end ----@return fk.SQLite3 db +---@return fk.SQLite3 function FServer:getDatabase()end function FRoom:getServer()end diff --git a/packages/standard/game_rule.lua b/packages/standard/game_rule.lua index 8b398599..1a9706c6 100644 --- a/packages/standard/game_rule.lua +++ b/packages/standard/game_rule.lua @@ -11,11 +11,41 @@ GameRule = fk.CreateTriggerSkill{ end, on_trigger = function(self, event, target, player, data) - if player == nil then return false end - local room = player.room - if player.room:askForSkillInvoke(player, self.name) then - -- do something + if RoomInstance.tag["SkipGameRule"] then + RoomInstance.tag["SkipGameRule"] = false + return false end + + if target == nil then + if event == fk.GameStart then + print("Game started") + RoomInstance.tag["FirstRound"] = true + end + return false + end + + local room = player.room + switch(event, { + [fk.TurnStart] = function() + player = room.current + if room.tag["FirstRound"] == true then + room.tag["FirstRound"] = false + player:setFlag("Global_FirstRound") + end + + -- TODO: send log + + player:addMark("Global_TurnCount") + player:setMark("damage_point_round", 0) + if not player.faceup then + player:setFlag("-Global_FirstRound") + player:turnOver() + elseif not player.dead then + --player:play() + room:askForSkillInvoke(player, "rule") + end + end, + }) return false end, diff --git a/qml/Pages/Room.qml b/qml/Pages/Room.qml index 38836f5a..205c42ae 100644 --- a/qml/Pages/Room.qml +++ b/qml/Pages/Room.qml @@ -121,7 +121,7 @@ Item { seatNumber: model.seatNumber isDead: model.isDead dying: model.dying - faceturned: model.faceturned + faceup: model.faceup chained: model.chained drank: model.drank isOwner: model.isOwner @@ -161,7 +161,7 @@ Item { self.seatNumber: dashboardModel.seatNumber self.isDead: dashboardModel.isDead self.dying: dashboardModel.dying - self.faceturned: dashboardModel.faceturned + self.faceup: dashboardModel.faceup self.chained: dashboardModel.chained self.drank: dashboardModel.drank self.isOwner: dashboardModel.isOwner @@ -274,7 +274,7 @@ Item { seatNumber: 1, isDead: false, dying: false, - faceturned: false, + faceup: true, chained: false, drank: false, isOwner: false @@ -297,7 +297,7 @@ Item { seatNumber: i + 1, isDead: false, dying: false, - faceturned: false, + faceup: true, chained: false, drank: false, isOwner: false diff --git a/qml/Pages/RoomElement/Photo.qml b/qml/Pages/RoomElement/Photo.qml index b36bf15a..52889b22 100644 --- a/qml/Pages/RoomElement/Photo.qml +++ b/qml/Pages/RoomElement/Photo.qml @@ -20,7 +20,7 @@ Item { property int seatNumber: 1 property bool isDead: false property bool dying: false - property bool faceturned: false + property bool faceup: true property bool chained: false property bool drank: false property bool isOwner: false @@ -110,9 +110,9 @@ Item { Image { id: turnedOver - visible: root.faceturned + visible: !root.faceup source: SkinBank.PHOTO_DIR + "faceturned" - anchors.centerIn: photoMask + x: 29; y: 5 } Image { diff --git a/src/server/room.cpp b/src/server/room.cpp index f556eb53..1f0393bf 100644 --- a/src/server/room.cpp +++ b/src/server/room.cpp @@ -30,6 +30,10 @@ Room::Room(Server* server) Room::~Room() { // TODO + if (isRunning()) { + terminate(); + wait(); + } disconnect(); lua_close(L); }