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

action_name[R]

@return [String] The queued action name this Intention will next take

agent[R]

@return [StateItem] The agent to whom this Intention applies

Public Class Methods

new(name, engine) click to toggle source

Constructor. Takes an agent name and an engine

# 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

allowed?() click to toggle source

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
apply() click to toggle source

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
apply_notification() click to toggle source

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
cancel_notification() click to toggle source

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
offer() click to toggle source

An action being pulled from the action queue is offered normally.

# 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