Class: Demiurge::Agent

Inherits:
ActionItem show all
Defined in:
lib/demiurge/agent.rb

Overview

Agents correspond roughly to "mobiles" in many games. An agent isn't particularly different from other Demiurge objects, but it's useful to have some helper classes for things like pathfinding. Also, humans expect agents to have some finite ability to perform actions over time, so it's nice to regulate how much an agent can get done and how "busy" it is. This keeps an AI agent from just queueing up 30 move intentions and crossing the room in a single tick, for instance. It does not keep that same AI from having an intentional 30-square move that works in a single tick, but it slows the rate of actions. Agents get a single "real" intention, unlike, say, rooms, which can have lots going on at once.

Since:

  • 0.0.1

Direct Known Subclasses

WanderingAgent

Constant Summary

Constants inherited from ActionItem

Demiurge::ActionItem::ACTION_LEGAL_KEYS

Instance Attribute Summary

Attributes inherited from StateItem

#engine, #name

Instance Method Summary collapse

Methods inherited from ActionItem

#__state_internal, #get_action, #get_actions_with_tags, #location, #location_name, #position, #register_actions, #run_action, #zone, #zone_name

Methods inherited from StateItem

from_name_type, #get_structure, #state, #state_type, #zone?

Constructor Details

#initialize(*args) ⇒ Agent

Returns a new instance of Agent

Since:

  • 0.0.1



18
19
20
21
22
# File 'lib/demiurge/agent.rb', line 18

def initialize(*args)
  super
  state["queued_actions"] ||= []
  state["queue_number"] ||= 0
end

Instance Method Details

#agent?Boolean

An Agent is, indeed, an Agent.

Returns:

  • (Boolean)

    Return true for Agent and its subclasses.

Since:

  • 0.0.1



34
35
36
# File 'lib/demiurge/agent.rb', line 34

def agent?
  true
end

#clear_intention_queuevoid

This method returns an undefined value.

Any queued actions waiting to occur will be discarded.

Since:

  • 0.0.1



109
110
111
112
# File 'lib/demiurge/agent.rb', line 109

def clear_intention_queue
  state.delete "queued_actions"
  nil
end

#finished_initObject

Since:

  • 0.0.1



24
25
26
27
28
# File 'lib/demiurge/agent.rb', line 24

def finished_init
  super
  @agent_maintenance = AgentInternal::AgentMaintenanceIntention.new(engine, @name)
  state["busy"] ||= 0 # By default, start out idle.
end

#intentions_for_next_stepArray<Intention>

Calculate the agent's intentions for the following tick. These Intentions can potentially trigger other Intentions.

Returns:

  • (Array<Intention>)

    The Intentions for the (first part of the) following tick.

Since:

  • 0.0.1



83
84
85
86
# File 'lib/demiurge/agent.rb', line 83

def intentions_for_next_step
  agent_action = AgentInternal::AgentActionIntention.new(@name, engine)
  super + [@agent_maintenance, agent_action]
end

#move_to_position(pos, options = {}) ⇒ void

This method returns an undefined value.

This method will move the agent and notify about that change. It doesn't use an intention or an agent's action queue, and it doesn't wait for a tick to happen. It just does it. The method does handle exits and generally allows the location to respond. But it's assumed that the offer cycle, if it needs to happen, has happened already.

Parameters:

  • pos (String)

    A position string to move to

  • options (Hash) (defaults to: {})

    A hash of how to do the movement; Demiurge doesn't internally use this hash, but your World Files or display library may do so

Since:

  • 0.0.1



49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
# File 'lib/demiurge/agent.rb', line 49

def move_to_position(pos, options = {})
  old_pos = self.position
  old_loc = self.location_name
  old_zone_name = self.zone_name
  expected_new_loc = pos.split("#")[0]

  if old_loc && !self.location
    raise ::Demiurge::Errors::LocationNameNotFoundError.new("Item #{@name.inspect} has an old location name (#{old_loc.inspect}) with no matching location object!",
                                                            { "item_name" => @name, "location_name" => old_loc, "moving_to" => pos },
                                                            execution_context: @engine.execution_context);
  end

  if old_loc != nil && expected_new_loc == old_loc
    self.location.item_change_position(self, old_pos, pos)
  elsif old_loc != nil
    # This also handles zone changes.
    self.location.item_change_location(self, old_pos, pos)
  end
  # We're not guaranteed to wind up where we expected, so get the
  # new location *after* item_change_location or
  # item_change_position.
  new_loc = self.location_name

  @engine.send_notification({ old_position: old_pos, old_location: old_loc, new_position: self.position, new_location: new_loc },
                              type: Demiurge::Notifications::MoveFrom, zone: old_zone_name, location: old_loc, actor: @name, include_context: true)
  @engine.send_notification({ old_position: old_pos, old_location: old_loc, new_position: self.position, new_location: new_loc, options: options },
                              type: Demiurge::Notifications::MoveTo, zone: self.zone_name, location: self.location_name, actor: @name, include_context: true)
end

#queue_action(action_name, *args) ⇒ Integer

Queue an action to be run after previous actions are complete, and when the agent is no longer busy from taking them. The action queue entry is assigned a unique-per-agent queue number, which is returned from this action.

Parameters:

  • action_name (String)

    The name of the action to take when possible

  • args (Array)

    Additional arguments to pass to the action's code block

Returns:

  • (Integer)

    Returns the queue number for this action - note that queue numbers are only unique per-agent

Raises:

Since:

  • 0.0.1



97
98
99
100
101
102
103
# File 'lib/demiurge/agent.rb', line 97

def queue_action(action_name, *args)
  raise ::Demiurge::Errors::NoSuchActionError.new("Not an action: #{action_name.inspect}!", { "action_name" => action_name },
                                                  execution_context: @engine.execution_context) unless get_action(action_name)
  state["queue_number"] += 1
  state["queued_actions"].push([action_name, args, state["queue_number"]])
  state["queue_number"]
end