jxg;
This commit is contained in:
commit
c99e200301
|
@ -747,6 +747,26 @@ Item {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// manualBox: same as popupBox, but must be closed manually
|
||||||
|
Loader {
|
||||||
|
id: manualBox
|
||||||
|
z: 999
|
||||||
|
onSourceChanged: {
|
||||||
|
if (item === null)
|
||||||
|
return;
|
||||||
|
item.finished.connect(() => sourceComponent = undefined);
|
||||||
|
item.widthChanged.connect(() => manualBox.moveToCenter());
|
||||||
|
item.heightChanged.connect(() => manualBox.moveToCenter());
|
||||||
|
moveToCenter();
|
||||||
|
}
|
||||||
|
onSourceComponentChanged: sourceChanged();
|
||||||
|
|
||||||
|
function moveToCenter() {
|
||||||
|
item.x = Math.round((roomArea.width - item.width) / 2);
|
||||||
|
item.y = Math.round(roomArea.height * 0.67 - item.height / 2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Loader {
|
Loader {
|
||||||
id: popupBox
|
id: popupBox
|
||||||
z: 999
|
z: 999
|
||||||
|
@ -772,27 +792,6 @@ Item {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// manualBox: same as popupBox, but must be closed manually
|
|
||||||
Loader {
|
|
||||||
id: manualBox
|
|
||||||
z: 999
|
|
||||||
onSourceChanged: {
|
|
||||||
if (item === null)
|
|
||||||
return;
|
|
||||||
item.finished.connect(() => sourceComponent = undefined);
|
|
||||||
item.widthChanged.connect(() => manualBox.moveToCenter());
|
|
||||||
item.heightChanged.connect(() => manualBox.moveToCenter());
|
|
||||||
moveToCenter();
|
|
||||||
}
|
|
||||||
onSourceComponentChanged: sourceChanged();
|
|
||||||
|
|
||||||
function moveToCenter()
|
|
||||||
{
|
|
||||||
item.x = Math.round((roomArea.width - item.width) / 2);
|
|
||||||
item.y = Math.round(roomArea.height * 0.67 - item.height / 2);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Loader {
|
Loader {
|
||||||
id: bigAnim
|
id: bigAnim
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -6,273 +6,273 @@ local RandomAI = AI:subclass("RandomAI")
|
||||||
---@param skill ActiveSkill
|
---@param skill ActiveSkill
|
||||||
---@param card Card | nil
|
---@param card Card | nil
|
||||||
local function useActiveSkill(self, skill, card)
|
local function useActiveSkill(self, skill, card)
|
||||||
local room = self.room
|
local room = self.room
|
||||||
local player = self.player
|
local player = self.player
|
||||||
|
|
||||||
local filter_func = skill.cardFilter
|
local filter_func = skill.cardFilter
|
||||||
if card then
|
if card then
|
||||||
filter_func = function()
|
filter_func = function()
|
||||||
return false
|
return false
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
end
|
||||||
|
|
||||||
if self.command == "PlayCard" and (not skill:canUse(player, card) or player:prohibitUse(card)) then
|
if self.command == "PlayCard" and (not skill:canUse(player, card) or player:prohibitUse(card)) then
|
||||||
return ""
|
|
||||||
end
|
|
||||||
|
|
||||||
local max_try_times = 100
|
|
||||||
local selected_targets = {}
|
|
||||||
local selected_cards = {}
|
|
||||||
local min = skill:getMinTargetNum()
|
|
||||||
local max = skill:getMaxTargetNum(player, card)
|
|
||||||
local min_card = skill:getMinCardNum()
|
|
||||||
local max_card = skill:getMaxCardNum()
|
|
||||||
for _ = 0, max_try_times do
|
|
||||||
if skill:feasible(selected_targets, selected_cards, self.player, card) then
|
|
||||||
break
|
|
||||||
end
|
|
||||||
local avail_targets = table.filter(self.room:getAlivePlayers(), function(p)
|
|
||||||
local ret = skill:targetFilter(p.id, selected_targets, selected_cards, card or Fk:cloneCard 'zixing')
|
|
||||||
if ret and card then
|
|
||||||
if player:prohibitUse(card) then
|
|
||||||
ret = false
|
|
||||||
end
|
|
||||||
end
|
|
||||||
return ret
|
|
||||||
end)
|
|
||||||
avail_targets = table.map(avail_targets, function(p)
|
|
||||||
return p.id
|
|
||||||
end)
|
|
||||||
local avail_cards = table.filter(player:getCardIds{Player.Hand, Player.Equip}, function(id)
|
|
||||||
return filter_func(skill, id, selected_cards, selected_targets)
|
|
||||||
end)
|
|
||||||
|
|
||||||
if #avail_targets == 0 and #avail_cards == 0 then
|
|
||||||
break
|
|
||||||
end
|
|
||||||
table.insertIfNeed(selected_targets, table.random(avail_targets))
|
|
||||||
table.insertIfNeed(selected_cards, table.random(avail_cards))
|
|
||||||
end
|
|
||||||
if skill:feasible(selected_targets, selected_cards, self.player, card) then
|
|
||||||
local ret = json.encode {
|
|
||||||
card = card and card.id or json.encode {
|
|
||||||
skill = skill.name,
|
|
||||||
subcards = selected_cards
|
|
||||||
},
|
|
||||||
targets = selected_targets
|
|
||||||
}
|
|
||||||
-- print(ret)
|
|
||||||
return ret
|
|
||||||
end
|
|
||||||
return ""
|
return ""
|
||||||
|
end
|
||||||
|
|
||||||
|
local max_try_times = 100
|
||||||
|
local selected_targets = {}
|
||||||
|
local selected_cards = {}
|
||||||
|
local min = skill:getMinTargetNum()
|
||||||
|
local max = skill:getMaxTargetNum(player, card)
|
||||||
|
local min_card = skill:getMinCardNum()
|
||||||
|
local max_card = skill:getMaxCardNum()
|
||||||
|
for _ = 0, max_try_times do
|
||||||
|
if skill:feasible(selected_targets, selected_cards, self.player, card) then
|
||||||
|
break
|
||||||
|
end
|
||||||
|
local avail_targets = table.filter(self.room:getAlivePlayers(), function(p)
|
||||||
|
local ret = skill:targetFilter(p.id, selected_targets, selected_cards, card or Fk:cloneCard 'zixing')
|
||||||
|
if ret and card then
|
||||||
|
if player:prohibitUse(card) then
|
||||||
|
ret = false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return ret
|
||||||
|
end)
|
||||||
|
avail_targets = table.map(avail_targets, function(p)
|
||||||
|
return p.id
|
||||||
|
end)
|
||||||
|
local avail_cards = table.filter(player:getCardIds { Player.Hand, Player.Equip }, function(id)
|
||||||
|
return filter_func(skill, id, selected_cards, selected_targets)
|
||||||
|
end)
|
||||||
|
|
||||||
|
if #avail_targets == 0 and #avail_cards == 0 then
|
||||||
|
break
|
||||||
|
end
|
||||||
|
table.insertIfNeed(selected_targets, table.random(avail_targets))
|
||||||
|
table.insertIfNeed(selected_cards, table.random(avail_cards))
|
||||||
|
end
|
||||||
|
if skill:feasible(selected_targets, selected_cards, self.player, card) then
|
||||||
|
local ret = json.encode {
|
||||||
|
card = card and card.id or json.encode {
|
||||||
|
skill = skill.name,
|
||||||
|
subcards = selected_cards
|
||||||
|
},
|
||||||
|
targets = selected_targets
|
||||||
|
}
|
||||||
|
-- print(ret)
|
||||||
|
return ret
|
||||||
|
end
|
||||||
|
return ""
|
||||||
end
|
end
|
||||||
|
|
||||||
---@param self RandomAI
|
---@param self RandomAI
|
||||||
---@param skill ViewAsSkill
|
---@param skill ViewAsSkill
|
||||||
local function useVSSkill(self, skill, pattern, cancelable, extra_data)
|
local function useVSSkill(self, skill, pattern, cancelable, extra_data)
|
||||||
local player = self.player
|
local player = self.player
|
||||||
local room = self.room
|
local room = self.room
|
||||||
local precondition
|
local precondition
|
||||||
|
|
||||||
if self.command == "PlayCard" then
|
if self.command == "PlayCard" then
|
||||||
precondition = skill:enabledAtPlay(player)
|
precondition = skill:enabledAtPlay(player)
|
||||||
if not precondition then
|
if not precondition then
|
||||||
return nil
|
return nil
|
||||||
end
|
|
||||||
local exp = Exppattern:Parse(skill.pattern)
|
|
||||||
local cnames = {}
|
|
||||||
for _, m in ipairs(exp.matchers) do
|
|
||||||
if m.name then
|
|
||||||
table.insertTable(cnames, m.name)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
for _, n in ipairs(cnames) do
|
|
||||||
local c = Fk:cloneCard(n)
|
|
||||||
precondition = c.skill:canUse(Self, c)
|
|
||||||
if precondition then
|
|
||||||
break
|
|
||||||
end
|
|
||||||
end
|
|
||||||
else
|
|
||||||
precondition = skill:enabledAtResponse(player)
|
|
||||||
if not precondition then
|
|
||||||
return nil
|
|
||||||
end
|
|
||||||
local exp = Exppattern:Parse(pattern)
|
|
||||||
precondition = exp:matchExp(skill.pattern)
|
|
||||||
end
|
end
|
||||||
|
local exp = Exppattern:Parse(skill.pattern)
|
||||||
if (not precondition) or math.random() < 0.2 then
|
local cnames = {}
|
||||||
return nil
|
for _, m in ipairs(exp.matchers) do
|
||||||
|
if m.name then
|
||||||
|
table.insertTable(cnames, m.name)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
for _, n in ipairs(cnames) do
|
||||||
local selected_cards = {}
|
local c = Fk:cloneCard(n)
|
||||||
local max_try_time = 100
|
precondition = c.skill:canUse(Self, c)
|
||||||
|
if precondition then
|
||||||
for _ = 0, max_try_time do
|
break
|
||||||
local avail_cards = table.filter(player:getCardIds{Player.Hand, Player.Equip}, function(id)
|
end
|
||||||
return skill:cardFilter(id, selected_cards)
|
|
||||||
end)
|
|
||||||
if #avail_cards == 0 then
|
|
||||||
break
|
|
||||||
end
|
|
||||||
table.insert(selected_cards, table.random(avail_cards))
|
|
||||||
if skill:viewAs(selected_cards) then
|
|
||||||
return {
|
|
||||||
skill = skill.name,
|
|
||||||
subcards = selected_cards
|
|
||||||
}
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
else
|
||||||
|
precondition = skill:enabledAtResponse(player)
|
||||||
|
if not precondition then
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
local exp = Exppattern:Parse(pattern)
|
||||||
|
precondition = exp:matchExp(skill.pattern)
|
||||||
|
end
|
||||||
|
|
||||||
|
if (not precondition) or math.random() < 0.2 then
|
||||||
return nil
|
return nil
|
||||||
|
end
|
||||||
|
|
||||||
|
local selected_cards = {}
|
||||||
|
local max_try_time = 100
|
||||||
|
|
||||||
|
for _ = 0, max_try_time do
|
||||||
|
local avail_cards = table.filter(player:getCardIds { Player.Hand, Player.Equip }, function(id)
|
||||||
|
return skill:cardFilter(id, selected_cards)
|
||||||
|
end)
|
||||||
|
if #avail_cards == 0 then
|
||||||
|
break
|
||||||
|
end
|
||||||
|
table.insert(selected_cards, table.random(avail_cards))
|
||||||
|
if skill:viewAs(selected_cards) then
|
||||||
|
return {
|
||||||
|
skill = skill.name,
|
||||||
|
subcards = selected_cards
|
||||||
|
}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return nil
|
||||||
end
|
end
|
||||||
|
|
||||||
local random_cb = {}
|
local random_cb = {}
|
||||||
|
|
||||||
random_cb.AskForUseActiveSkill = function(self, jsonData)
|
random_cb.AskForUseActiveSkill = function(self, jsonData)
|
||||||
local data = json.decode(jsonData)
|
local data = json.decode(jsonData)
|
||||||
local skill = Fk.skills[data[1]]
|
local skill = Fk.skills[data[1]]
|
||||||
local cancelable = data[3]
|
local cancelable = data[3]
|
||||||
if cancelable and math.random() < 0.25 then
|
if cancelable and math.random() < 0.25 then
|
||||||
return ""
|
return ""
|
||||||
end
|
end
|
||||||
local extra_data = json.decode(data[4])
|
local extra_data = json.decode(data[4])
|
||||||
for k, v in pairs(extra_data) do
|
for k, v in pairs(extra_data) do
|
||||||
skill[k] = v
|
skill[k] = v
|
||||||
end
|
end
|
||||||
return useActiveSkill(self, skill)
|
return useActiveSkill(self, skill)
|
||||||
end
|
end
|
||||||
|
|
||||||
random_cb.AskForSkillInvoke = function(self, jsonData)
|
random_cb.AskForSkillInvoke = function(self, jsonData)
|
||||||
return table.random {"1", ""}
|
return table.random { "1", "" }
|
||||||
end
|
end
|
||||||
|
|
||||||
random_cb.AskForUseCard = function(self, jsonData)
|
random_cb.AskForUseCard = function(self, jsonData)
|
||||||
local player = self.player
|
local player = self.player
|
||||||
local data = json.decode(jsonData)
|
local data = json.decode(jsonData)
|
||||||
local card_name = data[1]
|
local card_name = data[1]
|
||||||
local pattern = data[2] or card_name
|
local pattern = data[2] or card_name
|
||||||
local cancelable = data[4] or true
|
local cancelable = data[4] or true
|
||||||
local exp = Exppattern:Parse(pattern)
|
local exp = Exppattern:Parse(pattern)
|
||||||
|
|
||||||
local avail_cards = table.filter(player:getCardIds("he"), function(id)
|
local avail_cards = table.filter(player:getCardIds("he"), function(id)
|
||||||
return exp:match(Fk:getCardById(id)) and not player:prohibitUse(Fk:getCardById(id))
|
return exp:match(Fk:getCardById(id)) and not player:prohibitUse(Fk:getCardById(id))
|
||||||
end)
|
end)
|
||||||
if #avail_cards > 0 then
|
if #avail_cards > 0 then
|
||||||
if math.random() < 0.25 then
|
if math.random() < 0.25 then
|
||||||
return ""
|
return ""
|
||||||
end
|
|
||||||
avail_cards = table.map(avail_cards, function(id)
|
|
||||||
return Fk:getCardById(id)
|
|
||||||
end)
|
|
||||||
for _, card in ipairs(avail_cards) do
|
|
||||||
local skill = card.skill
|
|
||||||
local max_try_times = 100
|
|
||||||
local selected_targets = {}
|
|
||||||
local min = skill:getMinTargetNum()
|
|
||||||
local max = skill:getMaxTargetNum(player, card)
|
|
||||||
local min_card = skill:getMinCardNum()
|
|
||||||
local max_card = skill:getMaxCardNum()
|
|
||||||
for _ = 0, max_try_times do
|
|
||||||
if skill:feasible(selected_targets, {card.id}, self.player, card) then
|
|
||||||
break
|
|
||||||
end
|
|
||||||
local avail_targets = table.filter(self.room:getAlivePlayers(), function(p)
|
|
||||||
local ret = skill:targetFilter(p.id, selected_targets, {card}, card or Fk:cloneCard 'zixing')
|
|
||||||
return ret
|
|
||||||
end)
|
|
||||||
avail_targets = table.map(avail_targets, function(p)
|
|
||||||
return p.id
|
|
||||||
end)
|
|
||||||
|
|
||||||
if #avail_targets + #avail_cards < 1 then
|
|
||||||
break
|
|
||||||
end
|
|
||||||
table.insertIfNeed(selected_targets, table.random(avail_targets))
|
|
||||||
end
|
|
||||||
if skill:feasible(selected_targets, {card.id}, self.player, card) then
|
|
||||||
return json.encode {
|
|
||||||
card = table.random(avail_cards),
|
|
||||||
targets = selected_targets
|
|
||||||
}
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
return ""
|
avail_cards = table.map(avail_cards, function(id)
|
||||||
|
return Fk:getCardById(id)
|
||||||
|
end)
|
||||||
|
for _, card in ipairs(avail_cards) do
|
||||||
|
local skill = card.skill
|
||||||
|
local max_try_times = 100
|
||||||
|
local selected_targets = {}
|
||||||
|
local min = skill:getMinTargetNum()
|
||||||
|
local max = skill:getMaxTargetNum(player, card)
|
||||||
|
local min_card = skill:getMinCardNum()
|
||||||
|
local max_card = skill:getMaxCardNum()
|
||||||
|
for _ = 0, max_try_times do
|
||||||
|
if skill:feasible(selected_targets, { card.id }, self.player, card) then
|
||||||
|
break
|
||||||
|
end
|
||||||
|
local avail_targets = table.filter(self.room:getAlivePlayers(), function(p)
|
||||||
|
local ret = skill:targetFilter(p.id, selected_targets, { card }, card or Fk:cloneCard 'zixing')
|
||||||
|
return ret
|
||||||
|
end)
|
||||||
|
avail_targets = table.map(avail_targets, function(p)
|
||||||
|
return p.id
|
||||||
|
end)
|
||||||
|
|
||||||
|
if #avail_targets + #avail_cards < 1 then
|
||||||
|
break
|
||||||
|
end
|
||||||
|
table.insertIfNeed(selected_targets, table.random(avail_targets))
|
||||||
|
end
|
||||||
|
if skill:feasible(selected_targets, { card.id }, self.player, card) then
|
||||||
|
return json.encode {
|
||||||
|
card = table.random(avail_cards),
|
||||||
|
targets = selected_targets
|
||||||
|
}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return ""
|
||||||
end
|
end
|
||||||
|
|
||||||
---@param self RandomAI
|
---@param self RandomAI
|
||||||
random_cb.AskForResponseCard = function(self, jsonData)
|
random_cb.AskForResponseCard = function(self, jsonData)
|
||||||
local data = json.decode(jsonData)
|
local data = json.decode(jsonData)
|
||||||
local pattern = data[2]
|
local pattern = data[2]
|
||||||
local cancelable = true
|
local cancelable = true
|
||||||
local exp = Exppattern:Parse(pattern)
|
local exp = Exppattern:Parse(pattern)
|
||||||
local avail_cards = table.filter(self.player:getCardIds{Player.Hand, Player.Equip}, function(id)
|
local avail_cards = table.filter(self.player:getCardIds { Player.Hand, Player.Equip }, function(id)
|
||||||
return exp:match(Fk:getCardById(id))
|
return exp:match(Fk:getCardById(id))
|
||||||
end)
|
end)
|
||||||
if #avail_cards > 0 then
|
if #avail_cards > 0 then
|
||||||
return json.encode {
|
return json.encode {
|
||||||
card = table.random(avail_cards),
|
card = table.random(avail_cards),
|
||||||
targets = {}
|
targets = {}
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
-- TODO: vs skill
|
-- TODO: vs skill
|
||||||
return ""
|
return ""
|
||||||
end
|
end
|
||||||
|
|
||||||
---@param self RandomAI
|
---@param self RandomAI
|
||||||
random_cb.PlayCard = function(self, jsonData)
|
random_cb.PlayCard = function(self, jsonData)
|
||||||
local cards = table.map(self.player:getCardIds(Player.Hand), function(id)
|
local cards = table.map(self.player:getCardIds(Player.Hand), function(id)
|
||||||
return Fk:getCardById(id)
|
return Fk:getCardById(id)
|
||||||
end)
|
end)
|
||||||
local actives = table.filter(self.player:getAllSkills(), function(s)
|
local actives = table.filter(self.player:getAllSkills(), function(s)
|
||||||
return s:isInstanceOf(ActiveSkill)
|
return s:isInstanceOf(ActiveSkill)
|
||||||
end)
|
end)
|
||||||
local vss = table.filter(self.player:getAllSkills(), function(s)
|
local vss = table.filter(self.player:getAllSkills(), function(s)
|
||||||
return s:isInstanceOf(ViewAsSkill)
|
return s:isInstanceOf(ViewAsSkill)
|
||||||
end)
|
end)
|
||||||
table.insertTable(cards, actives)
|
table.insertTable(cards, actives)
|
||||||
table.insertTable(cards, vss)
|
table.insertTable(cards, vss)
|
||||||
|
|
||||||
while #cards > 0 do
|
while #cards > 0 do
|
||||||
local sth = table.random(cards)
|
local sth = table.random(cards)
|
||||||
if sth:isInstanceOf(Card) then
|
if sth:isInstanceOf(Card) then
|
||||||
local card = sth
|
local card = sth
|
||||||
local skill = card.skill ---@type ActiveSkill
|
local skill = card.skill ---@type ActiveSkill
|
||||||
if math.random() > 0.15 then
|
if math.random() > 0.15 then
|
||||||
local ret = useActiveSkill(self, skill, card)
|
local ret = useActiveSkill(self, skill, card)
|
||||||
if ret ~= "" then
|
if ret ~= "" then
|
||||||
return ret
|
return ret
|
||||||
end
|
|
||||||
table.removeOne(cards, card)
|
|
||||||
else
|
|
||||||
table.removeOne(cards, card)
|
|
||||||
end
|
|
||||||
elseif sth:isInstanceOf(ActiveSkill) then
|
|
||||||
local active = sth
|
|
||||||
if math.random() > 0.30 then
|
|
||||||
local ret = useActiveSkill(self, active, nil)
|
|
||||||
if ret ~= "" then
|
|
||||||
return ret
|
|
||||||
end
|
|
||||||
end
|
|
||||||
table.removeOne(cards, active)
|
|
||||||
else
|
|
||||||
local vs = sth
|
|
||||||
if math.random() > 0.20 then
|
|
||||||
local ret = useVSSkill(self, vs)
|
|
||||||
-- TODO: handle vs result
|
|
||||||
end
|
|
||||||
table.removeOne(cards, vs)
|
|
||||||
end
|
end
|
||||||
|
table.removeOne(cards, card)
|
||||||
|
else
|
||||||
|
table.removeOne(cards, card)
|
||||||
|
end
|
||||||
|
elseif sth:isInstanceOf(ActiveSkill) then
|
||||||
|
local active = sth
|
||||||
|
if math.random() > 0.30 then
|
||||||
|
local ret = useActiveSkill(self, active, nil)
|
||||||
|
if ret ~= "" then
|
||||||
|
return ret
|
||||||
|
end
|
||||||
|
end
|
||||||
|
table.removeOne(cards, active)
|
||||||
|
else
|
||||||
|
local vs = sth
|
||||||
|
if math.random() > 0.20 then
|
||||||
|
local ret = useVSSkill(self, vs)
|
||||||
|
-- TODO: handle vs result
|
||||||
|
end
|
||||||
|
table.removeOne(cards, vs)
|
||||||
end
|
end
|
||||||
|
end
|
||||||
|
|
||||||
return ""
|
return ""
|
||||||
end
|
end
|
||||||
|
|
||||||
function RandomAI:initialize(player)
|
function RandomAI:initialize(player)
|
||||||
AI.initialize(self, player)
|
AI.initialize(self, player)
|
||||||
self.cb_table = random_cb
|
self.cb_table = random_cb
|
||||||
end
|
end
|
||||||
|
|
||||||
return RandomAI
|
return RandomAI
|
||||||
|
|
|
@ -8,328 +8,6 @@ local trust_cb = {}
|
||||||
function TrustAI:initialize(player)
|
function TrustAI:initialize(player)
|
||||||
AI.initialize(self, player)
|
AI.initialize(self, player)
|
||||||
self.cb_table = trust_cb
|
self.cb_table = trust_cb
|
||||||
<<<<<<< HEAD
|
|
||||||
=======
|
|
||||||
self.player = player
|
|
||||||
self.room = RoomInstance or ClientInstance
|
|
||||||
|
|
||||||
fk.ai_role[player.id] = "neutral"
|
|
||||||
fk.roleValue[player.id] = {
|
|
||||||
lord = 0,
|
|
||||||
loyalist = 0,
|
|
||||||
rebel = 0,
|
|
||||||
renegade = 0
|
|
||||||
}
|
|
||||||
self:updatePlayers()
|
|
||||||
end
|
|
||||||
|
|
||||||
function TrustAI:isRolePredictable()
|
|
||||||
return self.room.settings.gameMode ~= "aaa_role_mode"
|
|
||||||
end
|
|
||||||
|
|
||||||
local function aliveRoles(room)
|
|
||||||
fk.alive_roles = {
|
|
||||||
lord = 0,
|
|
||||||
loyalist = 0,
|
|
||||||
rebel = 0,
|
|
||||||
renegade = 0
|
|
||||||
}
|
|
||||||
for _, ap in ipairs(room:getAllPlayers(false)) do
|
|
||||||
fk.alive_roles[ap.role] = 0
|
|
||||||
end
|
|
||||||
for _, ap in ipairs(room:getAlivePlayers(false)) do
|
|
||||||
fk.alive_roles[ap.role] = fk.alive_roles[ap.role] + 1
|
|
||||||
end
|
|
||||||
return fk.alive_roles
|
|
||||||
end
|
|
||||||
|
|
||||||
function TrustAI:objectiveLevel(to)
|
|
||||||
if self.player.id == to.id then
|
|
||||||
return -2
|
|
||||||
elseif #self.room:getAlivePlayers(false) < 3 then
|
|
||||||
return 5
|
|
||||||
end
|
|
||||||
local ars = aliveRoles(self.room)
|
|
||||||
if self:isRolePredictable() then
|
|
||||||
fk.ai_role[self.player.id] = self.role
|
|
||||||
fk.roleValue[self.player.id][self.role] = 666
|
|
||||||
if self.role == "renegade" then
|
|
||||||
fk.explicit_renegade = true
|
|
||||||
end
|
|
||||||
for _, p in ipairs(self.room:getAlivePlayers()) do
|
|
||||||
if
|
|
||||||
p.role == self.role or p.role == "lord" and self.role == "loyalist" or
|
|
||||||
p.role == "loyalist" and self.role == "lord"
|
|
||||||
then
|
|
||||||
table.insert(self.friends, p)
|
|
||||||
if p.id ~= self.player.id then
|
|
||||||
table.insert(self.friends_noself, p)
|
|
||||||
end
|
|
||||||
else
|
|
||||||
table.insert(self.enemies, p)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
elseif self.role == "renegade" then
|
|
||||||
if to.role == "lord" then
|
|
||||||
return -1
|
|
||||||
elseif ars.rebel < 1 then
|
|
||||||
return 4
|
|
||||||
elseif fk.ai_role[to.id] == "loyalist" then
|
|
||||||
return ars.lord + ars.loyalist - ars.rebel
|
|
||||||
elseif fk.ai_role[to.id] == "rebel" then
|
|
||||||
local r = ars.rebel - ars.lord + ars.loyalist
|
|
||||||
if r >= 0 then
|
|
||||||
return 3
|
|
||||||
else
|
|
||||||
return r
|
|
||||||
end
|
|
||||||
end
|
|
||||||
elseif self.role == "lord" or self.role == "loyalist" then
|
|
||||||
if fk.ai_role[to.id] == "rebel" then
|
|
||||||
return 5
|
|
||||||
elseif to.role == "lord" then
|
|
||||||
return -2
|
|
||||||
elseif ars.rebel < 1 then
|
|
||||||
if self.role == "lord" then
|
|
||||||
return fk.explicit_renegade and fk.ai_role[to.id] == "renegade" and 4 or to.hp > 1 and 2 or 0
|
|
||||||
elseif fk.explicit_renegade then
|
|
||||||
return fk.ai_role[to.id] == "renegade" and 4 or -1
|
|
||||||
else
|
|
||||||
return 3
|
|
||||||
end
|
|
||||||
elseif fk.ai_role[to.id] == "loyalist" then
|
|
||||||
return -2
|
|
||||||
elseif fk.ai_role[to.id] == "renegade" then
|
|
||||||
local r = ars.lord + ars.loyalist - ars.rebel
|
|
||||||
if r <= 0 then
|
|
||||||
return r
|
|
||||||
else
|
|
||||||
return 3
|
|
||||||
end
|
|
||||||
end
|
|
||||||
elseif self.role == "rebel" then
|
|
||||||
if to.role == "lord" then
|
|
||||||
return 5
|
|
||||||
elseif fk.ai_role[to.id] == "loyalist" then
|
|
||||||
return 4
|
|
||||||
elseif fk.ai_role[to.id] == "rebel" then
|
|
||||||
return -2
|
|
||||||
elseif fk.ai_role[to.id] == "renegade" then
|
|
||||||
local r = ars.rebel - ars.lord + ars.loyalist
|
|
||||||
if r > 0 then
|
|
||||||
return 1
|
|
||||||
else
|
|
||||||
return r
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
return 0
|
|
||||||
end
|
|
||||||
|
|
||||||
function TrustAI:updatePlayers(update)
|
|
||||||
self.role = self.player.role
|
|
||||||
local neutrality = {}
|
|
||||||
self.enemies = {}
|
|
||||||
self.friends = {}
|
|
||||||
self.friends_noself = {}
|
|
||||||
|
|
||||||
local aps = self.room:getAlivePlayers()
|
|
||||||
local function compare_func(a, b)
|
|
||||||
local v1 = fk.roleValue[a.id].rebel
|
|
||||||
local v2 = fk.roleValue[b.id].rebel
|
|
||||||
if v1 == v2 then
|
|
||||||
v1 = fk.roleValue[a.id].renegade
|
|
||||||
v2 = fk.roleValue[b.id].renegade
|
|
||||||
end
|
|
||||||
return v1 > v2
|
|
||||||
end
|
|
||||||
table.sort(aps, compare_func)
|
|
||||||
fk.explicit_renegade = false
|
|
||||||
local ars = aliveRoles(self.room)
|
|
||||||
local rebel, renegade, loyalist = 0, 0, 0
|
|
||||||
for _, ap in ipairs(aps) do
|
|
||||||
if ap.role == "lord" then
|
|
||||||
fk.ai_role[ap.id] = "loyalist"
|
|
||||||
elseif fk.roleValue[ap.id].rebel > 50 and ars.rebel > rebel then
|
|
||||||
rebel = rebel + 1
|
|
||||||
fk.ai_role[ap.id] = "rebel"
|
|
||||||
elseif fk.roleValue[ap.id].renegade > 50 and ars.renegade > renegade then
|
|
||||||
renegade = renegade + 1
|
|
||||||
fk.ai_role[ap.id] = "renegade"
|
|
||||||
fk.explicit_renegade = fk.roleValue[ap.id].renegade > 100
|
|
||||||
elseif fk.roleValue[ap.id].rebel < -50 and ars.loyalist > loyalist then
|
|
||||||
loyalist = loyalist + 1
|
|
||||||
fk.ai_role[ap.id] = "loyalist"
|
|
||||||
else
|
|
||||||
fk.ai_role[ap.id] = "neutral"
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
for n, p in ipairs(self.room:getAlivePlayers(false)) do
|
|
||||||
n = self:objectiveLevel(p)
|
|
||||||
if n < 0 then
|
|
||||||
table.insert(self.friends, p)
|
|
||||||
if p.id ~= self.player.id then
|
|
||||||
table.insert(self.friends_noself, p)
|
|
||||||
end
|
|
||||||
elseif n > 0 then
|
|
||||||
table.insert(self.enemies, p)
|
|
||||||
else
|
|
||||||
table.insert(neutrality, p)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
self:assignValue()
|
|
||||||
--[[
|
|
||||||
if self.enemies<1 and #neutrality>0
|
|
||||||
and#self.toUse<3 and self:getOverflow()>0
|
|
||||||
then
|
|
||||||
function compare_func(a,b)
|
|
||||||
return sgs.getDefense(a)<sgs.getDefense(b)
|
|
||||||
end
|
|
||||||
table.sort(neutrality,compare_func)
|
|
||||||
table.insert(self.enemies,neutrality[1])
|
|
||||||
end-]]
|
|
||||||
end
|
|
||||||
|
|
||||||
local function updateIntention(player, to, intention)
|
|
||||||
if player.id == to.id then
|
|
||||||
return
|
|
||||||
elseif player.role == "lord" then
|
|
||||||
fk.roleValue[to.id].rebel = fk.roleValue[to.id].rebel + intention * (200 - fk.roleValue[to.id].rebel) / 200
|
|
||||||
else
|
|
||||||
if to.role == "lord" or fk.ai_role[to.id] == "loyalist" then
|
|
||||||
fk.roleValue[player.id].rebel = fk.roleValue[player.id].rebel +
|
|
||||||
intention * (200 - fk.roleValue[player.id].rebel) / 200
|
|
||||||
elseif fk.ai_role[to.id] == "rebel" then
|
|
||||||
fk.roleValue[player.id].rebel = fk.roleValue[player.id].rebel -
|
|
||||||
intention * (fk.roleValue[player.id].rebel + 200) / 200
|
|
||||||
end
|
|
||||||
if fk.roleValue[player.id].rebel < 0 and intention > 0 or fk.roleValue[player.id].rebel > 0 and intention < 0 then
|
|
||||||
fk.roleValue[player.id].renegade = fk.roleValue[player.id].renegade +
|
|
||||||
intention * (100 - fk.roleValue[player.id].renegade) / 200
|
|
||||||
end
|
|
||||||
local aps = player.room:getAlivePlayers()
|
|
||||||
local function compare_func(a, b)
|
|
||||||
local v1 = fk.roleValue[a.id].rebel
|
|
||||||
local v2 = fk.roleValue[b.id].rebel
|
|
||||||
if v1 == v2 then
|
|
||||||
v1 = fk.roleValue[a.id].renegade
|
|
||||||
v2 = fk.roleValue[b.id].renegade
|
|
||||||
end
|
|
||||||
return v1 > v2
|
|
||||||
end
|
|
||||||
table.sort(aps, compare_func)
|
|
||||||
fk.explicit_renegade = false
|
|
||||||
local ars = aliveRoles(player.room)
|
|
||||||
local rebel, renegade, loyalist = 0, 0, 0
|
|
||||||
for _, ap in ipairs(aps) do
|
|
||||||
if ap.role == "lord" then
|
|
||||||
fk.ai_role[ap.id] = "loyalist"
|
|
||||||
elseif fk.roleValue[ap.id].rebel > 50 and ars.rebel > rebel then
|
|
||||||
rebel = rebel + 1
|
|
||||||
fk.ai_role[ap.id] = "rebel"
|
|
||||||
elseif fk.roleValue[ap.id].renegade > 50 and ars.renegade > renegade then
|
|
||||||
renegade = renegade + 1
|
|
||||||
fk.ai_role[ap.id] = "renegade"
|
|
||||||
fk.explicit_renegade = fk.roleValue[ap.id].renegade > 100
|
|
||||||
elseif fk.roleValue[ap.id].rebel < -50 and ars.loyalist > loyalist then
|
|
||||||
loyalist = loyalist + 1
|
|
||||||
fk.ai_role[ap.id] = "loyalist"
|
|
||||||
else
|
|
||||||
fk.ai_role[ap.id] = "neutral"
|
|
||||||
end
|
|
||||||
end
|
|
||||||
fk.qWarning(
|
|
||||||
player.general ..
|
|
||||||
" " ..
|
|
||||||
intention ..
|
|
||||||
" " ..
|
|
||||||
fk.ai_role[player.id] ..
|
|
||||||
" rebelValue:" .. fk.roleValue[player.id].rebel .. " renegadeValue:" .. fk.roleValue[player.id].renegade
|
|
||||||
) --]]
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
function TrustAI:filterEvent(event, player, data)
|
|
||||||
if event == fk.TargetSpecified then
|
|
||||||
local callback = fk.ai_card[data.card.name]
|
|
||||||
callback = callback and callback.intention
|
|
||||||
if type(callback) == "function" then
|
|
||||||
for _, p in ipairs(TargetGroup:getRealTargets(data.tos)) do
|
|
||||||
p = self.room:getPlayerById(p)
|
|
||||||
local intention = callback(p.ai, data.card, self.room:getPlayerById(data.from))
|
|
||||||
if type(intention) == "number" then
|
|
||||||
updateIntention(self.room:getPlayerById(data.from), p, intention)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
elseif type(callback) == "number" then
|
|
||||||
for _, p in ipairs(TargetGroup:getRealTargets(data.tos)) do
|
|
||||||
p = self.room:getPlayerById(p)
|
|
||||||
updateIntention(self.room:getPlayerById(data.from), p, callback)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
elseif event == fk.StartJudge then
|
|
||||||
fk.trick_judge[data.reason] = data.pattern
|
|
||||||
elseif event == fk.AfterCardsMove then
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
function TrustAI:isWeak(player, getAP)
|
|
||||||
player = player or self.player
|
|
||||||
if type(player) == "number" then
|
|
||||||
player = self.room:getPlayerById(player)
|
|
||||||
end
|
|
||||||
return player.hp < 2 or player.hp <= 2 and #player:getCardIds("&h") <= 2
|
|
||||||
end
|
|
||||||
|
|
||||||
function TrustAI:isFriend(pid, tid)
|
|
||||||
if tid then
|
|
||||||
local bt = self:isFriend(pid)
|
|
||||||
return bt ~= nil and bt == self:isFriend(tid)
|
|
||||||
end
|
|
||||||
if type(pid) == "number" then
|
|
||||||
pid = self.room:getPlayerById(pid)
|
|
||||||
end
|
|
||||||
local ve = self:objectiveLevel(pid)
|
|
||||||
if ve < 0 then
|
|
||||||
return true
|
|
||||||
elseif ve > 0 then
|
|
||||||
return false
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
function TrustAI:isEnemie(pid, tid)
|
|
||||||
if tid then
|
|
||||||
local bt = self:isFriend(pid)
|
|
||||||
return bt ~= nil and bt ~= self:isFriend(tid)
|
|
||||||
end
|
|
||||||
if type(pid) == "number" then
|
|
||||||
pid = self.room:getPlayerById(pid)
|
|
||||||
end
|
|
||||||
local ve = self:objectiveLevel(pid)
|
|
||||||
if ve > 0 then
|
|
||||||
return true
|
|
||||||
elseif ve < 0 then
|
|
||||||
return false
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
function TrustAI:eventData(game_event)
|
|
||||||
local event = self.room.logic:getCurrentEvent():findParent(GameEvent[game_event], true)
|
|
||||||
return event and event.data[1]
|
|
||||||
end
|
|
||||||
|
|
||||||
for _, n in ipairs(FileIO.ls("packages")) do
|
|
||||||
if FileIO.isDir("packages/" .. n) and FileIO.exists("packages/" .. n .. "/" .. n .. "_ai.lua") then
|
|
||||||
dofile("packages/" .. n .. "/" .. n .. "_ai.lua")
|
|
||||||
end
|
|
||||||
end
|
|
||||||
-- 加载两次拓展是为了能够引用,例如属性杀的使用直接套入普通杀的使用
|
|
||||||
for _, n in ipairs(FileIO.ls("packages")) do
|
|
||||||
if FileIO.isDir("packages/" .. n) and FileIO.exists("packages/" .. n .. "/" .. n .. "_ai.lua") then
|
|
||||||
dofile("packages/" .. n .. "/" .. n .. "_ai.lua")
|
|
||||||
end
|
|
||||||
>>>>>>> 79d3213cc0aa996c9072ae5696e387a9bda210d8
|
|
||||||
end
|
end
|
||||||
|
|
||||||
return TrustAI
|
return TrustAI
|
||||||
|
|
|
@ -129,3 +129,121 @@ fk.GeneralRevealed = 89
|
||||||
fk.GeneralHidden = 90
|
fk.GeneralHidden = 90
|
||||||
|
|
||||||
fk.NumOfEvents = 91
|
fk.NumOfEvents = 91
|
||||||
|
|
||||||
|
local events = {
|
||||||
|
"NonTrigger",
|
||||||
|
"GamePrepared",
|
||||||
|
"GameStart",
|
||||||
|
"BeforeTurnStart",
|
||||||
|
"TurnStart",
|
||||||
|
"TurnEnd",
|
||||||
|
"AfterTurnEnd",
|
||||||
|
"EventPhaseStart",
|
||||||
|
"EventPhaseProceeding",
|
||||||
|
"EventPhaseEnd",
|
||||||
|
"AfterPhaseEnd",
|
||||||
|
"EventPhaseChanging",
|
||||||
|
"EventPhaseSkipping",
|
||||||
|
|
||||||
|
"BeforeCardsMove",
|
||||||
|
"AfterCardsMove",
|
||||||
|
|
||||||
|
"DrawNCards",
|
||||||
|
"AfterDrawNCards",
|
||||||
|
"DrawInitialCards",
|
||||||
|
"AfterDrawInitialCards",
|
||||||
|
|
||||||
|
"PreHpRecover",
|
||||||
|
"HpRecover",
|
||||||
|
"PreHpLost",
|
||||||
|
"HpLost",
|
||||||
|
"BeforeHpChanged",
|
||||||
|
"HpChanged",
|
||||||
|
"MaxHpChanged",
|
||||||
|
|
||||||
|
"EventLoseSkill",
|
||||||
|
"EventAcquireSkill",
|
||||||
|
|
||||||
|
"StartJudge",
|
||||||
|
"AskForRetrial",
|
||||||
|
"FinishRetrial",
|
||||||
|
"FinishJudge",
|
||||||
|
|
||||||
|
"RoundStart",
|
||||||
|
"RoundEnd",
|
||||||
|
"BeforeTurnOver",
|
||||||
|
"TurnedOver",
|
||||||
|
"BeforeChainStateChange",
|
||||||
|
"ChainStateChanged",
|
||||||
|
|
||||||
|
"PreDamage",
|
||||||
|
"DamageCaused",
|
||||||
|
"DamageInflicted",
|
||||||
|
"Damage",
|
||||||
|
"Damaged",
|
||||||
|
"DamageFinished",
|
||||||
|
|
||||||
|
"EnterDying",
|
||||||
|
"Dying",
|
||||||
|
"AfterDying",
|
||||||
|
|
||||||
|
"PreCardUse",
|
||||||
|
"AfterCardUseDeclared",
|
||||||
|
"AfterCardTargetDeclared",
|
||||||
|
"CardUsing",
|
||||||
|
"BeforeCardUseEffect",
|
||||||
|
"TargetSpecifying",
|
||||||
|
"TargetConfirming",
|
||||||
|
"TargetSpecified",
|
||||||
|
"TargetConfirmed",
|
||||||
|
"CardUseFinished",
|
||||||
|
|
||||||
|
"PreCardRespond",
|
||||||
|
"CardResponding",
|
||||||
|
"CardRespondFinished",
|
||||||
|
|
||||||
|
"PreCardEffect",
|
||||||
|
"BeforeCardEffect",
|
||||||
|
"CardEffecting",
|
||||||
|
"CardEffectFinished",
|
||||||
|
"CardEffectCancelledOut",
|
||||||
|
|
||||||
|
"AskForPeaches",
|
||||||
|
"AskForPeachesDone",
|
||||||
|
"Death",
|
||||||
|
"BuryVictim",
|
||||||
|
"Deathed",
|
||||||
|
"BeforeGameOverJudge",
|
||||||
|
"GameOverJudge",
|
||||||
|
"GameFinished",
|
||||||
|
|
||||||
|
"AskForCardUse",
|
||||||
|
"AskForCardResponse",
|
||||||
|
|
||||||
|
"StartPindian",
|
||||||
|
"PindianCardsDisplayed",
|
||||||
|
"PindianResultConfirmed",
|
||||||
|
"PindianFinished",
|
||||||
|
|
||||||
|
"AfterDrawPileShuffle",
|
||||||
|
|
||||||
|
"BeforeTriggerSkillUse",
|
||||||
|
|
||||||
|
"BeforeDrawCard",
|
||||||
|
|
||||||
|
"CardShown",
|
||||||
|
|
||||||
|
"SkillEffect",
|
||||||
|
"AfterSkillEffect",
|
||||||
|
|
||||||
|
"AreaAborted",
|
||||||
|
"AreaResumed",
|
||||||
|
|
||||||
|
"GeneralRevealed",
|
||||||
|
"GeneralHidden",
|
||||||
|
|
||||||
|
"NumOfEvents"
|
||||||
|
}
|
||||||
|
for i, event in ipairs(events) do
|
||||||
|
fk[event] = i
|
||||||
|
end --]]
|
||||||
|
|
|
@ -194,16 +194,15 @@ end
|
||||||
GameEvent.functions[GameEvent.UseCard] = function(self)
|
GameEvent.functions[GameEvent.UseCard] = function(self)
|
||||||
local cardUseEvent = table.unpack(self.data)
|
local cardUseEvent = table.unpack(self.data)
|
||||||
local room = self.room
|
local room = self.room
|
||||||
local logic = room.logic
|
|
||||||
|
|
||||||
if cardUseEvent.card.skill then
|
if cardUseEvent.card.skill then
|
||||||
cardUseEvent.card.skill:onUse(room, cardUseEvent)
|
cardUseEvent.card.skill:onUse(room, cardUseEvent)
|
||||||
end
|
end
|
||||||
|
|
||||||
if logic:trigger(fk.PreCardUse, room:getPlayerById(cardUseEvent.from), cardUseEvent) then
|
if room.logic:trigger(fk.PreCardUse, room:getPlayerById(cardUseEvent.from), cardUseEvent) then
|
||||||
cardUseEvent.breakEvent = true
|
cardUseEvent.breakEvent = true
|
||||||
self.data = { cardUseEvent }
|
self.data = { cardUseEvent }
|
||||||
logic:breakEvent()
|
room.logic:breakEvent()
|
||||||
end
|
end
|
||||||
|
|
||||||
room:moveCardTo(cardUseEvent.card, Card.Processing, nil, fk.ReasonUse)
|
room:moveCardTo(cardUseEvent.card, Card.Processing, nil, fk.ReasonUse)
|
||||||
|
@ -223,7 +222,7 @@ GameEvent.functions[GameEvent.UseCard] = function(self)
|
||||||
break
|
break
|
||||||
end
|
end
|
||||||
|
|
||||||
logic:trigger(event, room:getPlayerById(cardUseEvent.from), cardUseEvent)
|
room.logic:trigger(event, room:getPlayerById(cardUseEvent.from), cardUseEvent)
|
||||||
if event == fk.CardUsing then
|
if event == fk.CardUsing then
|
||||||
room:doCardUseEffect(cardUseEvent)
|
room:doCardUseEffect(cardUseEvent)
|
||||||
end
|
end
|
||||||
|
@ -232,13 +231,12 @@ end
|
||||||
|
|
||||||
GameEvent.cleaners[GameEvent.UseCard] = function(self)
|
GameEvent.cleaners[GameEvent.UseCard] = function(self)
|
||||||
local cardUseEvent = table.unpack(self.data)
|
local cardUseEvent = table.unpack(self.data)
|
||||||
local room = self.room
|
|
||||||
|
|
||||||
room.logic:trigger(fk.CardUseFinished, room:getPlayerById(cardUseEvent.from), cardUseEvent)
|
self.room.logic:trigger(fk.CardUseFinished, self.room:getPlayerById(cardUseEvent.from), cardUseEvent)
|
||||||
|
|
||||||
local leftRealCardIds = room:getSubcardsByRule(cardUseEvent.card, { Card.Processing })
|
local leftRealCardIds = self.room:getSubcardsByRule(cardUseEvent.card, { Card.Processing })
|
||||||
if #leftRealCardIds > 0 then
|
if #leftRealCardIds > 0 then
|
||||||
room:moveCards({
|
self.room:moveCards({
|
||||||
ids = leftRealCardIds,
|
ids = leftRealCardIds,
|
||||||
toArea = Card.DiscardPile,
|
toArea = Card.DiscardPile,
|
||||||
moveReason = fk.ReasonUse
|
moveReason = fk.ReasonUse
|
||||||
|
@ -249,12 +247,11 @@ end
|
||||||
GameEvent.functions[GameEvent.RespondCard] = function(self)
|
GameEvent.functions[GameEvent.RespondCard] = function(self)
|
||||||
local cardResponseEvent = table.unpack(self.data)
|
local cardResponseEvent = table.unpack(self.data)
|
||||||
local room = self.room
|
local room = self.room
|
||||||
local logic = room.logic
|
|
||||||
|
|
||||||
if logic:trigger(fk.PreCardRespond, room:getPlayerById(cardResponseEvent.from), cardResponseEvent) then
|
if room.logic:trigger(fk.PreCardRespond, room:getPlayerById(cardResponseEvent.from), cardResponseEvent) then
|
||||||
cardResponseEvent.breakEvent = true
|
cardResponseEvent.breakEvent = true
|
||||||
self.data = { cardResponseEvent }
|
self.data = { cardResponseEvent }
|
||||||
logic:breakEvent()
|
room.logic:breakEvent()
|
||||||
end
|
end
|
||||||
|
|
||||||
local from = cardResponseEvent.customFrom or cardResponseEvent.from
|
local from = cardResponseEvent.customFrom or cardResponseEvent.from
|
||||||
|
@ -296,19 +293,18 @@ GameEvent.functions[GameEvent.RespondCard] = function(self)
|
||||||
if cardResponseEvent.retrial ~= true then
|
if cardResponseEvent.retrial ~= true then
|
||||||
playCardEmotionAndSound(room, room:getPlayerById(from), card)
|
playCardEmotionAndSound(room, room:getPlayerById(from), card)
|
||||||
end
|
end
|
||||||
logic:trigger(fk.CardResponding, room:getPlayerById(cardResponseEvent.from), cardResponseEvent)
|
room.logic:trigger(fk.CardResponding, room:getPlayerById(cardResponseEvent.from), cardResponseEvent)
|
||||||
self.data = { cardResponseEvent }
|
self.data = { cardResponseEvent }
|
||||||
end
|
end
|
||||||
|
|
||||||
GameEvent.cleaners[GameEvent.RespondCard] = function(self)
|
GameEvent.cleaners[GameEvent.RespondCard] = function(self)
|
||||||
local cardResponseEvent = table.unpack(self.data)
|
local cardResponseEvent = table.unpack(self.data)
|
||||||
local room = self.room
|
|
||||||
|
|
||||||
room.logic:trigger(fk.CardRespondFinished, room:getPlayerById(cardResponseEvent.from), cardResponseEvent)
|
self.room.logic:trigger(fk.CardRespondFinished, self.room:getPlayerById(cardResponseEvent.from), cardResponseEvent)
|
||||||
|
|
||||||
local realCardIds = room:getSubcardsByRule(cardResponseEvent.card, { Card.Processing })
|
local realCardIds = self.room:getSubcardsByRule(cardResponseEvent.card, { Card.Processing })
|
||||||
if #realCardIds > 0 and not cardResponseEvent.skipDrop then
|
if #realCardIds > 0 and not cardResponseEvent.skipDrop then
|
||||||
room:moveCards({
|
self.room:moveCards({
|
||||||
ids = realCardIds,
|
ids = realCardIds,
|
||||||
toArea = Card.DiscardPile,
|
toArea = Card.DiscardPile,
|
||||||
moveReason = fk.ReasonResonpse
|
moveReason = fk.ReasonResonpse
|
||||||
|
@ -319,34 +315,33 @@ end
|
||||||
GameEvent.functions[GameEvent.CardEffect] = function(self)
|
GameEvent.functions[GameEvent.CardEffect] = function(self)
|
||||||
local cardEffectEvent = table.unpack(self.data)
|
local cardEffectEvent = table.unpack(self.data)
|
||||||
local room = self.room
|
local room = self.room
|
||||||
local logic = room.logic
|
|
||||||
|
|
||||||
for _, event in ipairs({ fk.PreCardEffect, fk.BeforeCardEffect, fk.CardEffecting, fk.CardEffectFinished }) do
|
for _, event in ipairs({ fk.PreCardEffect, fk.BeforeCardEffect, fk.CardEffecting, fk.CardEffectFinished }) do
|
||||||
local user = cardEffectEvent.from and room:getPlayerById(cardEffectEvent.from) or nil
|
local user = cardEffectEvent.from and room:getPlayerById(cardEffectEvent.from) or nil
|
||||||
if cardEffectEvent.isCancellOut then
|
if cardEffectEvent.isCancellOut then
|
||||||
if logic:trigger(fk.CardEffectCancelledOut, user, cardEffectEvent) then
|
if room.logic:trigger(fk.CardEffectCancelledOut, user, cardEffectEvent) then
|
||||||
cardEffectEvent.isCancellOut = false
|
cardEffectEvent.isCancellOut = false
|
||||||
else
|
else
|
||||||
logic:breakEvent()
|
room.logic:breakEvent()
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
if not cardEffectEvent.toCard and
|
if not cardEffectEvent.toCard and
|
||||||
(not (room:getPlayerById(cardEffectEvent.to):isAlive() and cardEffectEvent.to) or
|
(not (room:getPlayerById(cardEffectEvent.to):isAlive() and cardEffectEvent.to) or
|
||||||
#room:deadPlayerFilter(TargetGroup:getRealTargets(cardEffectEvent.tos)) == 0) then
|
#room:deadPlayerFilter(TargetGroup:getRealTargets(cardEffectEvent.tos)) == 0) then
|
||||||
logic:breakEvent()
|
room.logic:breakEvent()
|
||||||
end
|
end
|
||||||
|
|
||||||
if table.contains((cardEffectEvent.nullifiedTargets or Util.DummyTable), cardEffectEvent.to) then
|
if table.contains((cardEffectEvent.nullifiedTargets or Util.DummyTable), cardEffectEvent.to) then
|
||||||
logic:breakEvent()
|
room.logic:breakEvent()
|
||||||
end
|
end
|
||||||
|
|
||||||
if event == fk.PreCardEffect then
|
if event == fk.PreCardEffect then
|
||||||
if cardEffectEvent.from and logic:trigger(event, room:getPlayerById(cardEffectEvent.from), cardEffectEvent) then
|
if cardEffectEvent.from and room.logic:trigger(event, room:getPlayerById(cardEffectEvent.from), cardEffectEvent) then
|
||||||
logic:breakEvent()
|
room.logic:breakEvent()
|
||||||
end
|
end
|
||||||
elseif cardEffectEvent.to and logic:trigger(event, room:getPlayerById(cardEffectEvent.to), cardEffectEvent) then
|
elseif cardEffectEvent.to and room.logic:trigger(event, room:getPlayerById(cardEffectEvent.to), cardEffectEvent) then
|
||||||
logic:breakEvent()
|
room.logic:breakEvent()
|
||||||
end
|
end
|
||||||
|
|
||||||
room:handleCardEffect(event, cardEffectEvent)
|
room:handleCardEffect(event, cardEffectEvent)
|
||||||
|
|
|
@ -13,424 +13,423 @@
|
||||||
local GameLogic = class("GameLogic")
|
local GameLogic = class("GameLogic")
|
||||||
|
|
||||||
function GameLogic:initialize(room)
|
function GameLogic:initialize(room)
|
||||||
self.room = room
|
self.room = room
|
||||||
self.skill_table = {} -- TriggerEvent --> TriggerSkill[]
|
self.skill_table = {} -- TriggerEvent --> TriggerSkill[]
|
||||||
self.skill_priority_table = {}
|
self.skill_priority_table = {}
|
||||||
self.refresh_skill_table = {}
|
self.refresh_skill_table = {}
|
||||||
self.skills = {} -- skillName[]
|
self.skills = {} -- skillName[]
|
||||||
self.game_event_stack = Stack:new()
|
self.game_event_stack = Stack:new()
|
||||||
self.all_game_events = {}
|
self.all_game_events = {}
|
||||||
self.event_recorder = {}
|
self.event_recorder = {}
|
||||||
self.current_event_id = 0
|
self.current_event_id = 0
|
||||||
|
|
||||||
self.role_table = {{"lord"}, {"lord", "rebel"}, {"lord", "rebel", "renegade"},
|
self.role_table = { { "lord" }, { "lord", "rebel" }, { "lord", "rebel", "renegade" },
|
||||||
{"lord", "loyalist", "rebel", "renegade"}, {"lord", "loyalist", "rebel", "rebel", "renegade"},
|
{ "lord", "loyalist", "rebel", "renegade" }, { "lord", "loyalist", "rebel", "rebel", "renegade" },
|
||||||
{"lord", "loyalist", "rebel", "rebel", "rebel", "renegade"},
|
{ "lord", "loyalist", "rebel", "rebel", "rebel", "renegade" },
|
||||||
{"lord", "loyalist", "loyalist", "rebel", "rebel", "rebel", "renegade"},
|
{ "lord", "loyalist", "loyalist", "rebel", "rebel", "rebel", "renegade" },
|
||||||
{"lord", "loyalist", "loyalist", "rebel", "rebel", "rebel", "rebel", "renegade"}}
|
{ "lord", "loyalist", "loyalist", "rebel", "rebel", "rebel", "rebel", "renegade" } }
|
||||||
end
|
end
|
||||||
|
|
||||||
function GameLogic:run()
|
function GameLogic:run()
|
||||||
-- default logic
|
-- default logic
|
||||||
local room = self.room
|
local room = self.room
|
||||||
table.shuffle(self.room.players)
|
table.shuffle(self.room.players)
|
||||||
self:assignRoles()
|
self:assignRoles()
|
||||||
self.room.game_started = true
|
self.room.game_started = true
|
||||||
room:doBroadcastNotify("StartGame", "")
|
room:doBroadcastNotify("StartGame", "")
|
||||||
room:adjustSeats()
|
room:adjustSeats()
|
||||||
|
|
||||||
self:chooseGenerals()
|
self:chooseGenerals()
|
||||||
|
|
||||||
self:buildPlayerCircle()
|
self:buildPlayerCircle()
|
||||||
self:broadcastGeneral()
|
self:broadcastGeneral()
|
||||||
self:prepareDrawPile()
|
self:prepareDrawPile()
|
||||||
self:attachSkillToPlayers()
|
self:attachSkillToPlayers()
|
||||||
self:prepareForStart()
|
self:prepareForStart()
|
||||||
|
|
||||||
self:action()
|
self:action()
|
||||||
end
|
end
|
||||||
|
|
||||||
local function execGameEvent(type, ...)
|
local function execGameEvent(type, ...)
|
||||||
local event = GameEvent:new(type, ...)
|
local event = GameEvent:new(type, ...)
|
||||||
local _, ret = event:exec()
|
local _, ret = event:exec()
|
||||||
return ret
|
return ret
|
||||||
end
|
end
|
||||||
|
|
||||||
function GameLogic:assignRoles()
|
function GameLogic:assignRoles()
|
||||||
local room = self.room
|
local room = self.room
|
||||||
local n = #room.players
|
local n = #room.players
|
||||||
local roles = self.role_table[n]
|
local roles = self.role_table[n]
|
||||||
table.shuffle(roles)
|
table.shuffle(roles)
|
||||||
|
|
||||||
for i = 1, n do
|
for i = 1, n do
|
||||||
local p = room.players[i]
|
local p = room.players[i]
|
||||||
p.role = roles[i]
|
p.role = roles[i]
|
||||||
if p.role == "lord" then
|
if p.role == "lord" then
|
||||||
p.role_shown = true
|
p.role_shown = true
|
||||||
room:broadcastProperty(p, "role")
|
room:broadcastProperty(p, "role")
|
||||||
else
|
else
|
||||||
room:notifyProperty(p, p, "role")
|
room:notifyProperty(p, p, "role")
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
function GameLogic:chooseGenerals()
|
function GameLogic:chooseGenerals()
|
||||||
local room = self.room
|
local room = self.room
|
||||||
local generalNum = room.settings.generalNum
|
local generalNum = room.settings.generalNum
|
||||||
local n = room.settings.enableDeputy and 2 or 1
|
local n = room.settings.enableDeputy and 2 or 1
|
||||||
local lord = room:getLord()
|
local lord = room:getLord()
|
||||||
local lord_generals = {}
|
local lord_generals = {}
|
||||||
|
|
||||||
if lord ~= nil then
|
if lord ~= nil then
|
||||||
room.current = lord
|
room.current = lord
|
||||||
local generals = {}
|
local generals = {}
|
||||||
local lordlist = {}
|
local lordlist = {}
|
||||||
local lordpools = {}
|
local lordpools = {}
|
||||||
if room.settings.gameMode == "aaa_role_mode" then
|
if room.settings.gameMode == "aaa_role_mode" then
|
||||||
for _, general in pairs(Fk:getAllGenerals()) do
|
for _, general in pairs(Fk:getAllGenerals()) do
|
||||||
if (not general.hidden and not general.total_hidden) and table.find(general.skills, function(s)
|
if (not general.hidden and not general.total_hidden) and table.find(general.skills, function(s)
|
||||||
return s.lordSkill
|
return s.lordSkill
|
||||||
end) and not table.find(lordlist, function(g)
|
end) and not table.find(lordlist, function(g)
|
||||||
return g.trueName == general.trueName
|
return g.trueName == general.trueName
|
||||||
end) then
|
end) then
|
||||||
table.insert(lordlist, general)
|
table.insert(lordlist, general)
|
||||||
end
|
|
||||||
end
|
|
||||||
lordlist = table.random(lordlist, 3) or {}
|
|
||||||
end
|
end
|
||||||
table.insertTable(generals, Fk:getGeneralsRandomly(generalNum, Fk:getAllGenerals(), nil, function(g)
|
end
|
||||||
return table.contains(table.map(lordlist, function(g)
|
lordlist = table.random(lordlist, 3) or {}
|
||||||
return g.trueName
|
end
|
||||||
end), g.trueName)
|
table.insertTable(generals, Fk:getGeneralsRandomly(generalNum, Fk:getAllGenerals(), nil, function(g)
|
||||||
end))
|
return table.contains(table.map(lordlist, function(g)
|
||||||
for i = 1, #generals do
|
return g.trueName
|
||||||
generals[i] = generals[i].name
|
end), g.trueName)
|
||||||
end
|
end))
|
||||||
lordpools = table.simpleClone(generals)
|
for i = 1, #generals do
|
||||||
table.insertTable(lordpools, table.map(lordlist, function(g)
|
generals[i] = generals[i].name
|
||||||
return g.name
|
end
|
||||||
end))
|
lordpools = table.simpleClone(generals)
|
||||||
lord_generals = room:askForGeneral(lord, lordpools, n)
|
table.insertTable(lordpools, table.map(lordlist, function(g)
|
||||||
local lord_general, deputy
|
return g.name
|
||||||
if type(lord_generals) == "table" then
|
end))
|
||||||
deputy = lord_generals[2]
|
lord_generals = room:askForGeneral(lord, lordpools, n)
|
||||||
lord_general = lord_generals[1]
|
local lord_general, deputy
|
||||||
else
|
if type(lord_generals) == "table" then
|
||||||
lord_general = lord_generals
|
deputy = lord_generals[2]
|
||||||
lord_generals = {lord_general}
|
lord_general = lord_generals[1]
|
||||||
end
|
else
|
||||||
|
lord_general = lord_generals
|
||||||
room:setPlayerGeneral(lord, lord_general, true)
|
lord_generals = { lord_general }
|
||||||
room:askForChooseKingdom({lord})
|
|
||||||
room:broadcastProperty(lord, "general")
|
|
||||||
room:broadcastProperty(lord, "kingdom")
|
|
||||||
room:setDeputyGeneral(lord, deputy)
|
|
||||||
room:broadcastProperty(lord, "deputyGeneral")
|
|
||||||
end
|
end
|
||||||
|
|
||||||
local nonlord = room:getOtherPlayers(lord, true)
|
room:setPlayerGeneral(lord, lord_general, true)
|
||||||
local generals = Fk:getGeneralsRandomly(#nonlord * generalNum, nil, lord_generals)
|
room:askForChooseKingdom({ lord })
|
||||||
table.shuffle(generals)
|
room:broadcastProperty(lord, "general")
|
||||||
for _, p in ipairs(nonlord) do
|
room:broadcastProperty(lord, "kingdom")
|
||||||
local arg = {}
|
room:setDeputyGeneral(lord, deputy)
|
||||||
for i = 1, generalNum do
|
room:broadcastProperty(lord, "deputyGeneral")
|
||||||
table.insert(arg, table.remove(generals, 1).name)
|
end
|
||||||
end
|
|
||||||
p.request_data = json.encode {arg, n}
|
local nonlord = room:getOtherPlayers(lord, true)
|
||||||
p.default_reply = table.random(arg, n)
|
local generals = Fk:getGeneralsRandomly(#nonlord * generalNum, nil, lord_generals)
|
||||||
|
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
|
end
|
||||||
|
p.request_data = json.encode { arg, n }
|
||||||
|
p.default_reply = table.random(arg, n)
|
||||||
|
end
|
||||||
|
|
||||||
room:notifyMoveFocus(nonlord, "AskForGeneral")
|
room:notifyMoveFocus(nonlord, "AskForGeneral")
|
||||||
room:doBroadcastRequest("AskForGeneral", nonlord)
|
room:doBroadcastRequest("AskForGeneral", nonlord)
|
||||||
|
|
||||||
for _, p in ipairs(nonlord) do
|
for _, p in ipairs(nonlord) do
|
||||||
if p.general == "" and p.reply_ready then
|
if p.general == "" and p.reply_ready then
|
||||||
local generals = json.decode(p.client_reply)
|
local generals = json.decode(p.client_reply)
|
||||||
local general = generals[1]
|
local general = generals[1]
|
||||||
local deputy = generals[2]
|
local deputy = generals[2]
|
||||||
room:setPlayerGeneral(p, general, true, true)
|
room:setPlayerGeneral(p, general, true, true)
|
||||||
room:setDeputyGeneral(p, deputy)
|
room:setDeputyGeneral(p, deputy)
|
||||||
else
|
else
|
||||||
room:setPlayerGeneral(p, p.default_reply[1], true, true)
|
room:setPlayerGeneral(p, p.default_reply[1], true, true)
|
||||||
room:setDeputyGeneral(p, p.default_reply[2])
|
room:setDeputyGeneral(p, p.default_reply[2])
|
||||||
end
|
|
||||||
p.default_reply = ""
|
|
||||||
end
|
end
|
||||||
|
p.default_reply = ""
|
||||||
|
end
|
||||||
|
|
||||||
room:askForChooseKingdom(nonlord)
|
room:askForChooseKingdom(nonlord)
|
||||||
end
|
end
|
||||||
|
|
||||||
function GameLogic:buildPlayerCircle()
|
function GameLogic:buildPlayerCircle()
|
||||||
local room = self.room
|
local room = self.room
|
||||||
local players = room.players
|
local players = room.players
|
||||||
room.alive_players = {table.unpack(players)}
|
room.alive_players = { table.unpack(players) }
|
||||||
for i = 1, #players - 1 do
|
for i = 1, #players - 1 do
|
||||||
players[i].next = players[i + 1]
|
players[i].next = players[i + 1]
|
||||||
end
|
end
|
||||||
players[#players].next = players[1]
|
players[#players].next = players[1]
|
||||||
end
|
end
|
||||||
|
|
||||||
function GameLogic:broadcastGeneral()
|
function GameLogic:broadcastGeneral()
|
||||||
local room = self.room
|
local room = self.room
|
||||||
local players = room.players
|
local players = room.players
|
||||||
|
|
||||||
for _, p in ipairs(players) do
|
for _, p in ipairs(players) do
|
||||||
assert(p.general ~= "")
|
assert(p.general ~= "")
|
||||||
local general = Fk.generals[p.general]
|
local general = Fk.generals[p.general]
|
||||||
local deputy = Fk.generals[p.deputyGeneral]
|
local deputy = Fk.generals[p.deputyGeneral]
|
||||||
p.maxHp = p:getGeneralMaxHp()
|
p.maxHp = p:getGeneralMaxHp()
|
||||||
p.hp = deputy and math.floor((deputy.hp + general.hp) / 2) or general.hp
|
p.hp = deputy and math.floor((deputy.hp + general.hp) / 2) or general.hp
|
||||||
p.shield = math.min(general.shield + (deputy and deputy.shield or 0), 5)
|
p.shield = math.min(general.shield + (deputy and deputy.shield or 0), 5)
|
||||||
-- TODO: setup AI here
|
-- TODO: setup AI here
|
||||||
|
|
||||||
if p.role ~= "lord" then
|
if p.role ~= "lord" then
|
||||||
room:broadcastProperty(p, "general")
|
room:broadcastProperty(p, "general")
|
||||||
room:broadcastProperty(p, "kingdom")
|
room:broadcastProperty(p, "kingdom")
|
||||||
room:broadcastProperty(p, "deputyGeneral")
|
room:broadcastProperty(p, "deputyGeneral")
|
||||||
elseif #players >= 5 then
|
elseif #players >= 5 then
|
||||||
p.maxHp = p.maxHp + 1
|
p.maxHp = p.maxHp + 1
|
||||||
p.hp = p.hp + 1
|
p.hp = p.hp + 1
|
||||||
end
|
|
||||||
room:broadcastProperty(p, "maxHp")
|
|
||||||
room:broadcastProperty(p, "hp")
|
|
||||||
room:broadcastProperty(p, "shield")
|
|
||||||
end
|
end
|
||||||
|
room:broadcastProperty(p, "maxHp")
|
||||||
|
room:broadcastProperty(p, "hp")
|
||||||
|
room:broadcastProperty(p, "shield")
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
function GameLogic:prepareDrawPile()
|
function GameLogic:prepareDrawPile()
|
||||||
local room = self.room
|
local room = self.room
|
||||||
local allCardIds = Fk:getAllCardIds()
|
local allCardIds = Fk:getAllCardIds()
|
||||||
|
|
||||||
for i = #allCardIds, 1, -1 do
|
for i = #allCardIds, 1, -1 do
|
||||||
if Fk:getCardById(allCardIds[i]).is_derived then
|
if Fk:getCardById(allCardIds[i]).is_derived then
|
||||||
local id = allCardIds[i]
|
local id = allCardIds[i]
|
||||||
table.removeOne(allCardIds, id)
|
table.removeOne(allCardIds, id)
|
||||||
table.insert(room.void, id)
|
table.insert(room.void, id)
|
||||||
room:setCardArea(id, Card.Void, nil)
|
room:setCardArea(id, Card.Void, nil)
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
end
|
||||||
|
|
||||||
table.shuffle(allCardIds)
|
table.shuffle(allCardIds)
|
||||||
room.draw_pile = allCardIds
|
room.draw_pile = allCardIds
|
||||||
for _, id in ipairs(room.draw_pile) do
|
for _, id in ipairs(room.draw_pile) do
|
||||||
self.room:setCardArea(id, Card.DrawPile, nil)
|
self.room:setCardArea(id, Card.DrawPile, nil)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
function GameLogic:attachSkillToPlayers()
|
function GameLogic:attachSkillToPlayers()
|
||||||
local room = self.room
|
local room = self.room
|
||||||
local players = room.players
|
local players = room.players
|
||||||
|
|
||||||
local addRoleModSkills = function(player, skillName)
|
local addRoleModSkills = function(player, skillName)
|
||||||
local skill = Fk.skills[skillName]
|
local skill = Fk.skills[skillName]
|
||||||
if skill.lordSkill and (player.role ~= "lord" or #room.players < 5) then
|
if skill.lordSkill and (player.role ~= "lord" or #room.players < 5) then
|
||||||
return
|
return
|
||||||
end
|
|
||||||
|
|
||||||
if #skill.attachedKingdom > 0 and not table.contains(skill.attachedKingdom, player.kingdom) then
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
room:handleAddLoseSkills(player, skillName, nil, false)
|
|
||||||
end
|
end
|
||||||
for _, p in ipairs(room.alive_players) do
|
|
||||||
local skills = Fk.generals[p.general].skills
|
|
||||||
for _, s in ipairs(skills) do
|
|
||||||
addRoleModSkills(p, s.name)
|
|
||||||
end
|
|
||||||
for _, sname in ipairs(Fk.generals[p.general].other_skills) do
|
|
||||||
addRoleModSkills(p, sname)
|
|
||||||
end
|
|
||||||
|
|
||||||
local deputy = Fk.generals[p.deputyGeneral]
|
if #skill.attachedKingdom > 0 and not table.contains(skill.attachedKingdom, player.kingdom) then
|
||||||
if deputy then
|
return
|
||||||
skills = deputy.skills
|
|
||||||
for _, s in ipairs(skills) do
|
|
||||||
addRoleModSkills(p, s.name)
|
|
||||||
end
|
|
||||||
for _, sname in ipairs(deputy.other_skills) do
|
|
||||||
addRoleModSkills(p, sname)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
room:handleAddLoseSkills(player, skillName, nil, false)
|
||||||
|
end
|
||||||
|
for _, p in ipairs(room.alive_players) do
|
||||||
|
local skills = Fk.generals[p.general].skills
|
||||||
|
for _, s in ipairs(skills) do
|
||||||
|
addRoleModSkills(p, s.name)
|
||||||
|
end
|
||||||
|
for _, sname in ipairs(Fk.generals[p.general].other_skills) do
|
||||||
|
addRoleModSkills(p, sname)
|
||||||
|
end
|
||||||
|
|
||||||
|
local deputy = Fk.generals[p.deputyGeneral]
|
||||||
|
if deputy then
|
||||||
|
skills = deputy.skills
|
||||||
|
for _, s in ipairs(skills) do
|
||||||
|
addRoleModSkills(p, s.name)
|
||||||
|
end
|
||||||
|
for _, sname in ipairs(deputy.other_skills) do
|
||||||
|
addRoleModSkills(p, sname)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
function GameLogic:prepareForStart()
|
function GameLogic:prepareForStart()
|
||||||
local room = self.room
|
local room = self.room
|
||||||
local players = room.players
|
local players = room.players
|
||||||
|
|
||||||
self:addTriggerSkill(GameRule)
|
self:addTriggerSkill(GameRule)
|
||||||
for _, trig in ipairs(Fk.global_trigger) do
|
for _, trig in ipairs(Fk.global_trigger) do
|
||||||
self:addTriggerSkill(trig)
|
self:addTriggerSkill(trig)
|
||||||
end
|
end
|
||||||
|
|
||||||
self.room:sendLog{
|
self.room:sendLog {
|
||||||
type = "$GameStart"
|
type = "$GameStart"
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
function GameLogic:action()
|
function GameLogic:action()
|
||||||
self:trigger(fk.GamePrepared)
|
self:trigger(fk.GamePrepared)
|
||||||
local room = self.room
|
local room = self.room
|
||||||
|
|
||||||
execGameEvent(GameEvent.DrawInitial)
|
execGameEvent(GameEvent.DrawInitial)
|
||||||
|
|
||||||
while true do
|
while true do
|
||||||
execGameEvent(GameEvent.Round)
|
execGameEvent(GameEvent.Round)
|
||||||
if room.game_finished then
|
if room.game_finished then
|
||||||
break
|
break
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
---@param skill TriggerSkill
|
---@param skill TriggerSkill
|
||||||
function GameLogic:addTriggerSkill(skill)
|
function GameLogic:addTriggerSkill(skill)
|
||||||
if skill == nil or table.contains(self.skills, skill.name) then
|
if skill == nil or table.contains(self.skills, skill.name) then
|
||||||
return
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
table.insert(self.skills, skill.name)
|
||||||
|
|
||||||
|
for _, event in ipairs(skill.refresh_events) do
|
||||||
|
if self.refresh_skill_table[event] == nil then
|
||||||
|
self.refresh_skill_table[event] = {}
|
||||||
|
end
|
||||||
|
table.insert(self.refresh_skill_table[event], skill)
|
||||||
|
end
|
||||||
|
|
||||||
|
for _, event in ipairs(skill.events) do
|
||||||
|
if self.skill_table[event] == nil then
|
||||||
|
self.skill_table[event] = {}
|
||||||
|
end
|
||||||
|
table.insert(self.skill_table[event], skill)
|
||||||
|
|
||||||
|
if self.skill_priority_table[event] == nil then
|
||||||
|
self.skill_priority_table[event] = {}
|
||||||
end
|
end
|
||||||
|
|
||||||
table.insert(self.skills, skill.name)
|
local priority_tab = self.skill_priority_table[event]
|
||||||
|
local prio = skill.priority_table[event]
|
||||||
for _, event in ipairs(skill.refresh_events) do
|
if not table.contains(priority_tab, prio) then
|
||||||
if self.refresh_skill_table[event] == nil then
|
for i, v in ipairs(priority_tab) do
|
||||||
self.refresh_skill_table[event] = {}
|
if v < prio then
|
||||||
|
table.insert(priority_tab, i, prio)
|
||||||
|
break
|
||||||
end
|
end
|
||||||
table.insert(self.refresh_skill_table[event], skill)
|
end
|
||||||
|
|
||||||
|
if not table.contains(priority_tab, prio) then
|
||||||
|
table.insert(priority_tab, prio)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
for _, event in ipairs(skill.events) do
|
if not table.contains(self.skill_priority_table[event], skill.priority_table[event]) then
|
||||||
if self.skill_table[event] == nil then
|
table.insert(self.skill_priority_table[event], skill.priority_table[event])
|
||||||
self.skill_table[event] = {}
|
|
||||||
end
|
|
||||||
table.insert(self.skill_table[event], skill)
|
|
||||||
|
|
||||||
if self.skill_priority_table[event] == nil then
|
|
||||||
self.skill_priority_table[event] = {}
|
|
||||||
end
|
|
||||||
|
|
||||||
local priority_tab = self.skill_priority_table[event]
|
|
||||||
local prio = skill.priority_table[event]
|
|
||||||
if not table.contains(priority_tab, prio) then
|
|
||||||
for i, v in ipairs(priority_tab) do
|
|
||||||
if v < prio then
|
|
||||||
table.insert(priority_tab, i, prio)
|
|
||||||
break
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
if not table.contains(priority_tab, prio) then
|
|
||||||
table.insert(priority_tab, prio)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
if not table.contains(self.skill_priority_table[event], skill.priority_table[event]) then
|
|
||||||
|
|
||||||
table.insert(self.skill_priority_table[event], skill.priority_table[event])
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
end
|
||||||
|
|
||||||
if skill.visible then
|
if skill.visible then
|
||||||
if (Fk.related_skills[skill.name] == nil) then
|
if (Fk.related_skills[skill.name] == nil) then
|
||||||
return
|
return
|
||||||
end
|
|
||||||
for _, s in ipairs(Fk.related_skills[skill.name]) do
|
|
||||||
if (s.class == TriggerSkill) then
|
|
||||||
self:addTriggerSkill(s)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
for _, s in ipairs(Fk.related_skills[skill.name]) do
|
||||||
|
if (s.class == TriggerSkill) then
|
||||||
|
self:addTriggerSkill(s)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
---@param event Event
|
---@param event Event
|
||||||
---@param target ServerPlayer|nil
|
---@param target ServerPlayer|nil
|
||||||
---@param data any|nil
|
---@param data any|nil
|
||||||
function GameLogic:trigger(event, target, data, refresh_only)
|
function GameLogic:trigger(event, target, data, refresh_only)
|
||||||
local room = self.room
|
local room = self.room
|
||||||
local broken = false
|
local broken = false
|
||||||
local skills = self.skill_table[event] or {}
|
local skills = self.skill_table[event] or {}
|
||||||
local skills_to_refresh = self.refresh_skill_table[event] or Util.DummyTable
|
local skills_to_refresh = self.refresh_skill_table[event] or Util.DummyTable
|
||||||
local _target = room.current -- for iteration
|
local _target = room.current -- for iteration
|
||||||
local player = _target
|
local player = _target
|
||||||
if #skills_to_refresh > 0 then
|
if #skills_to_refresh > 0 then
|
||||||
repeat
|
repeat
|
||||||
do
|
do
|
||||||
-- refresh skills. This should not be broken
|
-- refresh skills. This should not be broken
|
||||||
for _, skill in ipairs(skills_to_refresh) do
|
for _, skill in ipairs(skills_to_refresh) do
|
||||||
if skill:canRefresh(event, target, player, data) then
|
if skill:canRefresh(event, target, player, data) then
|
||||||
skill:refresh(event, target, player, data)
|
skill:refresh(event, target, player, data)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
player = player.next
|
player = player.next
|
||||||
end
|
end
|
||||||
until player == _target
|
until player == _target
|
||||||
|
end
|
||||||
|
|
||||||
|
if #skills == 0 or refresh_only then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
local prio_tab = self.skill_priority_table[event]
|
||||||
|
local prev_prio = math.huge
|
||||||
|
|
||||||
|
for _, prio in ipairs(prio_tab) do
|
||||||
|
if broken then
|
||||||
|
break
|
||||||
|
end
|
||||||
|
if prio >= prev_prio then
|
||||||
|
-- continue
|
||||||
|
goto trigger_loop_continue
|
||||||
end
|
end
|
||||||
|
|
||||||
if #skills == 0 or refresh_only then
|
repeat
|
||||||
return
|
do
|
||||||
end
|
local triggerables = table.filter(skills, function(skill)
|
||||||
|
return skill.priority_table[event] == prio and skill:triggerable(event, target, player, data)
|
||||||
|
end)
|
||||||
|
|
||||||
local prio_tab = self.skill_priority_table[event]
|
local skill_names = table.map(triggerables, function(skill)
|
||||||
local prev_prio = math.huge
|
return skill.name
|
||||||
|
end)
|
||||||
|
|
||||||
for _, prio in ipairs(prio_tab) do
|
while #skill_names > 0 do
|
||||||
if broken then
|
local skill_name = prio <= 0 and table.random(skill_names) or
|
||||||
|
room:askForChoice(player, skill_names, "trigger", "#choose-trigger")
|
||||||
|
|
||||||
|
local skill = skill_name == "game_rule" and GameRule or Fk.skills[skill_name]
|
||||||
|
|
||||||
|
local len = #skills
|
||||||
|
broken = skill:trigger(event, target, player, data)
|
||||||
|
|
||||||
|
table.insertTable(skill_names,
|
||||||
|
table.map(table.filter(table.slice(skills, len - #skills), function(s)
|
||||||
|
return s.priority_table[event] == prio and s:triggerable(event, target, player, data)
|
||||||
|
end), function(s)
|
||||||
|
return s.name
|
||||||
|
end))
|
||||||
|
|
||||||
|
broken = broken or (event == fk.AskForPeaches and room:getPlayerById(data.who).hp > 0)
|
||||||
|
|
||||||
|
if broken then
|
||||||
break
|
break
|
||||||
|
end
|
||||||
|
table.removeOne(skill_names, skill_name)
|
||||||
end
|
end
|
||||||
if prio >= prev_prio then
|
if broken then
|
||||||
-- continue
|
break
|
||||||
goto trigger_loop_continue
|
|
||||||
end
|
end
|
||||||
|
player = player.next
|
||||||
|
end
|
||||||
|
until player == _target
|
||||||
|
|
||||||
repeat
|
prev_prio = prio
|
||||||
do
|
::trigger_loop_continue::
|
||||||
local triggerables = table.filter(skills, function(skill)
|
end
|
||||||
return skill.priority_table[event] == prio and skill:triggerable(event, target, player, data)
|
_target.ai:filterEvent(event, target, data)
|
||||||
end)
|
return broken
|
||||||
|
|
||||||
local skill_names = table.map(triggerables, function(skill)
|
|
||||||
return skill.name
|
|
||||||
end)
|
|
||||||
|
|
||||||
while #skill_names > 0 do
|
|
||||||
local skill_name = prio <= 0 and table.random(skill_names) or
|
|
||||||
room:askForChoice(player, skill_names, "trigger", "#choose-trigger")
|
|
||||||
|
|
||||||
local skill = skill_name == "game_rule" and GameRule or Fk.skills[skill_name]
|
|
||||||
|
|
||||||
local len = #skills
|
|
||||||
broken = skill:trigger(event, target, player, data)
|
|
||||||
|
|
||||||
table.insertTable(skill_names,
|
|
||||||
table.map(table.filter(table.slice(skills, len - #skills), function(s)
|
|
||||||
return s.priority_table[event] == prio and s:triggerable(event, target, player, data)
|
|
||||||
end), function(s)
|
|
||||||
return s.name
|
|
||||||
end))
|
|
||||||
|
|
||||||
broken = broken or (event == fk.AskForPeaches and room:getPlayerById(data.who).hp > 0)
|
|
||||||
|
|
||||||
if broken then
|
|
||||||
break
|
|
||||||
end
|
|
||||||
table.removeOne(skill_names, skill_name)
|
|
||||||
end
|
|
||||||
if broken then
|
|
||||||
break
|
|
||||||
end
|
|
||||||
player = player.next
|
|
||||||
end
|
|
||||||
until player == _target
|
|
||||||
|
|
||||||
prev_prio = prio
|
|
||||||
::trigger_loop_continue::
|
|
||||||
end
|
|
||||||
_target.ai:filterEvent(event, target, data)
|
|
||||||
return broken
|
|
||||||
end
|
end
|
||||||
|
|
||||||
---@return GameEvent
|
---@return GameEvent
|
||||||
function GameLogic:getCurrentEvent()
|
function GameLogic:getCurrentEvent()
|
||||||
return self.game_event_stack.t[self.game_event_stack.p]
|
return self.game_event_stack.t[self.game_event_stack.p]
|
||||||
end
|
end
|
||||||
|
|
||||||
-- 在指定历史范围中找至多n个符合条件的事件
|
-- 在指定历史范围中找至多n个符合条件的事件
|
||||||
|
@ -440,90 +439,89 @@ end
|
||||||
---@param scope integer @ 查询历史范围,只能是当前阶段/回合/轮次
|
---@param scope integer @ 查询历史范围,只能是当前阶段/回合/轮次
|
||||||
---@return GameEvent[] @ 找到的符合条件的所有事件,最多n个但不保证有n个
|
---@return GameEvent[] @ 找到的符合条件的所有事件,最多n个但不保证有n个
|
||||||
function GameLogic:getEventsOfScope(eventType, n, func, scope)
|
function GameLogic:getEventsOfScope(eventType, n, func, scope)
|
||||||
scope = scope or Player.HistoryTurn
|
scope = scope or Player.HistoryTurn
|
||||||
local event = self:getCurrentEvent()
|
local event = self:getCurrentEvent()
|
||||||
local start_event ---@type GameEvent
|
local start_event ---@type GameEvent
|
||||||
if scope == Player.HistoryGame then
|
if scope == Player.HistoryGame then
|
||||||
start_event = self.all_game_events[1]
|
start_event = self.all_game_events[1]
|
||||||
elseif scope == Player.HistoryRound then
|
elseif scope == Player.HistoryRound then
|
||||||
start_event = event:findParent(GameEvent.Round, true)
|
start_event = event:findParent(GameEvent.Round, true)
|
||||||
elseif scope == Player.HistoryTurn then
|
elseif scope == Player.HistoryTurn then
|
||||||
start_event = event:findParent(GameEvent.Turn, true)
|
start_event = event:findParent(GameEvent.Turn, true)
|
||||||
elseif scope == Player.HistoryPhase then
|
elseif scope == Player.HistoryPhase then
|
||||||
start_event = event:findParent(GameEvent.Phase, true)
|
start_event = event:findParent(GameEvent.Phase, true)
|
||||||
end
|
end
|
||||||
|
|
||||||
return start_event:searchEvents(eventType, n, func)
|
return start_event:searchEvents(eventType, n, func)
|
||||||
end
|
end
|
||||||
|
|
||||||
function GameLogic:dumpEventStack(detailed)
|
function GameLogic:dumpEventStack(detailed)
|
||||||
local top = self:getCurrentEvent()
|
local top = self:getCurrentEvent()
|
||||||
local i = self.game_event_stack.p
|
local i = self.game_event_stack.p
|
||||||
local inspect = p
|
local inspect = p
|
||||||
if not top then
|
if not top then
|
||||||
return
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
print("===== Start of event stack dump =====")
|
||||||
|
if not detailed then
|
||||||
|
print("")
|
||||||
|
end
|
||||||
|
|
||||||
|
repeat
|
||||||
|
local printable_data
|
||||||
|
if type(top.data) ~= "table" then
|
||||||
|
printable_data = top.data
|
||||||
|
else
|
||||||
|
printable_data = table.cloneWithoutClass(top.data)
|
||||||
end
|
end
|
||||||
|
|
||||||
print("===== Start of event stack dump =====")
|
|
||||||
if not detailed then
|
if not detailed then
|
||||||
print("")
|
print("Stack level #" .. i .. ": " .. tostring(top))
|
||||||
|
else
|
||||||
|
print("\nStack level #" .. i .. ":")
|
||||||
|
inspect {
|
||||||
|
eventId = GameEvent:translate(top.event),
|
||||||
|
data = printable_data or "nil"
|
||||||
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
repeat
|
top = top.parent
|
||||||
local printable_data
|
i = i - 1
|
||||||
if type(top.data) ~= "table" then
|
until not top
|
||||||
printable_data = top.data
|
|
||||||
else
|
|
||||||
printable_data = table.cloneWithoutClass(top.data)
|
|
||||||
end
|
|
||||||
|
|
||||||
if not detailed then
|
print("\n===== End of event stack dump =====")
|
||||||
print("Stack level #" .. i .. ": " .. tostring(top))
|
|
||||||
else
|
|
||||||
print("\nStack level #" .. i .. ":")
|
|
||||||
inspect {
|
|
||||||
eventId = GameEvent:translate(top.event),
|
|
||||||
data = printable_data or "nil"
|
|
||||||
}
|
|
||||||
end
|
|
||||||
|
|
||||||
top = top.parent
|
|
||||||
i = i - 1
|
|
||||||
until not top
|
|
||||||
|
|
||||||
print("\n===== End of event stack dump =====")
|
|
||||||
end
|
end
|
||||||
|
|
||||||
function GameLogic:dumpAllEvents(from, to)
|
function GameLogic:dumpAllEvents(from, to)
|
||||||
from = from or 1
|
from = from or 1
|
||||||
to = to or #self.all_game_events
|
to = to or #self.all_game_events
|
||||||
assert(from <= to)
|
assert(from <= to)
|
||||||
|
local indent = 0
|
||||||
local indent = 0
|
local tab = " "
|
||||||
local tab = " "
|
for i = from, to, 1 do
|
||||||
for i = from, to, 1 do
|
local v = self.all_game_events[i]
|
||||||
local v = self.all_game_events[i]
|
if type(v) ~= "table" then
|
||||||
if type(v) ~= "table" then
|
indent = math.max(indent - 1, 0)
|
||||||
indent = math.max(indent - 1, 0)
|
-- v = "End"
|
||||||
-- v = "End"
|
-- print(tab:rep(indent) .. string.format("#%d: %s", i, v))
|
||||||
-- print(tab:rep(indent) .. string.format("#%d: %s", i, v))
|
else
|
||||||
else
|
print(tab:rep(indent) .. string.format("%s", tostring(v)))
|
||||||
print(tab:rep(indent) .. string.format("%s", tostring(v)))
|
if v.id ~= v.end_id then
|
||||||
if v.id ~= v.end_id then
|
indent = indent + 1
|
||||||
indent = indent + 1
|
end
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
function GameLogic:breakEvent(ret)
|
function GameLogic:breakEvent(ret)
|
||||||
self.room.breakEvent = true
|
self.room.breakEvent = true
|
||||||
coroutine.yield("__breakEvent", ret)
|
coroutine.yield("__breakEvent", ret)
|
||||||
end
|
end
|
||||||
|
|
||||||
function GameLogic:breakTurn()
|
function GameLogic:breakTurn()
|
||||||
local event = self:getCurrentEvent():findParent(GameEvent.Turn)
|
local event = self:getCurrentEvent():findParent(GameEvent.Turn)
|
||||||
event:shutdown()
|
event:shutdown()
|
||||||
end
|
end
|
||||||
|
|
||||||
return GameLogic
|
return GameLogic
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
dir=FreeKill-${FK_VER}
|
dir=FreeKill-${FK_VER}
|
||||||
mkdir $dir
|
mkdir $dir
|
||||||
echo Copying
|
echo Copying
|
||||||
cp -r ./Freekill/.git $dir
|
cp -r ./FreeKill/.git $dir
|
||||||
|
|
||||||
cd $dir
|
cd $dir
|
||||||
git restore .
|
git restore .
|
||||||
|
|
|
@ -8,10 +8,10 @@
|
||||||
给FreeKill的GitHub仓库点一个star吧!
|
给FreeKill的GitHub仓库点一个star吧!
|
||||||
你可以在武将一览界面禁将,在你创建的房间中禁用武将不会出现在选将框。
|
你可以在武将一览界面禁将,在你创建的房间中禁用武将不会出现在选将框。
|
||||||
如果你遇到了bug,请截图并反馈给开发者
|
如果你遇到了bug,请截图并反馈给开发者
|
||||||
如果你想要制作FK的mod,需要先学习Lua语言
|
如果你想要制作新月杀的mod,需要先学习Lua语言
|
||||||
目光所及短寸吾才之间满腹狭目之阿瞒有我见袁本良计初只能窥取冀州便是底竟不从易成略在胸如反掌之良计速出吾才满目光所及腹袁本初竟短寸之间不从之
|
目光所及短寸吾才之间满腹狭目之阿瞒有我见袁本良计初只能窥取冀州便是底竟不从易成略在胸如反掌之良计速出吾才满目光所及腹袁本初竟短寸之间不从之
|
||||||
吔!
|
吔!
|
||||||
想要参与到FK的开发工作中?那么就从制作自己的Mod开始吧!
|
想要参与到新月杀的开发工作中?那么就从制作自己的Mod开始吧!
|
||||||
在游戏的一些地方,长按可以显示该元素的详情。如果你用的是电脑版的话可以用鼠标右键代替长按。
|
在游戏的一些地方,长按可以显示该元素的详情。如果你用的是电脑版的话可以用鼠标右键代替长按。
|
||||||
注意:官方不支持任何形式的账号交易行为,请妥善保管好您的账号密码。请勿与他人共享账号。因账号交易、共享引起的账号争议官方均不受理。
|
注意:官方不支持任何形式的账号交易行为,请妥善保管好您的账号密码。请勿与他人共享账号。因账号交易、共享引起的账号争议官方均不受理。
|
||||||
如果牌不好的话,就请辱骂GK的发牌员吧
|
如果牌不好的话,就请辱骂GK的发牌员吧
|
||||||
|
@ -19,8 +19,9 @@
|
||||||
神曰:“MBKS”
|
神曰:“MBKS”
|
||||||
正在匹配势均力敌的对手…… 匹配到界徐盛
|
正在匹配势均力敌的对手…… 匹配到界徐盛
|
||||||
先喝酒还是先上刀,这是个值得考虑的问题
|
先喝酒还是先上刀,这是个值得考虑的问题
|
||||||
在抱怨FK只有标准包?那么去试着参与联机吧
|
在抱怨新月杀只有标准包?那么去试着参与联机吧
|
||||||
如果在牌局内想查看技能,可以长按想要看技能的角色
|
如果在牌局内想查看技能,可以长按想要看技能的角色
|
||||||
FK的开服很简单,只要单机启动就可以当做服务器使用了
|
新月杀的开服很简单,只要单机启动就可以当做服务器使用了
|
||||||
用Linux也能搭建FK服务器,起始页推荐的联机IP就是跑在Linux服务器上的
|
用Linux也能搭建新月杀服务器,起始页推荐的联机IP就是跑在Linux服务器上的
|
||||||
我们的游戏正在蒸蒸日上哦~
|
我们的游戏正在蒸蒸日上哦~
|
||||||
|
请素质游戏!不要做出烧条之类的举动哦~
|
||||||
|
|
Loading…
Reference in New Issue