class Demiurge::AgentInternal::AgentActionIntention
An AgentActionIntention
is how the agent takes queued actions each tick.
@note There is a bit of weirdness in how this intention handles
{#allowed?} and {#offer}. We want to be able to queue an action on the same tick that we execute it if the agent is idle. So we count this intention as #allowed? even if the queue is empty, then silent-cancel the intention during {#offer} if nobody has added anything to it. If you see a lot of cancel notifications from this object with "silent" set, now you know why.
@api private
Attributes
@return [String] The queued action name this Intention
will next take
@return [StateItem] The agent to whom this Intention
applies
Public Class Methods
Constructor. Takes an agent name and an engine
Demiurge::ActionItemInternal::ActionIntention::new
# File lib/demiurge/agent.rb, line 161 def initialize(name, engine) super(engine, name, "") @agent = engine.item_by_name(name) raise ::Demiurge::Errors::NoSuchAgentError.new("No such agent as #{name.inspect} found in AgentActionIntention!", "agent" => name, execution_context: engine.execution_context) unless @agent end
Public Instance Methods
This action is allowed if the agent is not busy, or will become not-busy soon
# File lib/demiurge/agent.rb, line 187 def allowed? # If the agent's busy state will clear this turn, this action # could happen. We intentionally don't send a "disallowed" # notification for the action. It's not cancelled, nor is it # dispatched successfully. It's just waiting for a later tick to # do one of those two things. return false if @agent.state["busy"] > 1 # A dilemma: if we cancel now when no actions are queued, then # any action queued this turn (e.g. from an # EveryXActionsIntention) won't be executed -- we said this # intention wasn't happening. If we *don't* return false in the # "allowed?" phase then we'll wind up sending out a cancel # notice every turn when there are no actions. So we add a # "silent" info option to the normal-every-turn cancellations, # but we *do* allow-then-cancel even in perfectly normal # circumstances. true end
If the agent can do so, take the action in question.
# File lib/demiurge/agent.rb, line 208 def apply unless agent.state["busy"] > 0 || agent.state["queued_actions"].empty? # Pull the first entry off the action queue queue = @agent.state["queued_actions"] if queue && queue.size > 0 if @action_queue_number != queue[0][2] @engine.admin_warning("Somehow the agent's action queue has gotten screwed up mid-offer!", "agent" => @name) else queue.shift # Remove the queue entry end end agent.run_action(@action_name, *@action_args, current_intention: self) agent.state["busy"] += (@action_struct["busy"] || 1) end end
Send out a notification to indicate this ActionIntention was applied.
@return [void] @since 0.2.0
# File lib/demiurge/agent.rb, line 255 def apply_notification @engine.send_notification({ id: @intention_id, intention_type: self.class.to_s, queue_number: @action_queue_number, action_name: @action_name, action_args: @action_args, }, type: Demiurge::Notifications::IntentionApplied, zone: @item.zone_name, location: @item.location_name, actor: @item.name, include_context: true) nil end
Send out a notification to indicate this ActionIntention was cancelled. If “silent” is set to true in the cancellation info, no notification will be sent.
@return [void] @since 0.2.0
# File lib/demiurge/agent.rb, line 230 def cancel_notification return if @cancelled_info && @cancelled_info["silent"] @engine.send_notification({ reason: @cancelled_reason, by: @cancelled_by, id: @intention_id, intention_type: self.class.to_s, info: @cancelled_info, queue_number: @action_queue_number, action_name: @action_name, action_args: @action_args, }, type: Demiurge::Notifications::IntentionCancelled, zone: @item.zone_name, location: @item.location_name, actor: @item.name, include_context: true) nil end
An action being pulled from the action queue is offered normally.
Demiurge::ActionItemInternal::ActionIntention#offer
# File lib/demiurge/agent.rb, line 169 def offer # Don't offer the action if it's going to be a no-op. if @agent.state["busy"] > 0 # See comment on "silent" in #allowed? below. self.cancel "Agent #{@name.inspect} was too busy to act (#{@agent.state["busy"]}).", "silent" => "true" return elsif @agent.state["queued_actions"].empty? self.cancel "Agent #{@name.inspect} had no actions during the 'offer' phase.", "silent" => "true" return end # Now offer the agent's action via the usual channels action = @agent.state["queued_actions"][0] @action_name, @action_args, @action_queue_number = *action @action_struct = @agent.get_action(@action_name) super end