src / dialogue / helper.rb

Attached Scenes

Note
No attached scenes.

Content

# = Redirects method calls to Xxx.current
#
#   `delegate Foo, :bar` means calling `bar` will forward to `Foo.current.bar`
#
def delegate(const, *keys)
  keys.each do |key|
    define_method(key) do |*args|
      const.current.send(key, *args)
    end
  end
end


class Dialogue
  class << self
    attr_accessor :current
  end

  attr_accessor :fiber, :last_value

  def initialize(&block)
    self.fiber = Fiber.new do
      yield
      Godot.emit_signal :finished, true
    end
    Dialogue.current = self
  end

  def continue(val = nil)
    fiber.resume(val)
  end

  def listen!
    self.last_value = Fiber.yield
  end

  def ___?
    last_value
  end

  def event(sym)
    Godot.emit_signal :event, sym
  end

  def wait(seconds)
    Godot.emit_signal :wait, seconds
    Dialogue.current.listen!
  end
end
delegate Dialogue, :continue, :___?, :event, :wait #


class Speaker
  class << self
    attr_accessor :all, :current

    def current=(speaker)
      current.temporary_face = nil if current
      @current = speaker
    end
  end
  self.all = []

  attr_accessor(
    :name,
    :temporary_face,
    :permanent_face,
    :position
  )

  def initialize(name, position: :right)
    self.name = name
    self.position = position
    self.permanent_face = ''
    Speaker.all << self
  end

  def says(str)
    communicate :says, str
  end

  def asks(str, choices = { 'Yes' => true, 'No' => false })
    communicate :asks, str, choices
  end

  def with(face)
    self.permanent_face = ''
    self.temporary_face = face.to_s
  end

  def got(face)
    self.permanent_face = face.to_s
  end

  def face
    temporary_face || permanent_face
  end

  def position_swap
    self.position = position == :left ? :right : :left
  end

  private
    def emit(key, args = {})
      Godot.emit_signal key, { name: name, face: face, position: position, **args }
    end

    def communicate(key, str, choices = {})
      emit key, { content: str, choices: choices }
      Dialogue.current.listen!
    end
end
delegate Speaker, :says, :asks, :with, :got #


# = const_missing
#
#   `Alice;` sets `Speaker.current` to the Speaker instance named "Alice"
#
def Object.const_missing(name)
  speaker = Speaker.all.find{|s| s.name == name.to_s }
  if speaker
    Speaker.current = speaker
  else
    super
  end
end


module SymbolExt
  def ~@
    event self
  end
end
Symbol.prepend SymbolExt


module StringExt
  def -@
    says self
  end

  def !@
    asks self
  end
end
String.prepend StringExt


module ArrayExt
  def -@
    says self
  end

  def !@
    asks self
  end
end
Array.prepend ArrayExt


module NumericExt
  def later
    wait self
  end
end
Numeric.prepend NumericExt


def speaker_position_swap!
  Speaker.all.each(&:position_swap) #
end


def speakers(names)
  names.each_with_index do |name, i|
    Speaker.new(name, position: (i.even? ? :left : :right))
  end
end