武将牌堆和更多bugfix (#261)
- 修复了询问卡牌时会返回不符合要求的牌的bug - 修复了摸牌阶段的skillname(游戏规则) - 实装武将牌堆 - 剥离身份模式特有常备主逻辑 - 为资产变换添加时机 - 分离了改判时的移动 - 将canUseGeneral改为Engine所属函数
This commit is contained in:
parent
3c33bcff77
commit
7e15ccb63e
|
@ -2,12 +2,12 @@
|
|||
|
||||
---@class Client
|
||||
---@field public client fk.Client
|
||||
---@field public players ClientPlayer[]
|
||||
---@field public alive_players ClientPlayer[]
|
||||
---@field public observers ClientPlayer[]
|
||||
---@field public current ClientPlayer
|
||||
---@field public discard_pile integer[]
|
||||
---@field public status_skills Skill[]
|
||||
---@field public players ClientPlayer[] @ 所有参战玩家的数组
|
||||
---@field public alive_players ClientPlayer[] @ 所有存活玩家的数组
|
||||
---@field public observers ClientPlayer[] @ 观察者的数组
|
||||
---@field public current ClientPlayer @ 当前回合玩家
|
||||
---@field public discard_pile integer[] @ 弃牌堆
|
||||
---@field public status_skills Skill[] @ 状态技总和
|
||||
---@field public observing boolean
|
||||
Client = class('Client')
|
||||
|
||||
|
|
|
@ -252,9 +252,11 @@ function Engine:addGenerals(generals)
|
|||
end
|
||||
end
|
||||
|
||||
local function canUseGeneral(g)
|
||||
local r = Fk:currentRoom()
|
||||
local general = Fk.generals[g]
|
||||
--- 判断一个武将是否在本房间可用。
|
||||
---@param g string @ 武将名
|
||||
function Engine:canUseGeneral(g)
|
||||
local r = self:currentRoom()
|
||||
local general = self.generals[g]
|
||||
if not general then return false end
|
||||
return not table.contains(r.disabled_packs, general.package.name) and
|
||||
not table.contains(r.disabled_generals, g) and not general.hidden and not general.total_hidden
|
||||
|
@ -270,7 +272,7 @@ function Engine:getSameGenerals(name)
|
|||
local tName = tmp[#tmp]
|
||||
local ret = self.same_generals[tName] or {}
|
||||
return table.filter(ret, function(g)
|
||||
return g ~= name and self.generals[g] ~= nil and canUseGeneral(g)
|
||||
return g ~= name and self.generals[g] ~= nil and self:canUseGeneral(g)
|
||||
end)
|
||||
end
|
||||
|
||||
|
@ -393,7 +395,7 @@ function Engine:getAllGenerals(except)
|
|||
local result = {}
|
||||
for _, general in pairs(self.generals) do
|
||||
if not (except and table.contains(except, general)) then
|
||||
if canUseGeneral(general.name) then
|
||||
if self:canUseGeneral(general.name) then
|
||||
table.insert(result, general)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -8,8 +8,8 @@
|
|||
---@field public name string @ 游戏模式名
|
||||
---@field public minPlayer integer @ 最小玩家数
|
||||
---@field public maxPlayer integer @ 最大玩家数
|
||||
---@field public rule TriggerSkill @ 规则(通过技能完成,通常用来为特定角色及特定时机提供触发事件)
|
||||
---@field public logic fun() @ 逻辑(通过function完成,通常用来初始化、分配身份及座次)
|
||||
---@field public rule nil|TriggerSkill @ 规则(通过技能完成,通常用来为特定角色及特定时机提供触发事件)
|
||||
---@field public logic nil|fun() @ 逻辑(通过function完成,通常用来初始化、分配身份及座次)
|
||||
---@field public whitelist string[]|nil @ 白名单
|
||||
---@field public blacklist string[]|nil @ 黑名单
|
||||
local GameMode = class("GameMode")
|
||||
|
|
|
@ -508,7 +508,7 @@ function Player:distanceTo(other, mode, ignore_dead)
|
|||
temp = temp.next
|
||||
end
|
||||
if temp ~= other then
|
||||
print("Distance malfunction: start and end does not matched.")
|
||||
print("Distance malfunction: start and end does not match.")
|
||||
end
|
||||
local left = #(ignore_dead and Fk:currentRoom().players or Fk:currentRoom().alive_players) - right - #table.filter(Fk:currentRoom().alive_players, function(p) return p:isRemoved() end)
|
||||
local ret = 0
|
||||
|
|
|
@ -129,3 +129,7 @@ fk.GeneralRevealed = 89
|
|||
fk.GeneralHidden = 90
|
||||
|
||||
fk.NumOfEvents = 91
|
||||
|
||||
fk.BeforePropertyChange = 92
|
||||
fk.PropertyChange = 93
|
||||
fk.AfterPropertyChange = 94
|
||||
|
|
|
@ -294,7 +294,7 @@ GameEvent.functions[GameEvent.Phase] = function(self)
|
|||
n = 2
|
||||
}
|
||||
room.logic:trigger(fk.DrawNCards, player, data)
|
||||
room:drawCards(player, data.n, self.name)
|
||||
room:drawCards(player, data.n, "game_rule")
|
||||
room.logic:trigger(fk.AfterDrawNCards, player, data)
|
||||
end,
|
||||
[Player.Play] = function()
|
||||
|
|
|
@ -9,6 +9,9 @@ GameEvent.functions[GameEvent.ChangeProperty] = function(self)
|
|||
|
||||
data.sendLog = data.sendLog or false
|
||||
local skills = {}
|
||||
if logic:trigger(fk.PropertyChange, player, data) then
|
||||
logic:breakEvent()
|
||||
end
|
||||
|
||||
if data.general and data.general ~= "" and data.general ~= player.general then
|
||||
local originalGeneral = Fk.generals[player.general] or Fk.generals["blank_shibing"]
|
||||
|
|
|
@ -90,32 +90,8 @@ function GameLogic:chooseGenerals()
|
|||
|
||||
if lord ~= nil then
|
||||
room.current = lord
|
||||
local generals = {}
|
||||
local lordlist = {}
|
||||
local lordpools = {}
|
||||
if room.settings.gameMode == "aaa_role_mode" then
|
||||
for _, general in pairs(Fk:getAllGenerals()) do
|
||||
if (not general.hidden and not general.total_hidden) and
|
||||
table.find(general.skills, function(s)
|
||||
return s.lordSkill
|
||||
end) and
|
||||
not table.find(lordlist, function(g)
|
||||
return g.trueName == general.trueName
|
||||
end) then
|
||||
table.insert(lordlist, general)
|
||||
end
|
||||
end
|
||||
lordlist = table.random(lordlist, 3) or {}
|
||||
end
|
||||
table.insertTable(generals, Fk:getGeneralsRandomly(generalNum, Fk:getAllGenerals(), nil, function(g)
|
||||
return table.contains(table.map(lordlist, function(g) return g.trueName end), g.trueName)
|
||||
end))
|
||||
for i = 1, #generals do
|
||||
generals[i] = generals[i].name
|
||||
end
|
||||
lordpools = table.simpleClone(generals)
|
||||
table.insertTable(lordpools, table.map(lordlist, function(g) return g.name end))
|
||||
lord_generals = room:askForGeneral(lord, lordpools, n)
|
||||
local generals = room:getNGenerals(generalNum)
|
||||
lord_generals = room:askForGeneral(lord, generals, n)
|
||||
local lord_general, deputy
|
||||
if type(lord_generals) == "table" then
|
||||
deputy = lord_generals[2]
|
||||
|
@ -125,6 +101,9 @@ function GameLogic:chooseGenerals()
|
|||
lord_generals = {lord_general}
|
||||
end
|
||||
|
||||
generals = table.filter(generals, function(g) return not table.contains(lord_generals, g) end)
|
||||
room:returnToGeneralPile(generals)
|
||||
|
||||
room:setPlayerGeneral(lord, lord_general, true)
|
||||
room:askForChooseKingdom({lord})
|
||||
room:broadcastProperty(lord, "general")
|
||||
|
@ -134,13 +113,10 @@ function GameLogic:chooseGenerals()
|
|||
end
|
||||
|
||||
local nonlord = room:getOtherPlayers(lord, true)
|
||||
local generals = Fk:getGeneralsRandomly(#nonlord * generalNum, nil, lord_generals)
|
||||
local generals = room:getNGenerals(#nonlord * generalNum)
|
||||
table.shuffle(generals)
|
||||
for _, p in ipairs(nonlord) do
|
||||
local arg = {}
|
||||
for i = 1, generalNum do
|
||||
table.insert(arg, table.remove(generals, 1).name)
|
||||
end
|
||||
for i, p in ipairs(nonlord) do
|
||||
local arg = table.slice(generals, (i - 1) * generalNum + 1, i * generalNum + 1)
|
||||
p.request_data = json.encode{ arg, n }
|
||||
p.default_reply = table.random(arg, n)
|
||||
end
|
||||
|
@ -148,11 +124,13 @@ function GameLogic:chooseGenerals()
|
|||
room:notifyMoveFocus(nonlord, "AskForGeneral")
|
||||
room:doBroadcastRequest("AskForGeneral", nonlord)
|
||||
|
||||
local selected = {}
|
||||
for _, p in ipairs(nonlord) do
|
||||
if p.general == "" and p.reply_ready then
|
||||
local generals = json.decode(p.client_reply)
|
||||
local general = generals[1]
|
||||
local deputy = generals[2]
|
||||
local general_ret = json.decode(p.client_reply)
|
||||
local general = general_ret[1]
|
||||
local deputy = general_ret[2]
|
||||
table.insertTableIfNeed(selected, general_ret)
|
||||
room:setPlayerGeneral(p, general, true, true)
|
||||
room:setDeputyGeneral(p, deputy)
|
||||
else
|
||||
|
@ -162,6 +140,9 @@ function GameLogic:chooseGenerals()
|
|||
p.default_reply = ""
|
||||
end
|
||||
|
||||
generals = table.filter(generals, function(g) return not table.contains(selected, g) end)
|
||||
room:returnToGeneralPile(generals)
|
||||
|
||||
room:askForChooseKingdom(nonlord)
|
||||
end
|
||||
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
---@field public game_finished boolean @ 游戏是否已经结束
|
||||
---@field public timeout integer @ 出牌时长上限
|
||||
---@field public tag table<string, any> @ Tag清单,其实跟Player的标记是差不多的东西
|
||||
---@field public general_pile string[] @ 武将牌堆,这是可用武将名的数组
|
||||
---@field public draw_pile integer[] @ 摸牌堆,这是卡牌id的数组
|
||||
---@field public discard_pile integer[] @ 弃牌堆,也是卡牌id的数组
|
||||
---@field public processing_area integer[] @ 处理区,依然是卡牌id数组
|
||||
|
@ -76,6 +77,7 @@ function Room:initialize(_room)
|
|||
self.game_finished = false
|
||||
self.timeout = _room:getTimeout()
|
||||
self.tag = {}
|
||||
self.general_pile = {}
|
||||
self.draw_pile = {}
|
||||
self.discard_pile = {}
|
||||
self.processing_area = {}
|
||||
|
@ -108,7 +110,7 @@ function Room:resume()
|
|||
-- 如果还没运行的话就先创建自己的主协程
|
||||
if not self.main_co then
|
||||
self.main_co = coroutine.create(function()
|
||||
self.tag["_general_pile"] = Fk:getAllGenerals()
|
||||
self:makeGeneralPile()
|
||||
self:run()
|
||||
end)
|
||||
end
|
||||
|
@ -144,6 +146,26 @@ function Room:resume()
|
|||
return true
|
||||
end
|
||||
|
||||
-- 构造武将牌堆
|
||||
function Room:makeGeneralPile()
|
||||
local trueNames = {}
|
||||
local ret = {}
|
||||
if self.game_started then
|
||||
for _, player in ipairs(self.players) do
|
||||
trueNames[Fk.generals[player.general].trueName] = true
|
||||
end
|
||||
end
|
||||
for name, general in pairs(Fk.generals) do
|
||||
if Fk:canUseGeneral(name) and not trueNames[general.trueName] then
|
||||
table.insert(ret, name)
|
||||
trueNames[general.trueName] = true
|
||||
end
|
||||
end
|
||||
table.shuffle(ret)
|
||||
self.general_pile = ret
|
||||
return true
|
||||
end
|
||||
|
||||
-- 供调度器使用的函数,用来指示房间是否就绪。
|
||||
-- 如果没有就绪的话,可能会返回第二个值来告诉调度器自己还有多久就绪。
|
||||
function Room:isReady()
|
||||
|
@ -1241,6 +1263,10 @@ function Room:askForCard(player, minNum, maxNum, includeEquip, skillName, cancel
|
|||
if includeEquip then
|
||||
table.insertTable(hands, player:getCardIds(Player.Equip))
|
||||
end
|
||||
local exp = Exppattern:Parse(pattern)
|
||||
hands = table.filter(hands, function(cid)
|
||||
return exp:match(Fk:getCardById(cid))
|
||||
end)
|
||||
for _ = 1, minNum do
|
||||
local randomId = hands[math.random(1, #hands)]
|
||||
table.insert(chosenCards, randomId)
|
||||
|
@ -1296,6 +1322,79 @@ function Room:askForChooseCardAndPlayers(player, targets, minNum, maxNum, patter
|
|||
end
|
||||
end
|
||||
|
||||
--- 抽个武将
|
||||
---
|
||||
--- 同getNCards,抽出来就没有了,所以记得放回去。
|
||||
---@param n number @ 数量
|
||||
---@param position string|nil @位置,top/bottom,默认top
|
||||
---@return string[] @ 武将名数组
|
||||
function Room:getNGenerals(n, position)
|
||||
position = position or "top"
|
||||
assert(position == "top" or position == "bottom")
|
||||
|
||||
local generals = {}
|
||||
while n > 0 do
|
||||
|
||||
local index = position == "top" and 1 or #self.general_pile
|
||||
table.insert(generals, table.remove(self.general_pile, index))
|
||||
|
||||
n = n - 1
|
||||
end
|
||||
|
||||
if #generals < 1 then
|
||||
self:gameOver("")
|
||||
end
|
||||
return generals
|
||||
end
|
||||
|
||||
--- 把武将牌塞回去(……)
|
||||
---@param g string[] @ 武将名数组
|
||||
---@param position string|nil @位置,top/bottom,默认bottom
|
||||
---@return boolean @ 是否成功
|
||||
function Room:returnToGeneralPile(g, position)
|
||||
position = position or "bottom"
|
||||
assert(position == "top" or position == "bottom")
|
||||
if position == "bottom" then
|
||||
table.insertTable(self.general_pile, g)
|
||||
elseif position == "top" then
|
||||
while #g > 0 do
|
||||
table.insert(self.general_pile, 1, table.remove(g))
|
||||
end
|
||||
end
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
--- 抽特定名字的武将(抽了就没了)
|
||||
---@param name string @ 武将name,如找不到则查找truename,再找不到则返回nil
|
||||
---@return string | nil @ 抽出的武将名
|
||||
function Room:findGeneral(name)
|
||||
for i, g in ipairs(self.general_pile) do
|
||||
if g == name or Fk.generals[g].trueName == Fk.generals[name].trueName then
|
||||
return table.remove(self.general_pile, i)
|
||||
end
|
||||
end
|
||||
return nil
|
||||
end
|
||||
|
||||
--- 自上而下抽符合特定情况的N个武将(抽了就没了)
|
||||
---@param func fun(name: string):any @ 武将筛选函数
|
||||
---@param n integer | nil @ 抽取数量,数量不足则直接抽干净
|
||||
---@return string[] @ 武将组合,可能为空
|
||||
function Room:findGenerals(func, n)
|
||||
n = n or 1
|
||||
local ret = {}
|
||||
local index = 1
|
||||
repeat
|
||||
if func(self.general_pile[index]) then
|
||||
table.insert(ret, table.remove(self.general_pile, index))
|
||||
else
|
||||
index = index + 1
|
||||
end
|
||||
until index >= #self.general_pile or #ret >= n
|
||||
return ret
|
||||
end
|
||||
|
||||
--- 询问玩家选择一名武将。
|
||||
---@param player ServerPlayer @ 询问目标
|
||||
---@param generals string[] @ 可选武将
|
||||
|
@ -2078,7 +2177,12 @@ function Room:askForMoveCardInBoard(player, targetOne, targetTwo, skillName, fla
|
|||
result = json.decode(result)
|
||||
end
|
||||
|
||||
local from, to = result.pos == 0 and targetOne, targetTwo or targetTwo, targetOne
|
||||
local from, to
|
||||
if result.pos == 0 then
|
||||
from, to = targetOne, targetTwo
|
||||
else
|
||||
from, to = targetTwo, targetOne
|
||||
end
|
||||
local cardToMove = self:getCardOwner(result.cardId):getVirualEquip(result.cardId) or Fk:getCardById(result.cardId)
|
||||
self:moveCardTo(
|
||||
cardToMove,
|
||||
|
@ -2270,7 +2374,7 @@ function Room:doCardUseEffect(cardUseEvent)
|
|||
|
||||
local realCardIds = self:getSubcardsByRule(cardUseEvent.card, { Card.Processing })
|
||||
|
||||
self.logic:trigger(fk.BeforeCardUseEffect, cardUseEvent.from, cardUseEvent)
|
||||
self.logic:trigger(fk.BeforeCardUseEffect, self:getPlayerById(cardUseEvent.from), cardUseEvent)
|
||||
-- If using Equip or Delayed trick, move them to the area and return
|
||||
if cardUseEvent.card.type == Card.TypeEquip then
|
||||
if #realCardIds == 0 then
|
||||
|
@ -2919,7 +3023,8 @@ function Room:retrial(card, player, judge, skillName, exchange)
|
|||
arg = skillName,
|
||||
}
|
||||
|
||||
self:moveCards(move1, move2)
|
||||
self:moveCards(move1)
|
||||
self:moveCards(move2)
|
||||
|
||||
if triggerResponded then
|
||||
self.logic:trigger(fk.CardRespondFinished, player, resp)
|
||||
|
|
|
@ -743,6 +743,7 @@ function ServerPlayer:setChainState(chained)
|
|||
end
|
||||
|
||||
function ServerPlayer:reset()
|
||||
if self.faceup and not self.chained then return end
|
||||
self.room:sendLog{
|
||||
type = "#ChainStateChange",
|
||||
from = self.id,
|
||||
|
|
|
@ -1164,10 +1164,88 @@ local diaochan = General:new(extension, "diaochan", "qun", 3, 3, General.Female)
|
|||
diaochan:addSkill(lijian)
|
||||
diaochan:addSkill(biyue)
|
||||
|
||||
local role_getlogic = function()
|
||||
local role_logic = GameLogic:subclass("role_logic")
|
||||
|
||||
function role_logic:chooseGenerals()
|
||||
local room = self.room ---@class Room
|
||||
local generalNum = room.settings.generalNum
|
||||
local n = room.settings.enableDeputy and 2 or 1
|
||||
local lord = room:getLord()
|
||||
local lord_generals = {}
|
||||
local lord_num = 3
|
||||
|
||||
if lord ~= nil then
|
||||
room.current = lord
|
||||
local generals = table.connect(room:findGenerals(function(g)
|
||||
return table.find(Fk.generals[g].skills, function(s) return s.lordSkill end)
|
||||
end, lord_num), room:getNGenerals(generalNum))
|
||||
if #room.general_pile < (#room.players - 1) * generalNum then
|
||||
room:gameOver("")
|
||||
end
|
||||
lord_generals = room:askForGeneral(lord, generals, n)
|
||||
local lord_general, deputy
|
||||
if type(lord_generals) == "table" then
|
||||
deputy = lord_generals[2]
|
||||
lord_general = lord_generals[1]
|
||||
else
|
||||
lord_general = lord_generals
|
||||
lord_generals = {lord_general}
|
||||
end
|
||||
|
||||
generals = table.filter(generals, function(g) return not table.contains(lord_generals, g) end)
|
||||
room:returnToGeneralPile(generals)
|
||||
|
||||
room:setPlayerGeneral(lord, lord_general, true)
|
||||
room:askForChooseKingdom({lord})
|
||||
room:broadcastProperty(lord, "general")
|
||||
room:broadcastProperty(lord, "kingdom")
|
||||
room:setDeputyGeneral(lord, deputy)
|
||||
room:broadcastProperty(lord, "deputyGeneral")
|
||||
end
|
||||
|
||||
local nonlord = room:getOtherPlayers(lord, true)
|
||||
local generals = room:getNGenerals(#nonlord * generalNum)
|
||||
table.shuffle(generals)
|
||||
for i, p in ipairs(nonlord) do
|
||||
local arg = table.slice(generals, (i - 1) * generalNum + 1, i * generalNum + 1)
|
||||
p.request_data = json.encode{ arg, n }
|
||||
p.default_reply = table.random(arg, n)
|
||||
end
|
||||
|
||||
room:notifyMoveFocus(nonlord, "AskForGeneral")
|
||||
room:doBroadcastRequest("AskForGeneral", nonlord)
|
||||
|
||||
local selected = {}
|
||||
for _, p in ipairs(nonlord) do
|
||||
if p.general == "" and p.reply_ready then
|
||||
local general_ret = json.decode(p.client_reply)
|
||||
local general = general_ret[1]
|
||||
local deputy = general_ret[2]
|
||||
table.insertTableIfNeed(selected, general_ret)
|
||||
room:setPlayerGeneral(p, general, true, true)
|
||||
room:setDeputyGeneral(p, deputy)
|
||||
else
|
||||
room:setPlayerGeneral(p, p.default_reply[1], true, true)
|
||||
room:setDeputyGeneral(p, p.default_reply[2])
|
||||
end
|
||||
p.default_reply = ""
|
||||
end
|
||||
|
||||
generals = table.filter(generals, function(g) return not table.contains(selected, g) end)
|
||||
room:returnToGeneralPile(generals)
|
||||
|
||||
room:askForChooseKingdom(nonlord)
|
||||
end
|
||||
|
||||
return role_logic
|
||||
end
|
||||
|
||||
local role_mode = fk.CreateGameMode{
|
||||
name = "aaa_role_mode", -- just to let it at the top of list
|
||||
minPlayer = 2,
|
||||
maxPlayer = 8,
|
||||
logic = role_getlogic,
|
||||
is_counted = function(self, room)
|
||||
return #room.players >= 5
|
||||
end,
|
||||
|
|
|
@ -295,12 +295,14 @@ local change_hero = fk.CreateActiveSkill{
|
|||
local from = room:getPlayerById(effect.from)
|
||||
local target = room:getPlayerById(effect.tos[1])
|
||||
local choice = self.interaction.data
|
||||
local generals = table.map(Fk:getGeneralsRandomly(8, Fk:getAllGenerals()), function(p) return p.name end)
|
||||
local generals = room:getNGenerals(8)
|
||||
local general = room:askForGeneral(from, generals, 1)
|
||||
if general == nil then
|
||||
general = table.random(generals)
|
||||
end
|
||||
table.removeOne(generals, general)
|
||||
room:changeHero(target, general, false, choice == "deputyGeneral", true)
|
||||
room:returnToGeneralPile(generals)
|
||||
end,
|
||||
}
|
||||
local test_zhenggong = fk.CreateTriggerSkill{
|
||||
|
|
Loading…
Reference in New Issue