FreeKill/lua/server/ai/ai.lua

230 lines
6.4 KiB
Lua
Raw Permalink Normal View History

-- SPDX-License-Identifier: GPL-3.0-or-later
-- AI base class.
-- Do nothing.
---@class AI: Object
---@field public room Room
---@field public player ServerPlayer
---@field public command string
2024-11-09 19:27:41 +08:00
---@field public data any
---@field public handler ReqActiveSkill 可能空,但是没打问号(免得一堆警告)
local AI = class("AI")
function AI:initialize(player)
2024-11-09 19:27:41 +08:00
---@diagnostic disable-next-line
self.room = RoomInstance
self.player = player
2024-11-09 19:27:41 +08:00
end
function AI:__tostring()
return string.format("%s: %s", self.class.name, tostring(self.player))
end
-- activeSkill, responseCard, useCard, playCard 四巨头专属
function AI:isInDashboard()
if not (self.handler and self.handler:isInstanceOf(Fk.request_handlers["AskForUseActiveSkill"])) then
fk.qWarning("请检查是否在AI中调用了专属于dashboard操作的一系列函数")
fk.qWarning(debug.traceback())
return false
end
return true
end
function AI:getPrompt()
local handler = self.handler
if not handler then return "" end
return handler.prompt
end
--- 返回当前手牌区域内包含展开的pile中所有可选且未选中的卡牌 返回ids
---@param pattern string? 可以带一个过滤条件
---@return integer[]
function AI:getEnabledCards(pattern)
if not self:isInDashboard() then return Util.DummyTable end
local ret = {}
for cid, item in pairs(self.handler.scene:getAllItems("CardItem")) do
if item.enabled and not item.selected then
if (not pattern) or Exppattern:Parse(pattern):match(Fk:getCardById(cid)) then
table.insert(ret, cid)
end
2024-11-09 19:27:41 +08:00
end
end
return ret
end
--- 返回当前所有可选并且还未选中的角色,包括自己
---@return ServerPlayer[]
function AI:getEnabledTargets()
if not self:isInDashboard() then return Util.DummyTable end
local room = self.room
local ret = {}
for pid, item in pairs(self.handler.scene:getAllItems("Photo")) do
if item.enabled and not item.selected then
table.insert(ret, room:getPlayerById(pid))
end
end
return ret
end
function AI:hasEnabledTarget()
if not self:isInDashboard() then return false end
local room = self.room
for _, item in pairs(self.handler.scene:getAllItems("Photo")) do
if item.enabled and not item.selected then
return true
end
end
return false
end
--- 获取技能面板中所有可以按下的技能按钮
---@return string[]
--- 获取技能面板中所有可以按下的技能按钮
---@return string[]
function AI:getEnabledSkills()
if not self:isInDashboard() then return Util.DummyTable end
local ret = {}
for name, item in pairs(self.handler.scene:getAllItems("SkillButton")) do
if item.enabled and not item.selected then
table.insert(ret, name)
end
end
return ret
end
---@return integer[]
function AI:getSelectedCards()
if not self:isInDashboard() then return Util.DummyTable end
return self.handler.pendings
end
---@return Card?
function AI:getSelectedCard()
if not self:isInDashboard() then return Util.DummyTable end
local handler = self.handler
if handler.selected_card then return handler.selected_card end
if not handler.skill_name then return end
local skill = Fk.skills[handler.skill_name]
if not skill:isInstanceOf(ViewAsSkill) then return end
return skill:viewAs(handler.pendings)
end
---@return ServerPlayer[]
function AI:getSelectedTargets()
if not self:isInDashboard() then return Util.DummyTable end
return table.map(self.handler.selected_targets, function(pid)
return self.room:getPlayerById(pid)
end)
end
function AI:getSelectedSkill()
if not self:isInDashboard() then return nil end
return self.handler.skill_name
end
function AI:selectCard(cid, selected)
if not self:isInDashboard() then return end
verbose("%s选择卡牌%d(%s)", selected and "" or "取消", cid, tostring(Fk:getCardById(cid)))
self.handler:update("CardItem", cid, "click", { selected = selected })
end
---@param player ServerPlayer
function AI:selectTarget(player, selected)
if not self:isInDashboard() then return end
verbose("%s选择角色%s", selected and "" or "取消", tostring(player))
self.handler:update("Photo", player.id, "click", { selected = selected })
end
function AI:selectSkill(skill_name, selected)
if not self:isInDashboard() then return end
verbose("%s选择技能%s", selected and "" or "取消", skill_name)
self.handler:update("SkillButton", skill_name, "click", { selected = selected })
end
function AI:unSelectAllCards()
for _, id in ipairs(self:getSelectedCards()) do
self:selectCard(id, false)
end
end
function AI:unSelectAllTargets()
for _, p in ipairs(self:getSelectedTargets()) do
self:selectTarget(p, false)
end
end
function AI:unSelectSkill()
local skill = self:getSelectedSkill()
if not skill then return end
self:selectSkill(skill, false)
end
function AI:unSelectAll()
self:unSelectSkill()
self:unSelectAllCards()
self:unSelectAllTargets()
end
function AI:okButtonEnabled()
if not self:isInDashboard() then return false end
return self.handler:feasible()
end
function AI:isDeadend()
if not self:isInDashboard() then return true end
return (not self:okButtonEnabled()) and #self:getEnabledCards() == 0
and #self:getEnabledTargets() == 0
end
function AI:doOKButton()
if not self:isInDashboard() then return end
if not self:okButtonEnabled() then return "" end
return self.handler:doOKButton()
end
2024-11-09 19:27:41 +08:00
---@return Skill?
function AI:currentSkill()
local room = self.room
if room.current_cost_skill then return room.current_cost_skill end
local sname = room.logic:getCurrentSkillName()
if sname then
return Fk.skills[sname]
end
end
function AI:makeReply()
Self = self.player
2024-11-09 19:27:41 +08:00
local now = os.getms()
local fn = self["handle" .. self.command]
local is_active = self.command == "AskForUseActiveSkill"
if is_active then
local skill = Fk.skills[self.data[1]]
skill._extra_data = self.data[4]
end
local ret = "__cancel"
if fn then
local handler_class = Fk.request_handlers[self.command]
if handler_class then
self.handler = handler_class:new(self.player, self.data)
self.handler:setup()
end
ret = fn(self, self.data)
end
if ret == nil or ret == "" then ret = "__cancel" end
self.handler = nil
if is_active then
local skill = Fk.skills[self.data[1]]
skill._extra_data = Util.DummyTable
end
verbose("%s 在%.2fms后得出结果:%s", self.command, (os.getms() - now) / 1000, json.encode(ret))
return ret
end
return AI