<commit_before># -*- coding: utf-8 -*-
require 'forwardable'
require 'mutex_m'

module Smalruby
  # キャラクターを表現するクラス
  class Character < Sprite
    extend Forwardable

    cattr_accessor :font_cache
    self.font_cache = {}
    font_cache.extend(Mutex_m)

    cattr_accessor :sound_cache
    self.sound_cache = {}
    sound_cache.extend(Mutex_m)

    cattr_accessor :hardware_cache
    self.hardware_cache = {}
    hardware_cache.extend(Mutex_m)

    attr_accessor :event_handlers
    attr_accessor :threads
    attr_accessor :checking_hit_targets
    attr_accessor :costumes
    attr_accessor :costume_index
    attr_reader :rotation_style

    def initialize(option = {})
      defaults = {
        x: 0,
        y: 0,
        costume: nil,
        angle: 0,
        visible: true,
        rotation_style: :free
      }
      opt = process_optional_arguments(option, defaults)

      @costumes = [opt[:costume]].flatten.compact.map { |costume|
        costume.is_a?(String) ? Image.load(asset_path(costume)) : costume
      }
      @costume_index = 0
      super(opt[:x], opt[:y], @costumes[@costume_index])

      @event_handlers = {}
      @threads = []
      @checking_hit_targets = []
      @angle = 0 unless Util.windows?

      self.scale_x = 1.0
      self.scale_y = 1.0
      @vector = { x: 1, y: 0 }

      [:visible].each do |k|
        if opt.key?(k)
          send("#{k}=", opt[k])
        end
      end

      self.rotation_style = opt[:rotation_style]

      self.angle = opt[:angle] if opt[:angle] != 0

      # HACK: Windows XP SP3の環境でワーカースレッドで音声を読み込めな
      # い不具合が発生した。このためメインスレッドでプリロードしておく。
      %w(do re mi fa so ra si do_2).each do |n|
        new_sound("piano_#{n}.wav")
      end

      World.instance.objects << self
    end

    # @!group 動き

    # (  )歩動かす
    def move(val = 1)
      self.x += @vector[:x] * val
      self.y += @vector[:y] * val
    end

    # (  )歩後ろに動かす
    def move_back(val = 1)
      move(-val)
    end

    # くるっと振り返る
    def turn
      sync_angle(@vector[:x] * -1, @vector[:y] * -1)
    end

    # 横に振り返る
    def turn_x
      sync_angle(@vector[:x] * -1, @vector[:y])
    end

    # 縦に振り返る
    def turn_y
      sync_angle(@vector[:x], @vector[:y] * -1)
    end

    # もし端に着いたら、跳ね返る
    def turn_if_reach_wall
      lr = reach_left_or_right_wall?
      tb = reach_top_or_bottom_wall?
      if lr && tb
        turn
      elsif lr
        turn_x
      elsif tb
        turn_y
      end
    end

    # (  )度回転する
    def rotate(angle)
      self.angle += angle
    end

    def rotation_style=(val)
      @rotation_style = val
      sync_angle(@vector[:x], @vector[:y])
    end

    # 角度
    def angle
      return super if @rotation_style == :free

      x, y = @vector[:x], @vector[:y]
      a = Math.acos(x / Math.sqrt(x**2 + y**2)) * 180 / Math::PI
      a = 360 - a if y < 0
      a
    end

    # (　)度に向ける
    def angle=(val)
      val %= 360
      radian = val * Math::PI / 180
      @vector[:x] = Math.cos(radian)
      @vector[:y] = Math.sin(radian)

      if @rotation_style == :free
        self.scale_x = 1
        super(val)
      elsif @rotation_style == :left_right
        if @vector[:x] >= 0
          self.scale_x = 1
        else
          self.scale_x = -1
        end
        super(0)
      else
        self.scale_x = 1
        super(0)
      end
    end

    # (  )に向ける
    def point_towards(target)
      if target == :mouse
        tx = Input.mouse_pos_x
        ty = Input.mouse_pos_y
      else
        tx = target.x
        ty = target.y
      end
      dx = tx - x
      dy = ty - y
      self.angle = Math.atan2(dy, dx) * 180 / Math::PI
    end

    # @!endgroup

    # @!group 見た目

    def say(options = {})
      defaults = {
        message: '',
        second: 0,
      }
      opts = process_optional_arguments(options, defaults)

      if @balloon
        @balloon.vanish
        @balloon = nil
      end

      message = opts[:message].to_s
      return if message.empty?

      lines = message.to_s.lines.map { |l| l.scan(/.{1,10}/) }.flatten
      font = new_font(16)
      width = lines.map { |l| font.get_width(l) }.max
      height = lines.length * (font.size + 1)
      frame_size = 3
      margin_size = 3
      image = Image.new(width + (frame_size + margin_size) * 2,
                        height + (frame_size + margin_size) * 2)<commit_msg>「(  )に行く」ブロックに対応するCharacter#go_toを追加<commit_after># -*- coding: utf-8 -*-
require 'forwardable'
require 'mutex_m'

