---@type Tinkr, Bastion local Tinkr, Bastion = ... -- APL (Attack priority list) class ---@class Bastion.APL ---@field apl Bastion.APLActor[] ---@field variables table ---@field name string ---@field last { successful: { name: string, time: number, index: number }, attempted: { name: string, time: number, index: number } } local APL = {} APL.__index = APL -- Constructor ---@param name string ---@return Bastion.APL function APL:New(name) local self = setmetatable({}, APL) self.apl = {} self.variables = {} self.name = name self.last = { successful = { name = "", time = -1, index = -1, }, attempted = { name = "", time = -1, index = -1, }, } return self end -- Add a variable to the APL ---@param name string ---@param value any function APL:SetVariable(name, value) self.variables[name] = value end -- Get and evaluate a variable ---@param name string ---@return boolean function APL:GetVariable(name) return self.variables[name] end ---@class Bastion.APL.Actor.Variable.Table : Bastion.APLActor.Table.Base ---@field variable string ---@field cb fun(...):any ---@field _apl Bastion.APL -- Add variable ---@param name string ---@param cb fun(...):any ---@return Bastion.APLActor function APL:AddVariable(name, cb) local actor = Bastion.APLActor:New({ type = "variable", variable = name, cb = cb, _apl = self, }) table.insert(self.apl, actor) return actor end ---@class Bastion.APL.Actor.Action.Table : Bastion.APLActor.Table.Base ---@field action string ---@field cb fun(...):any -- Add a manual action to the APL ---@param action string ---@param cb fun(...):any ---@return Bastion.APLActor function APL:AddAction(action, cb) local actor = Bastion.APLActor:New({ type = "action", action = action, cb = cb, }) table.insert(self.apl, actor) return actor end ---@class Bastion.APL.Actor.Spell.Table : Bastion.APLActor.Table.Base ---@field spell Bastion.Spell ---@field condition? string|fun(self: Bastion.Spell): boolean ---@field castableFunc false | fun(self: Bastion.Spell): boolean ---@field target Bastion.Unit | false ---@field onCastFunc false | fun(self: Bastion.Spell):any -- Add a spell to the APL ---@param spell Bastion.Spell ---@param condition? string|fun(self: Bastion.Spell):boolean ---@return Bastion.APLActor function APL:AddSpell(spell, condition) local castableFunc = spell.CastableIfFunc local onCastFunc = spell.OnCastFunc local target = spell:GetTarget() local actor = Bastion.APLActor:New({ type = "spell", spell = spell, condition = condition, castableFunc = castableFunc, target = target, onCastFunc = onCastFunc, }) table.insert(self.apl, actor) return actor end ---@class Bastion.APL.Actor.Item.Table : Bastion.APLActor.Table.Base ---@field item Bastion.Item ---@field condition? string | fun(self: Bastion.Item): boolean ---@field usableFunc false | fun(...): boolean ---@field target Bastion.Unit | nil -- Add an item to the APL ---@param item Bastion.Item ---@param condition? string | fun(self: Bastion.Item): boolean ---@return Bastion.APLActor function APL:AddItem(item, condition) local usableFunc = item.UsableIfFunc local target = item:GetTarget() local actor = Bastion.APLActor:New({ type = "item", item = item, condition = condition, usableFunc = usableFunc, target = target, }) table.insert(self.apl, actor) return actor end ---@class Bastion.APL.Actor.APL.Table : Bastion.APLActor.Table.Base ---@field apl Bastion.APL ---@field condition? fun(...):boolean -- Add an APL to the APL (for sub APLs) ---@param apl Bastion.APL ---@param condition fun(...):boolean ---@return Bastion.APLActor function APL:AddAPL(apl, condition) if not condition then error("Bastion: APL:AddAPL: No condition for APL " .. apl.name) end local actor = Bastion.APLActor:New({ type = "apl", apl = apl, condition = condition, }) table.insert(self.apl, actor) return actor end ---@param name string ---@param enabled? boolean function APL:ToggleAPL(name, enabled) for _, actor in ipairs(self.apl) do if actor.actor.type == "apl" and actor.name == name then actor.enabled = type(enabled) == "boolean" and enabled or not actor.enabled break end end end function APL:UpdateLastAttempted(name, index) self.last.attempted.name = name self.last.attempted.time = GetTime() self.last.attempted.index = index end function APL:UpdateLastSuccessful(name, index) self.last.successful.name = name self.last.successful.time = GetTime() self.last.successful.index = index end -- Execute the APL function APL:Execute() for i, actor in ipairs(self.apl) do self:UpdateLastAttempted(actor.name, i) if actor.enabled and (not actor:HasTraits() or actor:Evaluate()) and actor:Execute() then self:UpdateLastSuccessful(actor.name, i) return true end end end ---@class Bastion.APL.Actor.Sequencer.Table : Bastion.APLActor.Table.Base ---@field sequencer Bastion.Sequencer ---@field condition? fun(...):boolean -- Add a Sequencer to the APL ---@param sequencer Bastion.Sequencer ---@param condition fun(...):boolean ---@return Bastion.APLActor function APL:AddSequence(sequencer, condition) local actor = Bastion.APLActor:New({ type = "sequencer", sequencer = sequencer, condition = condition, }) table.insert(self.apl, actor) return actor end -- tostring ---@return string function APL:__tostring() return "Bastion.__APL(" .. self.name .. ")" end return APL