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