module Smalruby
  # キャラクターを表現するクラス
  class Character < Sprite
    extend Forwardable

    cattr_accessor :font_cache
    self.font_cache = {}
    font_cache.extend(Mutex_m)

    cattr_accessor :sound_cache
    self.sound_cache = {}
    sound_cache.extend(Mutex_m)

    cattr_accessor :hardware_cache
    self.hardware_cache = {}
    hardware_cache.extend(Mutex_m)

    attr_accessor :event_handlers
    attr_accessor :threads
    attr_accessor :checking_hit_targets
    attr_accessor :costumes
    attr_accessor :costume_index
    attr_reader :rotation_style

    def initialize(option = {})
      defaults = {
        x: 0,
        y: 0,
        costume: nil,
        angle: 0,
        visible: true,
        rotation_style: :free
      }
      opt = process_optional_arguments(option, defaults)

      @costumes = [opt[:costume]].flatten.compact.map { |costume|
        costume.is_a?(String) ? Image.load(asset_path(costume)) : costume
      }
      @costume_index = 0
      super(opt[:x], opt[:y], @costumes[@costume_index])

      @event_handlers = {}
      @threads = []
      @checking_hit_targets = []
      @angle = 0 unless Util.windows?

      self.scale_x = 1.0
      self.scale_y = 1.0
      @vector = { x: 1, y: 0 }

      [:visible].each do |k|
        if opt.key?(k)
          send("#{k}=", opt[k])
        end
      end

      self.rotation_style = opt[:rotation_style]

      self.angle = opt[:angle] if opt[:angle] != 0

      # HACK: Windows XP SP3の環境でワーカースレッドで音声を読み込めな
      # い不具合が発生した。このためメインスレッドでプリロードしておく。
      %w(do re mi fa so ra si do_2).each do |n|
        new_sound("piano_#{n}.wav")
      end

      World.instance.objects << self
    end

    # @!group 動き

    # (  )歩動かす
    def move(val = 1)
      self.x += @vector[:x] * val
      self.y += @vector[:y] * val
    end

    # (  )歩後ろに動かす
    def move_back(val = 1)
      move(-val)
    end

    # くるっと振り返る
    def turn
      sync_angle(@vector[:x] * -1, @vector[:y] * -1)
    end

    # 横に振り返る
    def turn_x
      sync_angle(@vector[:x] * -1, @vector[:y])
    end

    # 縦に振り返る
    def turn_y
      sync_angle(@vector[:x], @vector[:y] * -1)
    end

    # もし端に着いたら、跳ね返る
    def turn_if_reach_wall
      lr = reach_left_or_right_wall?
      tb = reach_top_or_bottom_wall?
      if lr && tb
        turn
      elsif lr
        turn_x
      elsif tb
        turn_y
      end
    end

    # (  )度回転する
    def rotate(angle)
      self.angle += angle
    end

    def rotation_style=(val)
      @rotation_style = val
      sync_angle(@vector[:x], @vector[:y])
    end

    # 角度
    def angle
      return super if @rotation_style == :free

      x, y = @vector[:x], @vector[:y]
      a = Math.acos(x / Math.sqrt(x**2 + y**2)) * 180 / Math::PI
      a = 360 - a if y < 0
      a
    end

    # (　)度に向ける
    def angle=(val)
      val %= 360
      radian = val * Math::PI / 180
      @vector[:x] = Math.cos(radian)
      @vector[:y] = Math.sin(radian)

      if @rotation_style == :free
        self.scale_x = 1
        super(val)
      elsif @rotation_style == :left_right
        if @vector[:x] >= 0
          self.scale_x = 1
        else
          self.scale_x = -1
        end
        super(0)
      else
        self.scale_x = 1
        super(0)
      end
    end

    # (  )に向ける
    def point_towards(target)
      if target == :mouse
        tx = Input.mouse_pos_x
        ty = Input.mouse_pos_y
      else
        tx = target.x
        ty = target.y
      end
      dx = tx - x
      dy = ty - y
      self.angle = Math.atan2(dy, dx) * 180 / Math::PI
    end

    # (  )に行く
    def go_to(target)
      if target == :mouse
        self.x = Input.mouse_pos_x - (image.width / 2)
        self.y = Input.mouse_pos_y - (image.height / 2)
      else
        self.x = target.x
        self.y = target.y
      end
    end

    # @!endgroup

    # @!group 見た目

    def say(options = {})
      defaults = {
        message: '',
        second: 0,
      }
      opts = process_optional_arguments(options, defaults)

      if @balloon
        @balloon.vanish
        @balloon = nil
      end

      message = opts[:message].to_s
      return if message.empty?

      lines = message.to_s.lines.map { |l| l.scan(/.{1,10}/) }.flatten
      font = new_font(16)
      width = lines.map { |l| font.get_width(l) }.max
      height = lines.length * (font.size + 1)
      frame_size = 3
      margin_size = 3
      image = Image.new(width + (frame_size + margin_size) * 2,
                        height + (frame_size + margin_size) * 2)
{'fraction_non_alphanumeric': 0.12139825776189413, 'fraction_numerical': 0.013178467723922269, 'mean_word_length': 1.847376788553259, 'pattern_counts': {'":': 0, '<': 11, '<?xml version=': 0, '>': 5, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2727910', 'n_tokens_mistral': 3484, 'n_tokens_neox': 3335, 'n_words': 695}