mirror of
https://github.com/Qsgs-Fans/FreeKill.git
synced 2024-11-16 11:42:45 +08:00
初步ai逻辑 (#270)
大部分修改的地方都在代码里加了注释 --------- Co-authored-by: notify <notify-ctrl@qq.com> Co-authored-by: xxyheaven <1433191064@qq.com> Co-authored-by: YoumuKon <38815081+YoumuKon@users.noreply.github.com>
This commit is contained in:
parent
f97df65ac6
commit
d1619672a2
BIN
audio/system/recast.mp3
Normal file
BIN
audio/system/recast.mp3
Normal file
Binary file not shown.
|
@ -329,12 +329,18 @@ end
|
|||
function Player:getCardIds(playerAreas, specialName)
|
||||
local rightAreas = { Player.Hand, Player.Equip, Player.Judge }
|
||||
playerAreas = playerAreas or rightAreas
|
||||
local cardIds = {}
|
||||
if type(playerAreas) == "string" then
|
||||
local str = playerAreas
|
||||
playerAreas = {}
|
||||
if str:find("h") then
|
||||
table.insert(playerAreas, Player.Hand)
|
||||
end
|
||||
if str:find("&") then--增加特殊区域
|
||||
for k, v in pairs(self.special_cards) do
|
||||
if k:endsWith("&") then table.insertTable(cardIds, v) end
|
||||
end
|
||||
end
|
||||
if str:find("e") then
|
||||
table.insert(playerAreas, Player.Equip)
|
||||
end
|
||||
|
@ -345,8 +351,7 @@ function Player:getCardIds(playerAreas, specialName)
|
|||
assert(type(playerAreas) == "number" or type(playerAreas) == "table")
|
||||
local areas = type(playerAreas) == "table" and playerAreas or { playerAreas }
|
||||
|
||||
local rightAreas = { Player.Hand, Player.Equip, Player.Judge, Player.Special }
|
||||
local cardIds = {}
|
||||
rightAreas = { Player.Hand, Player.Equip, Player.Judge, Player.Special }
|
||||
for _, area in ipairs(areas) do
|
||||
assert(table.contains(rightAreas, area))
|
||||
assert(area ~= Player.Special or type(specialName) == "string")
|
||||
|
|
|
@ -134,13 +134,15 @@ random_cb.AskForUseCard = function(self, jsonData)
|
|||
local cancelable = data[4] or true
|
||||
local exp = Exppattern:Parse(pattern)
|
||||
|
||||
local avail_cards = table.filter(player:getCardIds("he"), function(id)
|
||||
return exp:match(Fk:getCardById(id)) and not player:prohibitUse(Fk:getCardById(id))
|
||||
local avail_cards = table.map(player:getCardIds("he"),
|
||||
function(id) return Fk:getCardById(id) end)
|
||||
avail_cards = table.filter(avail_cards, function(c)
|
||||
return exp:match(c) and not player:prohibitUse(c)
|
||||
end)
|
||||
if #avail_cards > 0 then
|
||||
if math.random() < 0.25 then return "" end
|
||||
for _, card in ipairs(avail_cards) do
|
||||
local skill = Fk:getCardById(card).skill
|
||||
local skill = card.skill
|
||||
local max_try_times = 100
|
||||
local selected_targets = {}
|
||||
local min = skill:getMinTargetNum()
|
||||
|
@ -148,22 +150,19 @@ random_cb.AskForUseCard = function(self, jsonData)
|
|||
local min_card = skill:getMinCardNum()
|
||||
local max_card = skill:getMaxCardNum()
|
||||
for _ = 0, max_try_times do
|
||||
if skill:feasible(selected_targets, {card}, self.player, card) then break end
|
||||
if skill:feasible(selected_targets, { card.id }, self.player, card) then
|
||||
return json.encode{
|
||||
card = table.random(avail_cards).id,
|
||||
targets = selected_targets,
|
||||
}
|
||||
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
|
||||
return skill:targetFilter(p.id, selected_targets, {card.id}, card or Fk:cloneCard'zixing')
|
||||
end)
|
||||
avail_targets = table.map(avail_targets, function(p) return p.id end)
|
||||
|
||||
if #avail_targets == 0 and #avail_cards == 0 then break end
|
||||
table.insertIfNeed(selected_targets, table.random(avail_targets))
|
||||
table.insertIfNeed({card}, table.random(avail_cards))
|
||||
end
|
||||
if skill:feasible(selected_targets, {card}, self.player, card) then
|
||||
return json.encode{
|
||||
card = table.random(avail_cards),
|
||||
targets = selected_targets,
|
||||
}
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -44,7 +44,9 @@ function GameLogic:run()
|
|||
self.room.game_started = true
|
||||
room:doBroadcastNotify("StartGame", "")
|
||||
room:adjustSeats()
|
||||
|
||||
for _, p in ipairs(room.players) do
|
||||
p.ai = SmartAI:new(p)
|
||||
end
|
||||
self:chooseGenerals()
|
||||
|
||||
self:buildPlayerCircle()
|
||||
|
|
|
@ -3071,6 +3071,7 @@ function Room:recastCard(card_ids, who, skillName)
|
|||
moveReason = fk.ReasonPutIntoDiscardPile,
|
||||
proposer = who.id
|
||||
})
|
||||
self:broadcastPlaySound("./audio/system/recast")
|
||||
self:sendLog{
|
||||
type = skillName == "recast" and "#Recast" or "#RecastBySkill",
|
||||
from = who.id,
|
||||
|
|
|
@ -0,0 +1,178 @@
|
|||
fk.ai_card.thunder__slash = fk.ai_card.slash
|
||||
fk.ai_use_play.thunder__slash = fk.ai_use_play.slash
|
||||
fk.ai_card.fire__slash = fk.ai_card.slash
|
||||
fk.ai_use_play.fire__slash = fk.ai_use_play.slash
|
||||
fk.ai_card.analeptic = {
|
||||
intention = 60, -- 身份值
|
||||
value = 5, -- 卡牌价值
|
||||
priority = 3 -- 使用优先值
|
||||
}
|
||||
|
||||
fk.ai_use_play["analeptic"] = function(self, card)
|
||||
local cards = table.map(self.player:getCardIds("&he"), function(id)
|
||||
return Fk:getCardById(id)
|
||||
end)
|
||||
self:sortValue(cards)
|
||||
for _, sth in ipairs(self:getActives("slash")) do
|
||||
local slash = nil
|
||||
if sth:isInstanceOf(Card) then
|
||||
if sth.skill:canUse(self.player, sth) and not self.player:prohibitUse(sth) then
|
||||
slash = sth
|
||||
end
|
||||
else
|
||||
local selected = {}
|
||||
for _, c in ipairs(cards) do
|
||||
if sth:cardFilter(c.id, selected) then
|
||||
table.insert(selected, c.id)
|
||||
end
|
||||
end
|
||||
local tc = sth:viewAs(selected)
|
||||
if tc and tc:matchPattern("slash") and tc.skill:canUse(self.player, tc) and not self.player:prohibitUse(tc) then
|
||||
slash = tc
|
||||
end
|
||||
end
|
||||
if slash then
|
||||
fk.ai_use_play.slash(self, slash)
|
||||
if self.use_id then
|
||||
self.use_id = card.id
|
||||
self.use_tos = {}
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
fk.ai_card.iron_chain = {
|
||||
intention = function(self, card, from)
|
||||
if self.player.chained then
|
||||
return -80
|
||||
end
|
||||
return 80
|
||||
end, -- 身份值
|
||||
value = 2, -- 卡牌价值
|
||||
priority = 3 -- 使用优先值
|
||||
}
|
||||
|
||||
fk.ai_use_play["iron_chain"] = function(self, card)
|
||||
for _, p in ipairs(self.friends) do
|
||||
if card.skill:targetFilter(p.id, self.use_tos, {}, card) and p.chained then
|
||||
table.insert(self.use_tos, p.id)
|
||||
end
|
||||
end
|
||||
self:sort(self.enemies)
|
||||
for _, p in ipairs(self.enemies) do
|
||||
if card.skill:targetFilter(p.id, self.use_tos, {}, card) and not p.chained then
|
||||
table.insert(self.use_tos, p.id)
|
||||
end
|
||||
end
|
||||
if #self.use_tos < 2 then
|
||||
self.use_tos = {}
|
||||
else
|
||||
self.use_id = card.id
|
||||
end
|
||||
end
|
||||
|
||||
fk.ai_use_play["recast"] = function(self, card)
|
||||
if self.command == "PlayCard" then
|
||||
self.use_id = card.id
|
||||
self.special_skill = "recast"
|
||||
end
|
||||
end
|
||||
|
||||
fk.ai_card.fire_attack = {
|
||||
intention = 90, -- 身份值
|
||||
value = 3, -- 卡牌价值
|
||||
priority = 4 -- 使用优先值
|
||||
}
|
||||
|
||||
fk.ai_use_play["fire_attack"] = function(self, card)
|
||||
self:sort(self.enemies)
|
||||
for _, p in ipairs(self.enemies) do
|
||||
if card.skill:targetFilter(p.id, self.use_tos, {}, card) and #self.player:getCardIds("h") > 2 then
|
||||
self.use_id = card.id
|
||||
table.insert(self.use_tos, p.id)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
fk.ai_discard["fire_attack_skill"] = function(self, min_num, num, include_equip, cancelable, pattern, prompt)
|
||||
local use = self:eventData("UseCard")
|
||||
for _, p in ipairs(TargetGroup:getRealTargets(use.tos)) do
|
||||
if self:isEnemie(p) then
|
||||
local cards = table.map(self.player:getCardIds("h"), function(id)
|
||||
return Fk:getCardById(id)
|
||||
end)
|
||||
local exp = Exppattern:Parse(pattern)
|
||||
cards = table.filter(cards, function(c)
|
||||
return exp:match(c)
|
||||
end)
|
||||
if #cards > 0 then
|
||||
self:sortValue(cards)
|
||||
return { cards[1].id }
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
fk.ai_nullification.fire_attack = function(self, card, to, from, positive)
|
||||
if positive then
|
||||
if self:isFriend(to) and #to:getCardIds("h") > 0 and #from:getCardIds("h") > 0 then
|
||||
if #self.avail_cards > 1 or self:isWeak(to) or to.id == self.player.id then
|
||||
self.use_id = self.avail_cards[1]
|
||||
end
|
||||
end
|
||||
else
|
||||
if self:isEnemie(to) and #to:getCardIds("h") > 0 and #from:getCardIds("h") > 1 then
|
||||
if #self.avail_cards > 1 or self:isWeak(to) then
|
||||
self.use_id = self.avail_cards[1]
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
fk.ai_card.fire_attack = {
|
||||
intention = 120, -- 身份值
|
||||
value = 2, -- 卡牌价值
|
||||
priority = 2 -- 使用优先值
|
||||
}
|
||||
|
||||
fk.ai_use_play["supply_shortage"] = function(self, card)
|
||||
self:sort(self.enemies)
|
||||
for _, p in ipairs(self.enemies) do
|
||||
if card.skill:targetFilter(p.id, self.use_tos, {}, card) and not p.chained then
|
||||
self.use_id = card.id
|
||||
table.insert(self.use_tos, p.id)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
fk.ai_nullification.supply_shortage = function(self, card, to, from, positive)
|
||||
if positive then
|
||||
if self:isFriend(to) then
|
||||
if #self.avail_cards > 1 or self:isWeak(to) or to.id == self.player.id then
|
||||
self.use_id = self.avail_cards[1]
|
||||
end
|
||||
end
|
||||
else
|
||||
if self:isEnemie(to) then
|
||||
if #self.avail_cards > 1 or self:isWeak(to) then
|
||||
self.use_id = self.avail_cards[1]
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
fk.ai_card.supply_shortage = {
|
||||
intention = 130, -- 身份值
|
||||
value = 2, -- 卡牌价值
|
||||
priority = 1 -- 使用优先值
|
||||
}
|
||||
|
||||
fk.ai_skill_invoke["#fan_skill"] = function(self)
|
||||
local use = self:eventData("UseCard")
|
||||
for _, p in ipairs(TargetGroup:getRealTargets(use.tos)) do
|
||||
if not self:isFriend(p) then
|
||||
return true
|
||||
end
|
||||
end
|
||||
end
|
|
@ -182,7 +182,11 @@ local ironChainCardSkill = fk.CreateActiveSkill{
|
|||
mod_target_filter = function(self, to_select, selected, user, card, distance_limited)
|
||||
return true
|
||||
end,
|
||||
target_filter = function() return true end,
|
||||
target_filter = function(self, to_select, selected, _, card)
|
||||
if #selected < self:getMaxTargetNum(Self, card) then
|
||||
return self:modTargetFilter(to_select, selected, Self.id, card)
|
||||
end
|
||||
end,
|
||||
on_effect = function(self, room, cardEffectEvent)
|
||||
local to = room:getPlayerById(cardEffectEvent.to)
|
||||
to:setChainState(not to.chained)
|
||||
|
@ -208,10 +212,13 @@ local fireAttackSkill = fk.CreateActiveSkill{
|
|||
name = "fire_attack_skill",
|
||||
target_num = 1,
|
||||
mod_target_filter = function(self, to_select, selected, user, card, distance_limited)
|
||||
return not Fk:currentRoom():getPlayerById(to_select):isKongcheng()
|
||||
local to = Fk:currentRoom():getPlayerById(to_select)
|
||||
return not to:isKongcheng()
|
||||
end,
|
||||
target_filter = function(self, to_select)
|
||||
return self:modTargetFilter(to_select)
|
||||
target_filter = function(self, to_select, selected, _, card)
|
||||
if #selected < self:getMaxTargetNum(Self, card) then
|
||||
return self:modTargetFilter(to_select, selected, Self.id, card)
|
||||
end
|
||||
end,
|
||||
on_effect = function(self, room, cardEffectEvent)
|
||||
local from = room:getPlayerById(cardEffectEvent.from)
|
||||
|
|
|
@ -0,0 +1,262 @@
|
|||
fk.ai_use_play["rende"] = function(self, skill)
|
||||
for _, p in ipairs(self.friends_noself) do
|
||||
if p.kingdom == "shu" and #self.player:getCardIds("h") >= self.player.hp then
|
||||
self.use_id = {}
|
||||
for _, cid in ipairs(self.player:getCardIds("h")) do
|
||||
if #self.use_id < #self.player:getCardIds("h") / 2 then
|
||||
table.insert(self.use_id, cid)
|
||||
end
|
||||
end
|
||||
self.use_tos = { p.id }
|
||||
return
|
||||
end
|
||||
end
|
||||
for _, p in ipairs(self.friends_noself) do
|
||||
if #self.player:getCardIds("h") >= self.player.hp then
|
||||
self.use_id = {}
|
||||
for _, cid in ipairs(self.player:getCardIds("h")) do
|
||||
if #self.use_id < #self.player:getCardIds("h") / 2 then
|
||||
table.insert(self.use_id, cid)
|
||||
end
|
||||
end
|
||||
self.use_tos = { p.id }
|
||||
return
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
fk.ai_card["jijiang"] = { priority = 10 }
|
||||
|
||||
fk.ai_use_play["lijian"] = function(self, skill)
|
||||
local c = Fk:cloneCard("duel")
|
||||
c.skillName = "lijian"
|
||||
local cards = table.map(
|
||||
self.player:getCardIds("he"),
|
||||
function(id)
|
||||
return Fk:getCardById(id)
|
||||
end
|
||||
)
|
||||
self:sortValue(cards)
|
||||
for _, p in ipairs(self.enemies) do
|
||||
for _, pt in ipairs(self.enemies) do
|
||||
if p.gender == General.Male and pt.gender == General.Male and p.id ~= pt.id
|
||||
and c.skill:targetFilter(pt.id, {}, p.id, c) then
|
||||
self.use_id = { cards[1].id }
|
||||
self.use_tos = { pt.id, p.id }
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
for _, p in ipairs(self.friends_noself) do
|
||||
for _, pt in ipairs(self.enemies) do
|
||||
if p.gender == General.Male and pt.gender == General.Male and p.id ~= pt.id
|
||||
and c.skill:targetFilter(pt.id, {}, p.id, c) then
|
||||
self.use_id = { cards[1].id }
|
||||
self.use_tos = { pt.id, p.id }
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
fk.ai_card["lijian"] = { priority = 2 }
|
||||
|
||||
fk.ai_use_play["zhiheng"] = function(self, skill)
|
||||
local card_ids = {}
|
||||
local cards = table.map(
|
||||
self.player:getCardIds("he"),
|
||||
function(id)
|
||||
return Fk:getCardById(id)
|
||||
end
|
||||
)
|
||||
self:sortValue(cards)
|
||||
for _, h in ipairs(cards) do
|
||||
if #card_ids < #cards / 2 then
|
||||
table.insert(card_ids, h.id)
|
||||
end
|
||||
end
|
||||
if #card_ids > 0 then
|
||||
self.use_id = card_ids
|
||||
end
|
||||
end
|
||||
|
||||
fk.ai_use_play["kurou"] = function(self, skill)
|
||||
if #self:getActives("peach") + self.player.hp > 1 then
|
||||
local slash = Fk:cloneCard("slash")
|
||||
if slash.skill:canUse(self.player, slash) and not self.player:prohibitUse(slash) then
|
||||
fk.ai_use_play.slash(self, slash)
|
||||
if self.use_id then
|
||||
self.use_id = {}
|
||||
self.use_tos = {}
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
fk.ai_use_play["fanjian"] = function(self, skill)
|
||||
for _, p in ipairs(self.enemies) do
|
||||
if #self.player:getCardIds("h") > 0 then
|
||||
self.use_id = {}
|
||||
table.insert(self.use_tos, p.id)
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
fk.ai_use_play["jieyin"] = function(self, skill)
|
||||
local cards = table.map(
|
||||
self.player:getCardIds("h"),
|
||||
function(id)
|
||||
return Fk:getCardById(id)
|
||||
end
|
||||
)
|
||||
self:sortValue(cards)
|
||||
for _, p in ipairs(self.friends_noself) do
|
||||
if #cards > 1 and p.gender == General.Male and p:isWounded() then
|
||||
self.use_id = { cards[1].id, cards[2].id }
|
||||
table.insert(self.use_tos, p.id)
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
fk.ai_use_play["qingnang"] = function(self, skill)
|
||||
local cards = table.map(
|
||||
self.player:getCardIds("h"),
|
||||
function(id)
|
||||
return Fk:getCardById(id)
|
||||
end
|
||||
)
|
||||
self:sortValue(cards)
|
||||
for _, p in ipairs(self.friends) do
|
||||
if #cards > 0 and p:isWounded() then
|
||||
self.use_id = { cards[1].id }
|
||||
table.insert(self.use_tos, p.id)
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
fk.ai_skill_invoke["jianxiong"] = true
|
||||
|
||||
fk.ai_card["hujia"] = { priority = 10 }
|
||||
|
||||
fk.ai_response_card["#hujia-ask"] = function(self, pattern, prompt, cancelable, data)
|
||||
local to = self.room:getPlayerById(tonumber(prompt:split(":")[2]))
|
||||
if to and self:isFriend(to) and (self:isWeak(to) or #self:getActives(pattern)>1) then
|
||||
self:setUseId(pattern)
|
||||
end
|
||||
end
|
||||
|
||||
fk.ai_response_card["#jijiang-ask"] = fk.ai_response_card["#hujia-ask"]
|
||||
|
||||
fk.ai_skill_invoke["fankui"] = function(self, data, prompt)
|
||||
local damage = self:eventData("Damage")
|
||||
return damage and damage.from and not self:isFriend(damage.from)
|
||||
end
|
||||
|
||||
fk.ai_response_card["#guicai-ask"] = function(self, pattern, prompt, cancelable, data)
|
||||
local cards = table.map(self.player:getHandlyIds(true), function(id)
|
||||
return Fk:getCardById(id)
|
||||
end)
|
||||
local id = self:getRetrialCardId(cards)
|
||||
if id then
|
||||
self.use_id = id
|
||||
end
|
||||
end
|
||||
|
||||
fk.ai_skill_invoke["ganglie"] = function(self, data, prompt)
|
||||
local damage = self:eventData("Damage")
|
||||
return damage and damage.from and not self:isFriend(damage.from)
|
||||
end
|
||||
|
||||
fk.ai_judge["ganglie"] = { ".|.|heart", false }
|
||||
|
||||
fk.ai_skill_invoke["luoyi"] = function(self, data, prompt)
|
||||
for _, p in ipairs(self.enemies) do
|
||||
if #self:getActives("slash") > 0 and not self:isWeak() then
|
||||
return true
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
fk.ai_skill_invoke["tiandu"] = true
|
||||
|
||||
fk.ai_skill_invoke["yiji"] = true
|
||||
|
||||
fk.ai_skill_invoke["luoshen"] = true
|
||||
|
||||
fk.ai_skill_invoke["guanxing"] = true
|
||||
|
||||
fk.ai_skill_invoke["tieqi"] = function(self, data, prompt)
|
||||
local use = self:eventData("UseCard")
|
||||
for _, p in ipairs(TargetGroup:getRealTargets(use.tos)) do
|
||||
p = self.room:getPlayerById(p)
|
||||
if self:isEnemie(p) then
|
||||
return true
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
fk.ai_skill_invoke["jizhi"] = true
|
||||
|
||||
fk.ai_skill_invoke["keji"] = true
|
||||
|
||||
fk.ai_skill_invoke["yingzi"] = true
|
||||
|
||||
fk.ai_skill_invoke["lianying"] = true
|
||||
|
||||
fk.ai_skill_invoke["xiaoji"] = true
|
||||
|
||||
fk.ai_skill_invoke["biyue"] = true
|
||||
|
||||
fk.ai_choose_players["tuxi"] = function(self, targets, min_num, num, cancelable)
|
||||
for _, pid in ipairs(targets) do
|
||||
local p = self.room:getPlayerById(pid)
|
||||
if self:isEnemie(p) and #self.use_tos < num then
|
||||
table.insert(self.use_tos, pid)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
fk.ai_use_skill["yiji_active"] = function(self, prompt, cancelable, data)
|
||||
for _, p in ipairs(self.friends_noself) do
|
||||
for c, cid in ipairs(self.player.tag["yiji_ids"]) do
|
||||
c = Fk:getCardById(cid)
|
||||
if c:getMark("yiji") > 0 and c.skill:canUse(p, c) then
|
||||
self.use_tos = { p.id }
|
||||
self.use_id = json.encode {
|
||||
skill = "yiji_active",
|
||||
subcards = { cid }
|
||||
}
|
||||
return
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
fk.ai_choose_players["liuli"] = function(self, targets, min_num, num, cancelable)
|
||||
local cards = table.map(
|
||||
self.player:getCardIds("he"),
|
||||
function(id)
|
||||
return Fk:getCardById(id)
|
||||
end
|
||||
)
|
||||
self:sortValue(cards)
|
||||
for _, pid in ipairs(targets) do
|
||||
local p = self.room:getPlayerById(pid)
|
||||
if self:isEnemie(p) and #self.use_tos < num and #cards > 0 then
|
||||
table.insert(self.use_tos, pid)
|
||||
self.use_id = { cards[1].id }
|
||||
return
|
||||
end
|
||||
end
|
||||
for _, pid in ipairs(targets) do
|
||||
local p = self.room:getPlayerById(pid)
|
||||
if not self:isFriend(p) and #self.use_tos < num and #cards > 0 then
|
||||
table.insert(self.use_tos, pid)
|
||||
self.use_id = { cards[1].id }
|
||||
return
|
||||
end
|
||||
end
|
||||
end
|
|
@ -10,9 +10,11 @@ local jianxiong = fk.CreateTriggerSkill{
|
|||
anim_type = "masochism",
|
||||
events = {fk.Damaged},
|
||||
can_trigger = function(self, event, target, player, data)
|
||||
local room = target.room
|
||||
return target == player and player:hasSkill(self.name) and data.card and
|
||||
table.every(data.card:isVirtual() and data.card.subcards or {data.card.id}, function(id) return room:getCardArea(id) == Card.Processing end)
|
||||
if target == player and player:hasSkill(self.name) and data.card then
|
||||
local room = player.room
|
||||
local subcards = data.card:isVirtual() and data.card.subcards or {data.card.id}
|
||||
return #subcards>0 and table.every(subcards, function(id) return room:getCardArea(id) == Card.Processing end)
|
||||
end
|
||||
end,
|
||||
on_use = function(self, event, target, player, data)
|
||||
player.room:obtainCard(player.id, data.card, true, fk.ReasonJustMove)
|
||||
|
@ -282,6 +284,7 @@ local yiji = fk.CreateTriggerSkill{
|
|||
for _, id in ipairs(ids) do
|
||||
room:setCardMark(Fk:getCardById(id), "yiji", 1)
|
||||
end
|
||||
player.tag["yiji_ids"] = ids --存储遗技卡牌表
|
||||
while table.find(ids, function(id) return Fk:getCardById(id):getMark("yiji") > 0 end) do
|
||||
if not room:askForUseActiveSkill(player, "yiji_active", "#yiji-give", true) then
|
||||
for _, id in ipairs(ids) do
|
||||
|
@ -491,7 +494,7 @@ local jijiangResponse = fk.CreateTriggerSkill{
|
|||
end
|
||||
|
||||
if event == fk.PreCardUse and player.phase == Player.Play then
|
||||
room:setPlayerMark(player, "jijiang-failed-phase", 1)
|
||||
room:setPlayerMark(player, "jijiang-failed-phase", 1)
|
||||
end
|
||||
return true
|
||||
end,
|
||||
|
|
|
@ -0,0 +1,438 @@
|
|||
fk.ai_card.slash = {
|
||||
intention = 100, -- 身份值
|
||||
value = 4, -- 卡牌价值
|
||||
priority = 2.5 -- 使用优先值
|
||||
}
|
||||
fk.ai_card.peach = {
|
||||
intention = -150,
|
||||
value = 10,
|
||||
priority = 0.5
|
||||
}
|
||||
fk.ai_card.dismantlement = {
|
||||
intention = function(self, card, from)
|
||||
if #self.player.player_cards[Player.Judge] < 1 then
|
||||
return 80
|
||||
elseif self.ai_role[from.id] == "neutral" then
|
||||
return 30
|
||||
end
|
||||
end,
|
||||
value = 3.5,
|
||||
priority = 10.5
|
||||
}
|
||||
fk.ai_card.snatch = {
|
||||
intention = function(self, card, from)
|
||||
if #self.player.player_cards[Player.Judge] < 1 then
|
||||
return 80
|
||||
elseif self.ai_role[from.id] == "neutral" then
|
||||
return 30
|
||||
end
|
||||
end,
|
||||
value = 4.5,
|
||||
priority = 10.4
|
||||
}
|
||||
fk.ai_card.duel = {
|
||||
intention = 120,
|
||||
value = 4.5,
|
||||
priority = 3.5
|
||||
}
|
||||
fk.ai_card.collateral = {
|
||||
intention = 20,
|
||||
value = 3,
|
||||
priority = 4.5
|
||||
}
|
||||
fk.ai_card.ex_nihilo = {
|
||||
intention = -200,
|
||||
value = 8,
|
||||
priority = 10
|
||||
}
|
||||
fk.ai_card.savage_assault = {
|
||||
intention = 20,
|
||||
value = 2,
|
||||
priority = 4
|
||||
}
|
||||
fk.ai_card.archery_attack = {
|
||||
intention = 30,
|
||||
value = 2,
|
||||
priority = 3
|
||||
}
|
||||
fk.ai_card.god_salvation = {
|
||||
intention = function(self, card, from)
|
||||
if self.player.hp ~= self.player.maxHp then
|
||||
return -45
|
||||
end
|
||||
end,
|
||||
value = 1.5,
|
||||
priority = 4
|
||||
}
|
||||
fk.ai_card.amazing_grace = {
|
||||
intention = -30,
|
||||
value = 2,
|
||||
priority = 2
|
||||
}
|
||||
fk.ai_card.indulgence = {
|
||||
intention = 150,
|
||||
value = -1,
|
||||
priority = 2
|
||||
}
|
||||
|
||||
local function slashEeffect(slash, to)
|
||||
for _, s in ipairs(to:getAllSkills()) do
|
||||
if s.name == "#vine_skill" then
|
||||
if slash.name == "slash" then
|
||||
return
|
||||
end
|
||||
end
|
||||
if s.name == "#nioh_shield_skill" then
|
||||
if slash.color == Card.Black then
|
||||
return
|
||||
end
|
||||
end
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
fk.ai_use_play["slash"] = function(self, card)
|
||||
self:sort(self.enemies)
|
||||
for _, p in ipairs(self.enemies) do
|
||||
if card.skill:targetFilter(p.id, self.use_tos, {}, card) and slashEeffect(card, p) and self:objectiveLevel(p) > 2 then
|
||||
self.use_id = card.id
|
||||
table.insert(self.use_tos, p.id)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
fk.ai_ask_usecard["#slash-jink"] = function(self, pattern, prompt, cancelable, extra_data)
|
||||
local act = self:getActives(pattern)
|
||||
if tonumber(prompt:split(":")[4]) > #act then
|
||||
return
|
||||
end
|
||||
local cards =
|
||||
table.map(
|
||||
self.player:getCardIds("&he"),
|
||||
function(id)
|
||||
return Fk:getCardById(id)
|
||||
end
|
||||
)
|
||||
self:sortValue(cards)
|
||||
for _, sth in ipairs(act) do
|
||||
if sth:isInstanceOf(Card) then
|
||||
self.use_id = sth.id
|
||||
break
|
||||
else
|
||||
local selected = {}
|
||||
for _, c in ipairs(cards) do
|
||||
if sth.cardFilter(sth, c.id, selected) then
|
||||
table.insert(selected, c.id)
|
||||
end
|
||||
end
|
||||
local tc = sth.viewAs(sth, selected)
|
||||
if tc and tc:matchPattern(pattern) then
|
||||
self.use_id =
|
||||
json.encode {
|
||||
skill = sth.name,
|
||||
subcards = selected
|
||||
}
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
fk.ai_ask_usecard["#slash-jinks"] = fk.ai_ask_usecard["#slash-jink"]
|
||||
|
||||
fk.ai_use_play["snatch"] = function(self, card)
|
||||
for _, p in ipairs(self.friends_noself) do
|
||||
if card.skill:targetFilter(p.id, self.use_tos, {}, card) and #p:getCardIds("j") > 0 then
|
||||
self.use_id = card.id
|
||||
table.insert(self.use_tos, p.id)
|
||||
end
|
||||
end
|
||||
self:sort(self.enemies)
|
||||
for _, p in ipairs(self.enemies) do
|
||||
if card.skill:targetFilter(p.id, self.use_tos, {}, card) and #p:getCardIds("he") > 0 then
|
||||
self.use_id = card.id
|
||||
table.insert(self.use_tos, p.id)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
fk.ai_nullification.snatch = function(self, card, to, from, positive)
|
||||
if positive then
|
||||
if self:isFriend(to) and not self:isFriend(from) and self.ai_role[from.id] ~= "neutral" then
|
||||
if #self.avail_cards > 1 or self:isWeak(to) or to.id == self.player.id then
|
||||
self.use_id = self.avail_cards[1]
|
||||
end
|
||||
end
|
||||
else
|
||||
if self:isEnemie(to) and self:isEnemie(from) then
|
||||
if #self.avail_cards > 1 or self:isWeak(to) then
|
||||
self.use_id = self.avail_cards[1]
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
fk.ai_use_play["dismantlement"] = function(self, card)
|
||||
for _, p in ipairs(self.friends_noself) do
|
||||
if card.skill:targetFilter(p.id, self.use_tos, {}, card) and #p:getCardIds("j") > 0 then
|
||||
self.use_id = card.id
|
||||
table.insert(self.use_tos, p.id)
|
||||
end
|
||||
end
|
||||
self:sort(self.enemies)
|
||||
for _, p in ipairs(self.enemies) do
|
||||
if card.skill:targetFilter(p.id, self.use_tos, {}, card) and #p:getCardIds("he") > 0 then
|
||||
self.use_id = card.id
|
||||
table.insert(self.use_tos, p.id)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
fk.ai_nullification.dismantlement = function(self, card, to, from, positive)
|
||||
if positive then
|
||||
if self:isFriend(to) and not self:isFriend(from) and self.ai_role[from.id] ~= "neutral" then
|
||||
if #self.avail_cards > 1 or self:isWeak(to) or to.id == self.player.id then
|
||||
self.use_id = self.avail_cards[1]
|
||||
end
|
||||
end
|
||||
else
|
||||
if self:isEnemie(to) and self:isEnemie(from) then
|
||||
if #self.avail_cards > 1 or self:isWeak(to) then
|
||||
self.use_id = self.avail_cards[1]
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
fk.ai_use_play["indulgence"] = function(self, card)
|
||||
self:sort(self.enemies, nil, true)
|
||||
for _, p in ipairs(self.enemies) do
|
||||
if card.skill:targetFilter(p.id, self.use_tos, {}, card) then
|
||||
self.use_id = card.id
|
||||
table.insert(self.use_tos, p.id)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
fk.ai_nullification.indulgence = function(self, card, to, from, positive)
|
||||
if positive then
|
||||
if self:isFriend(to) then
|
||||
if #self.avail_cards > 1 or self:isWeak(to) or to.id == self.player.id then
|
||||
self.use_id = self.avail_cards[1]
|
||||
end
|
||||
end
|
||||
else
|
||||
if self:isEnemie(to) then
|
||||
if #self.avail_cards > 1 or self:isWeak(to) then
|
||||
self.use_id = self.avail_cards[1]
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
fk.ai_use_play["collateral"] = function(self, card)
|
||||
local max = (card.skill:getMaxTargetNum(self.player, card) - 1) * 2
|
||||
self:sort(self.enemies)
|
||||
for _, p in ipairs(self.enemies) do
|
||||
if #self.use_tos < max and card.skill:targetFilter(p.id, {}, {}, card) then
|
||||
for _, pt in ipairs(self.enemies) do
|
||||
if p ~= pt and p:inMyAttackRange(pt) then
|
||||
table.insert(self.use_tos, p.id)
|
||||
table.insert(self.use_tos, pt.id)
|
||||
self.use_id = card.id
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
for _, p in ipairs(self.friends_noself) do
|
||||
if #self.use_tos < max and card.skill:targetFilter(p.id, {}, {}, card) then
|
||||
for _, pt in ipairs(self.enemies) do
|
||||
if p ~= pt and p:inMyAttackRange(pt) then
|
||||
table.insert(self.use_tos, p.id)
|
||||
table.insert(self.use_tos, pt.id)
|
||||
self.use_id = card.id
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
fk.ai_nullification.collateral = function(self, card, to, from, positive)
|
||||
if positive then
|
||||
if self:isFriend(to) and self:isEnemie(from) then
|
||||
if #self.avail_cards > 1 or self:isWeak(to) or to.id == self.player.id then
|
||||
self.use_id = self.avail_cards[1]
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
fk.ai_nullification.ex_nihilo = function(self, card, to, from, positive)
|
||||
if positive then
|
||||
if self:isEnemie(to) then
|
||||
if #self.avail_cards > 1 or self:isWeak(to) then
|
||||
self.use_id = self.avail_cards[1]
|
||||
end
|
||||
end
|
||||
else
|
||||
if self:isFriend(to) then
|
||||
if #self.avail_cards > 1 or self:isWeak(to) or to.id == self.player.id then
|
||||
self.use_id = self.avail_cards[1]
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
fk.ai_nullification.savage_assault = function(self, card, to, from, positive)
|
||||
if positive then
|
||||
if self:isFriend(to) then
|
||||
if #self.avail_cards > 1 or self:isWeak(to) or to.id == self.player.id then
|
||||
self.use_id = self.avail_cards[1]
|
||||
end
|
||||
end
|
||||
else
|
||||
if self:isEnemie(to) then
|
||||
if #self.avail_cards > 1 or self:isWeak(to) then
|
||||
self.use_id = self.avail_cards[1]
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
fk.ai_nullification.archery_attack = function(self, card, to, from, positive)
|
||||
if positive then
|
||||
if self:isFriend(to) then
|
||||
if #self.avail_cards > 1 or self:isWeak(to) or to.id == self.player.id then
|
||||
self.use_id = self.avail_cards[1]
|
||||
end
|
||||
end
|
||||
else
|
||||
if self:isEnemie(to) then
|
||||
if #self.avail_cards > 1 or self:isWeak(to) then
|
||||
self.use_id = self.avail_cards[1]
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
fk.ai_nullification.god_salvation = function(self, card, to, from, positive)
|
||||
if positive then
|
||||
if self:isEnemie(to) and to.hp ~= to.maxHp then
|
||||
if #self.avail_cards > 1 or self:isWeak(to) then
|
||||
self.use_id = self.avail_cards[1]
|
||||
end
|
||||
end
|
||||
else
|
||||
if self:isFriend(to) and to.hp ~= to.maxHp then
|
||||
if #self.avail_cards > 1 or self:isWeak(to) or to.id == self.player.id then
|
||||
self.use_id = self.avail_cards[1]
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
fk.ai_use_play["god_salvation"] = function(self, card)
|
||||
local can = 0
|
||||
for _, p in ipairs(self.enemies) do
|
||||
if p:isWounded()
|
||||
then
|
||||
can = can - 1
|
||||
if self:isWeak(p)
|
||||
then
|
||||
can = can - 1
|
||||
end
|
||||
end
|
||||
end
|
||||
for _, p in ipairs(self.friends) do
|
||||
if p:isWounded()
|
||||
then
|
||||
can = can + 1
|
||||
if self:isWeak(p)
|
||||
then
|
||||
can = can + 1
|
||||
end
|
||||
end
|
||||
end
|
||||
self.use_id = can > 0 and card.id
|
||||
end
|
||||
|
||||
fk.ai_use_play["amazing_grace"] = function(self, card)
|
||||
self.use_id = #self.player:getCardIds("&h") <= self.player.hp and card.id
|
||||
end
|
||||
|
||||
fk.ai_use_play["ex_nihilo"] = function(self, card)
|
||||
self.use_id = card.id
|
||||
end
|
||||
|
||||
fk.ai_use_play["lightning"] = function(self, card)
|
||||
self.use_id = #self.enemies > #self.friends and card.id
|
||||
end
|
||||
|
||||
fk.ai_use_play["peach"] = function(self, card)
|
||||
if self.command == "PlayCard" then
|
||||
self.use_id = self.player.hp ~= self.player.maxHp and self.player.hp < #self.player:getCardIds("h") and card.id
|
||||
else
|
||||
for _, p in ipairs(self.friends) do
|
||||
if p.dying then
|
||||
self.use_id = card.id
|
||||
self.use_tos = { p.id }
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
fk.ai_use_play["duel"] = function(self, card)
|
||||
self:sort(self.enemies)
|
||||
for _, p in ipairs(self.enemies) do
|
||||
if card.skill:targetFilter(p.id, self.use_tos, {}, card) then
|
||||
self.use_id = card.id
|
||||
table.insert(self.use_tos, p.id)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
fk.ai_skill_invoke["#ice_sword_skill"] = function(self)
|
||||
local damage = self:eventData("Damage")
|
||||
return self:isFriend(damage.to) or not self:isWeak(damage.to) and #damage.to:getCardIds("e") > 1
|
||||
end
|
||||
|
||||
fk.ai_skill_invoke["#double_swords_skill"] = function(self)
|
||||
local use = self:eventData("UseCard")
|
||||
for _, p in ipairs(TargetGroup:getRealTargets(use.tos)) do
|
||||
if not self:isFriend(p) and self.room:getPlayerById(p).gender ~= self.player.gender then
|
||||
return true
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
fk.ai_discard["#double_swords_skill"] = function(self, min_num, num, include_equip, cancelable, pattern, prompt)
|
||||
local use = self:eventData("UseCard")
|
||||
return self:isEnemie(use.from) and { self.player:getCardIds("h")[1] }
|
||||
end
|
||||
|
||||
fk.ai_discard["#axe_skill"] = function(self, min_num, num, include_equip, cancelable, pattern, prompt)
|
||||
local ids = {}
|
||||
local effect = self:eventData("CardEffect")
|
||||
for _, cid in ipairs(self.player:getCardIds("he")) do
|
||||
if Fk:getCardById(cid):matchPattern(pattern) then
|
||||
table.insert(ids, cid)
|
||||
end
|
||||
if #ids >= min_num and self:isEnemie(effect.to)
|
||||
and (self:isWeak(effect.to) or #self.player:getCardIds("he") > 3) then
|
||||
return ids
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
fk.ai_skill_invoke["#kylin_bow_skill"] = function(self)
|
||||
local damage = self:eventData("Damage")
|
||||
return not self:isFriend(damage.to)
|
||||
end
|
||||
|
||||
fk.ai_skill_invoke["#eight_diagram_skill"] = function(self)
|
||||
local effect = self:eventData("CardEffect")
|
||||
return effect and self:isFriend(effect.to)
|
||||
end
|
|
@ -172,13 +172,13 @@ extension:addCards({
|
|||
local dismantlementSkill = fk.CreateActiveSkill{
|
||||
name = "dismantlement_skill",
|
||||
target_num = 1,
|
||||
mod_target_filter = function(self, to_select, selected, user)
|
||||
mod_target_filter = function(self, to_select, selected, user, card)
|
||||
local player = Fk:currentRoom():getPlayerById(to_select)
|
||||
return Fk:currentRoom():getPlayerById(user) ~= player and not player:isAllNude()
|
||||
return user ~= to_select and not player:isAllNude()
|
||||
end,
|
||||
target_filter = function(self, to_select, selected)
|
||||
if #selected < self:getMaxTargetNum(Self) then
|
||||
return self:modTargetFilter(to_select, selected, Self.id)
|
||||
target_filter = function(self, to_select, selected, _, card)
|
||||
if #selected < self:getMaxTargetNum(Self, card) then
|
||||
return self:modTargetFilter(to_select, selected, Self.id, card)
|
||||
end
|
||||
end,
|
||||
on_effect = function(self, room, effect)
|
||||
|
@ -216,7 +216,7 @@ local snatchSkill = fk.CreateActiveSkill{
|
|||
return from ~= player and not (player:isAllNude() or (distance_limited and not self:withinDistanceLimit(from, false, card, player)))
|
||||
end,
|
||||
target_filter = function(self, to_select, selected, _, card)
|
||||
if #selected == 0 then
|
||||
if #selected < self:getMaxTargetNum(Self, card) then
|
||||
return self:modTargetFilter(to_select, selected, Self.id, card, true)
|
||||
end
|
||||
end,
|
||||
|
@ -247,12 +247,12 @@ extension:addCards({
|
|||
|
||||
local duelSkill = fk.CreateActiveSkill{
|
||||
name = "duel_skill",
|
||||
mod_target_filter = function(self, to_select, selected, user)
|
||||
mod_target_filter = function(self, to_select, selected, user, card)
|
||||
return user ~= to_select
|
||||
end,
|
||||
target_filter = function(self, to_select, selected)
|
||||
if #selected == 0 then
|
||||
return self:modTargetFilter(to_select, selected, Self.id)
|
||||
target_filter = function(self, to_select, selected, _, card)
|
||||
if #selected < self:getMaxTargetNum(Self, card) then
|
||||
return self:modTargetFilter(to_select, selected, Self.id, card)
|
||||
end
|
||||
end,
|
||||
target_num = 1,
|
||||
|
@ -334,19 +334,33 @@ local collateralSkill = fk.CreateActiveSkill{
|
|||
name = "collateral_skill",
|
||||
mod_target_filter = function(self, to_select, selected, user, card, distance_limited)
|
||||
local player = Fk:currentRoom():getPlayerById(to_select)
|
||||
return user ~= player.id and player:getEquipment(Card.SubtypeWeapon)
|
||||
return user ~= to_select and player:getEquipment(Card.SubtypeWeapon)
|
||||
end,
|
||||
target_filter = function(self, to_select, selected)
|
||||
local player = Fk:currentRoom():getPlayerById(to_select)
|
||||
if #selected == 0 then
|
||||
return Self ~= player and player:getEquipment(Card.SubtypeWeapon)
|
||||
elseif #selected == 1 then
|
||||
return Fk:currentRoom():getPlayerById(selected[1]):inMyAttackRange(player)
|
||||
target_filter = function(self, to_select, selected, _, card)
|
||||
if #selected >= (self:getMaxTargetNum(Self, card) - 1) * 2 then
|
||||
return false--修改借刀的目标选择
|
||||
elseif #selected % 2 == 0 then
|
||||
return self:modTargetFilter(to_select, selected, Self.id, card)
|
||||
else
|
||||
local player = Fk:currentRoom():getPlayerById(to_select)
|
||||
local from = Fk:currentRoom():getPlayerById(selected[#selected])
|
||||
return self:modTargetFilter(selected[#selected], selected, Self.id, card)
|
||||
and from:inMyAttackRange(player) and not from:isProhibited(player, Fk:cloneCard("slash"))
|
||||
end
|
||||
end,
|
||||
target_num = 2,
|
||||
on_use = function(self, room, cardUseEvent)
|
||||
cardUseEvent.tos = { { cardUseEvent.tos[1][1], cardUseEvent.tos[2][1] } }
|
||||
local tos = {}
|
||||
local exclusive = {}
|
||||
for i, pid in ipairs(TargetGroup:getRealTargets(cardUseEvent.tos)) do
|
||||
if i % 2 == 1 then
|
||||
exclusive = { pid }
|
||||
else
|
||||
table.insert(exclusive, pid)
|
||||
table.insert(tos, exclusive)
|
||||
end
|
||||
end
|
||||
cardUseEvent.tos = tos
|
||||
end,
|
||||
on_effect = function(self, room, effect)
|
||||
local to = room:getPlayerById(effect.to)
|
||||
|
|
Loading…
Reference in New Issue
Block a user