lib/gamefic/response.rb in gamefic-3.4.0 vs lib/gamefic/response.rb in gamefic-3.5.0
- old
+ new
@@ -6,23 +6,22 @@
#
class Response
# @return [Symbol]
attr_reader :verb
- # @return [Array<Query::Base>]
+ # @return [Array<Query::Base, Query::Text>]
attr_reader :queries
# @param verb [Symbol]
# @param narrative [Narrative]
- # @param queries [Array<Query::Base>]
+ # @param args [Array<Object>]
# @param meta [Boolean]
# @param block [Proc]
- def initialize verb, narrative, *queries, meta: false, &block
+ def initialize verb, narrative, *args, meta: false, &block
@verb = verb
- @queries = map_queryable_objects(queries)
+ @queries = map_queries(args, narrative)
@meta = meta
- @block = block
@callback = Callback.new(narrative, block)
end
# The `meta?` flag is just a way for authors to identify responses that
# serve a purpose other than performing in-game actions. Out-of-game
@@ -55,55 +54,87 @@
# True if the Response can be executed for the given actor and command.
#
# @param actor [Active]
# @param command [Command]
def accept? actor, command
- return false if command.verb != verb || command.arguments.length != queries.length
-
- queries.each_with_index do |query, idx|
- return false unless query.accept?(actor, command.arguments[idx])
- end
-
- true
+ command.verb == verb &&
+ command.arguments.length == queries.length &&
+ queries.zip(command.arguments).all? { |query, argument| query.accept?(actor, argument) }
end
def execute *args
- @callback.run *args
+ @callback.run(*args)
end
def precision
@precision ||= calculate_precision
end
+ # Turn an actor and an expression into a command by matching the
+ # expression's tokens to queries. Return nil if the expression
+ # could not be matched.
+ #
+ # @param actor [Actor]
+ # @param expression [Expression]
+ # @return [Command, nil]
+ def to_command actor, expression
+ return nil unless expression.verb == verb && expression.tokens.length <= queries.length
+
+ results = filter(actor, expression)
+ return nil unless results
+
+ Command.new(
+ verb,
+ results.map(&:match),
+ results.sum(&:strictness),
+ precision
+ )
+ end
+
private
+ def filter actor, expression
+ remainder = ''
+ result = queries.zip(expression.tokens)
+ .map do |query, token|
+ token = "#{remainder} #{token}".strip
+ result = query.filter(actor, token)
+ return nil unless result.match
+
+ remainder = result.remainder
+ result
+ end
+ result if remainder.empty?
+ end
+
def generate_default_syntax
- user_friendly = verb.to_s.gsub(/_/, ' ')
- args = []
- used_names = []
- queries.each do |_c|
- num = 1
- new_name = ":var"
- while used_names.include? new_name
- num += 1
- new_name = ":var#{num}"
- end
- used_names.push new_name
- user_friendly += " #{new_name}"
- args.push new_name
- end
- Syntax.new(user_friendly.strip, "#{verb} #{args.join(' ')}".strip)
+ args = queries.length.times.map { |num| num.zero? ? ':var' : ":var#{num + 1}" }
+ tmpl = "#{verb} #{args.join(' ')}".strip
+ Syntax.new(tmpl.gsub('_', ' '), tmpl)
end
def calculate_precision
- total = 0
- queries.each { |q| total += q.precision }
- total -= 1000 if verb.nil?
+ total = queries.sum(&:precision)
+ total -= 1000 unless verb
total
end
- def map_queryable_objects queries
- # @todo Considering moving mapping from Actions to here
- queries
+ def map_queries args, narrative
+ args.map do |arg|
+ select_query(arg, narrative).tap { |qry| qry.narrative = narrative }
+ end
+ end
+
+ def select_query arg, narrative
+ case arg
+ when Entity, Class, Module, Proc, Proxy
+ narrative.available(arg)
+ when String, Regexp
+ narrative.plaintext(arg)
+ when Query::Base, Query::Text
+ arg
+ else
+ raise ArgumentError, "invalid argument in response: #{arg.inspect}"
+ end
end
end
end