mirror of
https://github.com/Qsgs-Fans/FreeKill.git
synced 2024-11-16 11:42:45 +08:00
parent
041d5835ff
commit
eba115a4fa
20
docs/dev/smart-ai.rst
Normal file
20
docs/dev/smart-ai.rst
Normal file
|
@ -0,0 +1,20 @@
|
|||
关于类似神杀的Smart-AI的实现思路
|
||||
==================================
|
||||
|
||||
AI的目的就是为了响应各种askFor,而Smart-ai则是给了玩家自定义askFor策略的接口。
|
||||
|
||||
大体框架还是一样的,根据command type去选择执行某个通用函数,再根据各种参数不断
|
||||
细化函数执行,最后执行Mod开发者的自定义逻辑。
|
||||
|
||||
而如何设计这种接口就是要面对的问题了。
|
||||
|
||||
神杀智慧1:堆积如山的hasSkill
|
||||
------------------------------
|
||||
|
||||
神杀一个突出的问题就是各种hasSkill写死,比如判断要不要黑杀某人:直接写死hasSkill
|
||||
仁王盾啥的
|
||||
|
||||
神杀智慧2:一次性sort所有卡牌/主动技/视为技
|
||||
--------------------------------------------
|
||||
|
||||
如题,这导致每次都要花秒级甚至分钟级别的时间来出一张牌。
|
|
@ -739,7 +739,7 @@ fk.client_callback["AskForUseActiveSkill"] = function(jsonData)
|
|||
-- jsonData: [ string skill_name, string prompt, bool cancelable. json extra_data ]
|
||||
local data = json.decode(jsonData)
|
||||
local skill = Fk.skills[data[1]]
|
||||
local extra_data = json.decode(data[4])
|
||||
local extra_data = data[4]
|
||||
for k, v in pairs(extra_data) do
|
||||
skill[k] = v
|
||||
end
|
||||
|
|
|
@ -382,6 +382,7 @@ end
|
|||
---@param include_hand bool @ 是否包含真正的手牌
|
||||
---@return integer[]
|
||||
function Player:getHandlyIds(include_hand)
|
||||
include_hand = include_hand or include_hand == nil
|
||||
local ret = include_hand and self:getCardIds("h") or {}
|
||||
for k, v in pairs(self.special_cards) do
|
||||
if k:endsWith("&") then table.insertTable(ret, v) end
|
||||
|
|
|
@ -85,6 +85,35 @@ function fk.qlist(list)
|
|||
return qlist_iterator, list, -1
|
||||
end
|
||||
|
||||
--- 用于for循环的迭代函数。可以将表按照某种权值的顺序进行遍历,这样不用进行完整排序。
|
||||
---@generic T
|
||||
---@param t T[]
|
||||
---@param val_func? fun(e: T): integer @ 计算权值的函数,对int[]可不写
|
||||
---@param reverse? boolean @ 是否反排?反排的话优先返回权值小的元素
|
||||
function fk.sorted_pairs(t, val_func, reverse)
|
||||
val_func = val_func or function(e) return e end
|
||||
local t2 = table.simpleClone(t) -- 克隆一次表,用作迭代器上值
|
||||
local iter = function()
|
||||
local max_idx, max, max_val = -1, nil, nil
|
||||
for i, v in ipairs(t2) do
|
||||
if not max then
|
||||
max_idx, max, max_val = i, v, val_func(v)
|
||||
else
|
||||
local val = val_func(v)
|
||||
local checked = val > max_val
|
||||
if reverse then checked = not checked end
|
||||
if checked then
|
||||
max_idx, max, max_val = i, v, val
|
||||
end
|
||||
end
|
||||
end
|
||||
if max_idx == -1 then return nil, nil end
|
||||
table.remove(t2, max_idx)
|
||||
return -1, max, max_val
|
||||
end
|
||||
return iter, nil, 1
|
||||
end
|
||||
|
||||
---@param func fun(element, index, array)
|
||||
function table:forEach(func)
|
||||
for i, v in ipairs(self) do
|
||||
|
|
|
@ -6,7 +6,7 @@ RandomAI = require "server.ai.random_ai"
|
|||
|
||||
--[[ 在release版暂时不启动。
|
||||
SmartAI = require "server.ai.smart_ai"
|
||||
|
||||
---[[ 调试中,暂且不加载额外的AI。
|
||||
-- load ai module from packages
|
||||
local directories = FileIO.ls("packages")
|
||||
require "packages.standard.ai"
|
||||
|
|
|
@ -6,7 +6,7 @@ local RandomAI = AI:subclass("RandomAI")
|
|||
---@param self RandomAI
|
||||
---@param skill ActiveSkill
|
||||
---@param card Card | nil
|
||||
local function useActiveSkill(self, skill, card)
|
||||
function RandomAI:useActiveSkill(skill, card)
|
||||
local room = self.room
|
||||
local player = self.player
|
||||
|
||||
|
@ -62,7 +62,7 @@ end
|
|||
|
||||
---@param self RandomAI
|
||||
---@param skill ViewAsSkill
|
||||
local function useVSSkill(self, skill, pattern, cancelable, extra_data)
|
||||
function RandomAI:useVSSkill(skill, pattern, cancelable, extra_data)
|
||||
local player = self.player
|
||||
local room = self.room
|
||||
local precondition
|
||||
|
@ -116,11 +116,11 @@ random_cb["AskForUseActiveSkill"] = function(self, jsonData)
|
|||
local skill = Fk.skills[data[1]]
|
||||
local cancelable = data[3]
|
||||
if cancelable and math.random() < 0.25 then return "" end
|
||||
local extra_data = json.decode(data[4])
|
||||
local extra_data = data[4]
|
||||
for k, v in pairs(extra_data) do
|
||||
skill[k] = v
|
||||
end
|
||||
return useActiveSkill(self, skill)
|
||||
return RandomAI.useActiveSkill(self, skill)
|
||||
end
|
||||
|
||||
random_cb["AskForSkillInvoke"] = function(self, jsonData)
|
||||
|
@ -202,7 +202,7 @@ random_cb["PlayCard"] = function(self, jsonData)
|
|||
local card = sth
|
||||
local skill = card.skill ---@type ActiveSkill
|
||||
if math.random() > 0.15 then
|
||||
local ret = useActiveSkill(self, skill, card)
|
||||
local ret = RandomAI.useActiveSkill(self, skill, card)
|
||||
if ret ~= "" then return ret end
|
||||
table.removeOne(cards, card)
|
||||
else
|
||||
|
@ -211,14 +211,14 @@ random_cb["PlayCard"] = function(self, jsonData)
|
|||
elseif sth:isInstanceOf(ActiveSkill) then
|
||||
local active = sth
|
||||
if math.random() > 0.30 then
|
||||
local ret = useActiveSkill(self, active, nil)
|
||||
local ret = RandomAI.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)
|
||||
local ret = self:useVSSkill(vs)
|
||||
-- TODO: handle vs result
|
||||
end
|
||||
table.removeOne(cards, vs)
|
||||
|
@ -228,6 +228,9 @@ random_cb["PlayCard"] = function(self, jsonData)
|
|||
return ""
|
||||
end
|
||||
|
||||
-- FIXME: for smart ai
|
||||
RandomAI.cb_table = random_cb
|
||||
|
||||
function RandomAI:initialize(player)
|
||||
AI.initialize(self, player)
|
||||
self.cb_table = random_cb
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -1067,7 +1067,7 @@ function Room:askForUseActiveSkill(player, skill_name, prompt, cancelable, extra
|
|||
|
||||
local command = "AskForUseActiveSkill"
|
||||
self:notifyMoveFocus(player, extra_data.skillName or skill_name) -- for display skill name instead of command name
|
||||
local data = {skill_name, prompt, cancelable, json.encode(extra_data)}
|
||||
local data = {skill_name, prompt, cancelable, extra_data}
|
||||
|
||||
Fk.currentResponseReason = extra_data.skillName
|
||||
local result = self:doRequest(player, command, json.encode(data))
|
||||
|
@ -1951,7 +1951,7 @@ end
|
|||
---@param pattern string|nil @ 使用牌的规则,默认就是card_name的值
|
||||
---@param prompt string|nil @ 提示信息
|
||||
---@param cancelable bool @ 能否点取消
|
||||
---@param extra_data integer|nil @ 额外信息
|
||||
---@param extra_data? UseExtraData @ 额外信息
|
||||
---@param event_data CardEffectEvent|nil @ 事件信息
|
||||
---@return CardUseStruct | nil @ 返回关于本次使用牌的数据,以便后续处理
|
||||
function Room:askForUseCard(player, card_name, pattern, prompt, cancelable, extra_data, event_data)
|
||||
|
@ -2683,7 +2683,7 @@ function Room:handleCardEffect(event, cardEffectEvent)
|
|||
local players = {}
|
||||
Fk.currentResponsePattern = "nullification"
|
||||
for _, p in ipairs(self.alive_players) do
|
||||
local cards = p:getHandlyIds(true)
|
||||
local cards = p:getHandlyIds()
|
||||
for _, cid in ipairs(cards) do
|
||||
if
|
||||
Fk:getCardById(cid).trueName == "nullification" and
|
||||
|
|
|
@ -84,6 +84,14 @@ fk.IceDamage = 4
|
|||
---@field public who integer
|
||||
---@field public damage DamageStruct
|
||||
|
||||
--- askForUseCard中的extra_data
|
||||
---@class UseExtraData
|
||||
---@field public must_targets? integer[] @ 必须选的目标(?)
|
||||
---@field public exclusive_targets? integer[] @ ??
|
||||
---@field public bypass_distances? boolean @ 无距离限制?
|
||||
---@field public bypass_times? boolean @ 无次数限制?
|
||||
---@field public playing? boolean @ (AI专用) 出牌阶段?
|
||||
|
||||
---@class CardUseStruct
|
||||
---@field public from integer
|
||||
---@field public tos TargetGroup
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
--[[
|
||||
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
|
||||
|
@ -98,7 +99,7 @@ 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
|
||||
if self:isEnemy(p) then
|
||||
local cards = table.map(self.player:getCardIds("h"), function(id)
|
||||
return Fk:getCardById(id)
|
||||
end)
|
||||
|
@ -122,7 +123,7 @@ fk.ai_nullification.fire_attack = function(self, card, to, from, positive)
|
|||
end
|
||||
end
|
||||
else
|
||||
if self:isEnemie(to) and #to:getCardIds("h") > 0 and #from:getCardIds("h") > 1 then
|
||||
if self:isEnemy(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
|
||||
|
@ -154,7 +155,7 @@ fk.ai_nullification.supply_shortage = function(self, card, to, from, positive)
|
|||
end
|
||||
end
|
||||
else
|
||||
if self:isEnemie(to) then
|
||||
if self:isEnemy(to) then
|
||||
if #self.avail_cards > 1 or self:isWeak(to) then
|
||||
self.use_id = self.avail_cards[1]
|
||||
end
|
||||
|
@ -176,3 +177,4 @@ fk.ai_skill_invoke["#fan_skill"] = function(self)
|
|||
end
|
||||
end
|
||||
end
|
||||
--]]
|
||||
|
|
|
@ -5,70 +5,51 @@
|
|||
-----------------------------
|
||||
|
||||
--- 弃牌相关判定函数的表。键为技能名,值为原型如下的函数。
|
||||
---@type table<string, fun(self: SmartAI, min_num: number, num: number, include_equip: bool, cancelable: bool, pattern: string, prompt: string): any>
|
||||
---@type table<string, fun(self: SmartAI, min_num: number, num: number, include_equip: bool, cancelable: bool, pattern: string, prompt: string): integer[]|nil>
|
||||
fk.ai_discard = {}
|
||||
|
||||
--- 请求弃置
|
||||
---
|
||||
---由skillName进行下一级的决策,只需要在下一级里返回需要弃置的卡牌id表就行
|
||||
fk.ai_use_skill["discard_skill"] = function(self, prompt, cancelable, data)
|
||||
local ask = fk.ai_discard[data.skillName]
|
||||
self:assignValue()
|
||||
if type(ask) == "function" then
|
||||
ask = ask(self, data.min_num, data.num, data.include_equip, cancelable, data.pattern, prompt)
|
||||
end
|
||||
if type(ask) ~= "table" and not cancelable then
|
||||
local default_discard = function(self, min_num, num, include_equip, cancelable, pattern, prompt)
|
||||
if cancelable then return nil end
|
||||
local flag = "h"
|
||||
if data.include_equip then
|
||||
if include_equip then
|
||||
flag = "he"
|
||||
end
|
||||
ask = {}
|
||||
local cards = table.map(self.player:getCardIds(flag), function(id)
|
||||
return Fk:getCardById(id)
|
||||
end
|
||||
)
|
||||
self:sortValue(cards)
|
||||
for _, c in ipairs(cards) do
|
||||
table.insert(ask, c.id)
|
||||
if #ask >= data.min_num then
|
||||
local ret = {}
|
||||
local cards = self.player:getCardIds(flag)
|
||||
for _, cid in ipairs(cards) do
|
||||
table.insert(ret, cid)
|
||||
if #ret >= min_num then
|
||||
break
|
||||
end
|
||||
end
|
||||
return ret
|
||||
end
|
||||
if type(ask) == "table" and #ask >= data.min_num then
|
||||
self.use_id = json.encode {
|
||||
skill = data.skillName,
|
||||
subcards = ask
|
||||
}
|
||||
end
|
||||
|
||||
fk.ai_active_skill["discard_skill"] = function(self, prompt, cancelable, data)
|
||||
local ret = self:callFromTable(fk.ai_discard, not cancelable and default_discard, data.skillName,
|
||||
self, data.min_num, data.num, data.include_equip, cancelable, data.pattern, prompt)
|
||||
|
||||
if ret == nil or #ret < data.min_num then return nil end
|
||||
|
||||
return self:buildUseReply { skill = "discard_skill", subcards = ret }
|
||||
end
|
||||
|
||||
-- choose_players_skill: 选人相关AI
|
||||
-------------------------------------
|
||||
|
||||
---@class ChoosePlayersReply
|
||||
---@field cardId integer|nil
|
||||
---@field targets integer[]
|
||||
|
||||
--- 选人相关判定函数的表。键为技能名,值为原型如下的函数。
|
||||
---@type table<string, fun(self: SmartAI, targets: integer[], min_num: number, num: number, cancelable: bool)>
|
||||
---@type table<string, fun(self: SmartAI, targets: integer[], min_num: number, num: number, cancelable: bool): ChoosePlayersReply|nil>
|
||||
fk.ai_choose_players = {}
|
||||
|
||||
--- 请求选择目标
|
||||
---
|
||||
---由skillName进行下一级的决策,只需要在下一级里给self.use_tos添加角色id为目标就行
|
||||
fk.ai_use_skill["choose_players_skill"] = function(self, prompt, cancelable, data)
|
||||
local ask = fk.ai_choose_players[data.skillName]
|
||||
if type(ask) == "function" then
|
||||
ask(self, data.targets, data.min_num, data.num, cancelable)
|
||||
end
|
||||
if #self.use_tos > 0 then
|
||||
if self.use_id then
|
||||
self.use_id = json.encode {
|
||||
skill = data.skillName,
|
||||
subcards = self.use_id
|
||||
}
|
||||
else
|
||||
self.use_id = json.encode {
|
||||
skill = data.skillName,
|
||||
subcards = {}
|
||||
}
|
||||
end
|
||||
fk.ai_active_skill["choose_players_skill"] = function(self, prompt, cancelable, data)
|
||||
local ret = self:callFromTable(fk.ai_choose_players, nil, data.skillName,
|
||||
self, data.targets, data.min_num, data.num, cancelable)
|
||||
|
||||
if ret then
|
||||
return self:buildUseReply({ skill = "choose_players_skill", subcards = { ret.cardId } }, ret.targets)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
require "packages.standard.ai.aux_skills"
|
||||
|
||||
--[[
|
||||
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
|
||||
|
@ -194,7 +195,7 @@ 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
|
||||
if self:isEnemy(p) then
|
||||
return true
|
||||
end
|
||||
end
|
||||
|
@ -215,13 +216,13 @@ 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
|
||||
if self:isEnemy(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)
|
||||
fk.ai_active_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)
|
||||
|
@ -247,7 +248,7 @@ fk.ai_choose_players["liuli"] = function(self, targets, min_num, num, cancelable
|
|||
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
|
||||
if self:isEnemy(p) and #self.use_tos < num and #cards > 0 then
|
||||
table.insert(self.use_tos, pid)
|
||||
self.use_id = { cards[1].id }
|
||||
return
|
||||
|
@ -262,3 +263,4 @@ fk.ai_choose_players["liuli"] = function(self, targets, min_num, num, cancelable
|
|||
end
|
||||
end
|
||||
end
|
||||
--]]
|
||||
|
|
|
@ -1,3 +1,38 @@
|
|||
-- 基本牌:杀,闪,桃
|
||||
|
||||
fk.ai_use_card["slash"] = function(self, pattern, prompt, cancelable, extra_data)
|
||||
local slashes = self:getCards("slash", "use", extra_data)
|
||||
if #slashes == 0 then return nil end
|
||||
|
||||
-- TODO: 目标合法性
|
||||
local targets = {}
|
||||
if self.enemies[1] then table.insert(targets, self.enemies[1].id) end
|
||||
|
||||
return self:buildUseReply(slashes[1].id, targets)
|
||||
end
|
||||
|
||||
fk.ai_use_card["peach"] = function(self, _, _, _, extra_data)
|
||||
local cards = self:getCards("peach", "use", extra_data)
|
||||
if #cards == 0 then return nil end
|
||||
|
||||
return self:buildUseReply(cards[1].id)
|
||||
end
|
||||
|
||||
-- 自救见军争卡牌AI
|
||||
fk.ai_use_card["#AskForPeaches"] = function(self)
|
||||
local room = self.room
|
||||
local deathEvent = room.logic:getCurrentEvent()
|
||||
local data = deathEvent.data[1] ---@type DyingStruct
|
||||
|
||||
-- TODO: 关于救不回来、神关羽之类的更复杂逻辑
|
||||
-- TODO: 这些逻辑感觉不能写死在此函数里面,得想出更加多样的办法
|
||||
if self:isFriend(room:getPlayerById(data.who)) then
|
||||
return fk.ai_use_card["peach"](self)
|
||||
end
|
||||
return nil
|
||||
end
|
||||
|
||||
--[[
|
||||
fk.ai_card.slash = {
|
||||
intention = 100, -- 身份值
|
||||
value = 4, -- 卡牌价值
|
||||
|
@ -101,7 +136,7 @@ fk.ai_use_play["slash"] = function(self, card)
|
|||
end
|
||||
end
|
||||
|
||||
fk.ai_ask_usecard["#slash-jink"] = function(self, pattern, prompt, cancelable, extra_data)
|
||||
fk.ai_use_card["#slash-jink"] = function(self, pattern, prompt, cancelable, extra_data)
|
||||
local act = self:getActives(pattern)
|
||||
if tonumber(prompt:split(":")[4]) > #act then
|
||||
return
|
||||
|
@ -138,7 +173,7 @@ fk.ai_ask_usecard["#slash-jink"] = function(self, pattern, prompt, cancelable, e
|
|||
end
|
||||
end
|
||||
|
||||
fk.ai_ask_usecard["#slash-jinks"] = fk.ai_ask_usecard["#slash-jink"]
|
||||
fk.ai_use_card["#slash-jinks"] = fk.ai_use_card["#slash-jink"]
|
||||
|
||||
fk.ai_use_play["snatch"] = function(self, card)
|
||||
for _, p in ipairs(self.friends_noself) do
|
||||
|
@ -164,7 +199,7 @@ fk.ai_nullification.snatch = function(self, card, to, from, positive)
|
|||
end
|
||||
end
|
||||
else
|
||||
if self:isEnemie(to) and self:isEnemie(from) then
|
||||
if self:isEnemy(to) and self:isEnemy(from) then
|
||||
if #self.avail_cards > 1 or self:isWeak(to) then
|
||||
self.use_id = self.avail_cards[1]
|
||||
end
|
||||
|
@ -196,7 +231,7 @@ fk.ai_nullification.dismantlement = function(self, card, to, from, positive)
|
|||
end
|
||||
end
|
||||
else
|
||||
if self:isEnemie(to) and self:isEnemie(from) then
|
||||
if self:isEnemy(to) and self:isEnemy(from) then
|
||||
if #self.avail_cards > 1 or self:isWeak(to) then
|
||||
self.use_id = self.avail_cards[1]
|
||||
end
|
||||
|
@ -222,7 +257,7 @@ fk.ai_nullification.indulgence = function(self, card, to, from, positive)
|
|||
end
|
||||
end
|
||||
else
|
||||
if self:isEnemie(to) then
|
||||
if self:isEnemy(to) then
|
||||
if #self.avail_cards > 1 or self:isWeak(to) then
|
||||
self.use_id = self.avail_cards[1]
|
||||
end
|
||||
|
@ -261,7 +296,7 @@ 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:isFriend(to) and self:isEnemy(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
|
||||
|
@ -271,7 +306,7 @@ end
|
|||
|
||||
fk.ai_nullification.ex_nihilo = function(self, card, to, from, positive)
|
||||
if positive then
|
||||
if self:isEnemie(to) then
|
||||
if self:isEnemy(to) then
|
||||
if #self.avail_cards > 1 or self:isWeak(to) then
|
||||
self.use_id = self.avail_cards[1]
|
||||
end
|
||||
|
@ -293,7 +328,7 @@ fk.ai_nullification.savage_assault = function(self, card, to, from, positive)
|
|||
end
|
||||
end
|
||||
else
|
||||
if self:isEnemie(to) then
|
||||
if self:isEnemy(to) then
|
||||
if #self.avail_cards > 1 or self:isWeak(to) then
|
||||
self.use_id = self.avail_cards[1]
|
||||
end
|
||||
|
@ -309,7 +344,7 @@ fk.ai_nullification.archery_attack = function(self, card, to, from, positive)
|
|||
end
|
||||
end
|
||||
else
|
||||
if self:isEnemie(to) then
|
||||
if self:isEnemy(to) then
|
||||
if #self.avail_cards > 1 or self:isWeak(to) then
|
||||
self.use_id = self.avail_cards[1]
|
||||
end
|
||||
|
@ -319,7 +354,7 @@ 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:isEnemy(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
|
||||
|
@ -410,7 +445,7 @@ 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] }
|
||||
return self:isEnemy(use.from) and { self.player:getCardIds("h")[1] }
|
||||
end
|
||||
|
||||
fk.ai_discard["#axe_skill"] = function(self, min_num, num, include_equip, cancelable, pattern, prompt)
|
||||
|
@ -420,7 +455,7 @@ fk.ai_discard["#axe_skill"] = function(self, min_num, num, include_equip, cancel
|
|||
if Fk:getCardById(cid):matchPattern(pattern) then
|
||||
table.insert(ids, cid)
|
||||
end
|
||||
if #ids >= min_num and self:isEnemie(effect.to)
|
||||
if #ids >= min_num and self:isEnemy(effect.to)
|
||||
and (self:isWeak(effect.to) or #self.player:getCardIds("he") > 3) then
|
||||
return ids
|
||||
end
|
||||
|
@ -436,3 +471,4 @@ fk.ai_skill_invoke["#eight_diagram_skill"] = function(self)
|
|||
local effect = self:eventData("CardEffect")
|
||||
return effect and self:isFriend(effect.to)
|
||||
end
|
||||
--]]
|
||||
|
|
Loading…
Reference in New Issue
Block a user