lib/gamefic/scene/active_choice.rb



# frozen_string_literal: true


module Gamefic
  module Scene
    # A scene that presents a list of optional choices. The scene can still

    # attempt to process input that does not match any of the options.

    #

    # Authors can use the `without_selection` class method to select one of

    # three actions to take when the user does not enter one of the options:

    # `:perform`, `:recue`, or `:continue`.

    #

    class ActiveChoice < MultipleChoice
      WITHOUT_SELECTION_ACTIONS = %i[perform recue continue].freeze

      use_props_class Props::MultipleChoice

      def finish
        return super if props.selected?

        send(self.class.without_selection_action)
      end

      def without_selection_action
        self.class.without_selection_action
      end

      def self.type
        'ActiveChoice'
      end

      # Select the behavior for input that does not match a selectable option.

      # The available settings are `:perform`, `:recue`, and `:continue`.

      #

      # * `:perform` - Skip the `on_finish` blocks and try to perform the input

      #   as a command. This is the default behavior.

      # * `:recue` - Restart the scene until the user makes a valid selection.

      #   This is the same behavior as a `MultipleChoice` scene.

      # * `:continue` - Execute the `on_finish` blocks regardless of whether the

      #   input matches an option.

      #

      # @param action [Symbol]

      def self.without_selection(action)
        WITHOUT_SELECTION_ACTIONS.include?(action) ||
          raise(ArgumentError, "without_selection_action must be one of #{WITHOUT_SELECTION_ACTIONS.map(&:inspect).join_or}")

        @without_selection_action = action
      end

      # @return [Symbol]

      def self.without_selection_action
        @without_selection_action ||= :perform
      end

      def self.inherited(klass)
        super
        klass.without_selection without_selection_action
      end

      private

      def perform
        actor.perform props.input
      end

      def recue
        actor.tell props.invalid_message
        actor.recue
      end

      def continue
        run_finish_blocks
      end
    end
  end
end