class Demiurge::ActionItem
A Demiurge::ActionItem
keeps track of actions from Ruby code blocks and implements the Demiurge
DSL
for action code, including inside World Files.
@since 0.0.1
Constants
- ACTION_LEGAL_KEYS
Legal keys to pass to
ActionItem#register_actions
' hash @since 0.0.1
Public Class Methods
Constructor. Set up ActionItem-specific things like EveryXTicks actions.
@param name [String] The registered StateItem
name @param engine [Demiurge::Engine] The Engine
this item is part of @param state [Hash] The state hash for this item @return [void] @since 0.0.1
Demiurge::StateItem::new
# File lib/demiurge/action_item.rb, line 15 def initialize(name, engine, state) super # Set @name and @engine and @state @every_x_ticks_intention = ActionItemInternal::EveryXTicksIntention.new(engine, name) nil end
Public Instance Methods
An internal function that provides the object's internal state to an action block via a Runner class.
@return [Hash] The internal state of this item for use in DSL
action blocks @api private @since 0.0.1
# File lib/demiurge/action_item.rb, line 88 def __state_internal @state end
Callback to be called from the Engine
when all items are loaded.
@return [void] @since 0.0.1
# File lib/demiurge/action_item.rb, line 25 def finished_init loc = self.location loc.move_item_inside(self) unless loc.nil? nil end
Get the action hash structure for a given action name. This is normally done to verify that a specific action name exists at all.
@param action_name [String] The action name to query @return [Hash, nil] A hash of information about the action, or nil if the action doesn't exist @since 0.0.1
# File lib/demiurge/action_item.rb, line 181 def get_action(action_name) action = @engine.action_for_item(@name, action_name) if !action && state["parent"] # Do we have a parent and no action definition yet? If so, defer to the parent. action = @engine.item_by_name(state["parent"]).get_action(action_name) end action end
Get this item's intentions for the next tick.
@return [Array<Demiurge::Intention>] An array of intentions for next tick @since 0.0.1
# File lib/demiurge/action_item.rb, line 96 def intentions_for_next_step everies = @state["everies"] return [] if everies.nil? || everies.empty? [@every_x_ticks_intention] end
Get the location StateItem
where this item is located.
@return [Demiurge::StateItem, nil] The location's StateItem
, or nil if this item has no location @since 0.0.1
# File lib/demiurge/action_item.rb, line 46 def location ln = location_name return nil if ln == "" || ln == nil @engine.item_by_name(location_name) end
Get the name of this item's location. This is compatible with complex positions, and removes any sub-location suffix, if there is one.
@return [String, nil] The location name where this item exists, or nil if it has no location @since 0.0.1
# File lib/demiurge/action_item.rb, line 37 def location_name pos = @state["position"] pos ? pos.split("#",2)[0] : nil end
A Position can be simply a location (“here's a room-type object and you're in it”) or something more specific, such as a specific coordinate within a room. In general, a Position consists of a location's unique item name, optionally followed by an optional pound sign (“#”) and zone-specific additional coordinates.
@return [String, nil] This item's position, or nil if it has no location.
# File lib/demiurge/action_item.rb, line 60 def position @state["position"] end
This method is called by (among other things) define_action to specify things about an action. It's how to specify the action's code, how busy it makes an agent when it occurs, what Runner to use with it, and any appropriate String tags. While it can be called multiple times to specify different things about a single action, it must not be called with the same information. So the block can only be specified once, “busy” can only be specified once and so on.
This means that if an action's block is given implicitly by something like an every_X_ticks declaration, it can use define_action to set “busy” or “engine_code”. But it can't define a different block of code to run with define_action.
@param action_hash [Hash] Specify something or everything about an action by its name. @option action_hash [String] name The name of the action, which is required. @option action_hash [Proc] block The block of code for the action itself @return void @since 0.0.1
# File lib/demiurge/action_item.rb, line 125 def register_actions(action_hash) @engine.register_actions_by_item_and_action_name(@name => action_hash) end
This is a raw, low-level way to execute an action of an ActionItem
. It doesn't wait for Intentions. It doesn't send extra notifications. It doesn't offer or give a chance to cancel the action. It just runs.
@param action_name [String] The name of the action to run. Must already be registered. @param args [Array] Additional arguments to pass to the action's code block @param current_intention [nil, Intention] Current intention being executed, if any. This is used for to cancel an intention, if necessary @return [void] @since 0.0.1
# File lib/demiurge/action_item.rb, line 139 def run_action(action_name, *args, current_intention: nil) action = get_action(action_name) raise ::Demiurge::Errors::NoSuchActionError.new("No such action as #{action_name.inspect} for #{@name.inspect}!", "item" => self.name, "action" => action_name, execution_context: @engine.execution_context) unless action block = action["block"] raise ::Demiurge::Errors::NoSuchActionError.new("Action was never defined for #{action_name.inspect} of object #{@name.inspect}!", "item" => self.name, "action" => action_name, execution_context: @engine.execution_context) unless block runner_constructor_args = {} if action["engine_code"] block_runner_type = ActionItemInternal::EngineBlockRunner elsif self.agent? block_runner_type = ActionItemInternal::AgentBlockRunner runner_constructor_args[:current_intention] = current_intention else block_runner_type = ActionItemInternal::ActionItemBlockRunner runner_constructor_args[:current_intention] = current_intention end # TODO: can we save block runners between actions? block_runner = block_runner_type.new(self, **runner_constructor_args) begin @engine.push_context("running_action" => action_name, "running_action_item" => @name) do block_runner.instance_exec(*args, &block) end rescue #STDERR.puts "#{$!.message}\n#{$!.backtrace.join("\n")}" raise ::Demiurge::Errors::BadScriptError.new("Script error of type #{$!.class} with message: #{$!.message}", { "runner type": block_runner_type.to_s, "action" => action_name, }, execution_context: @engine.execution_context); end nil end
Get the StateItem
of the Zone
where this item is located. This may be different from its “home” Zone
.
@return [StateItem, nil] This item's Zone's StateItem
, or nil in the very unusual case that it has no current Zone
.
# File lib/demiurge/action_item.rb, line 68 def zone zn = zone_name zn ? @engine.item_by_name(zn) : nil end
Get the Zone
name for this StateItem's current location, which may be different from its “home” Zone
.
@return [String, nil] This item's Zone's name, or nil in the very unusual case that it has no current Zone
.
# File lib/demiurge/action_item.rb, line 77 def zone_name l = location l ? l.zone_name : state["zone"] end